Extending the NDS Schema Object Class and Attribute Definitions
Articles and Tips: article
Software Engineer
Novell Consulting Services
01 Nov 1997
Describes the structure of the NDS schema and the Novell SDK functions for extending the schema and also provides sample code for each. Topics include object classes, attribute syntaxes, and attribute types.
- Introduction
- The NDS Schema Structure
- Object Classes
- Attribute Syntaxes
- Attribute Types
- NDS Schema APIs
Introduction
The Novell Directory Services database supports a base set of object and attribute class definitions. These definitions serve as a template or database schema to define the types of objects and attributes that can be instantiated in the NDS database. The schema outlines the rules that must be adhered to in order for an object to be created. For example, in order to create an object of type "User" in the NDS database, the object name and a value for the Surname must be provided. The Directory Service Agent (Novell Directory Services Server) is responsible for enforcing the schema definitions. The operation to add an NDS "User" object will fail if the rules defined by the schema are not followed explictly.
The schema also defines the relationship of objects to one another within the hierarchy of the directory tree. For example, some objects are defined as "containers" and others are defined as "leaf-nodes." The container objects are allowed to have subordinates while leaf-nodes are an ending point in the tree and cannot. The schema enforces these location-relationships in the tree hierarchy even to the extent of defining which objects can reside under each container.
One of the most powerful capabilities NDS offers is an extensible schema. The Novell SDK provides the functions necessary to define your own object and attribute class definitions in the NDS schema. Network Administrators are being asked to invest more time and resources to maintain and configure an ever-increasing number of network resources. As the number of network resources grow exponentially, so does the need for a directory service that can serve as a single repository where network administrators can globally manage them. This section will describe the structure of the NDS schema and the Novell SDK functions for extending the schema and provide sample code for each.
The NDS Schema Structure
The NDS Schema is composed of three major components:
Object Classes
Attribute Syntaxes
Attribute Types
Let's take a closer look at each of these.
Object Classes
Object Classes define the type of objects that can exist in the NDS database. The Object Class definition is composed of four types of information:
Structure Rules: Named By and Containment
Super Classes
Mandatory Attributes
Optional Attributes
Structure Rules
The Structure Rules define the structure relationships of objects in the tree and how the object is named. Containment defines where within the tree an object can reside. Objects can be defined as containers which can contain other containers and leaf-node objects. An example of a container is Organization (O) which can contain other containers called Orgainizational Units (OU). Containers are analogous to directories or folders in the file system with the extra stipulation that only objects that have the proper containment defined can reside beneath them.
This would be anologous to a directory in the file system that would only allow files with the BMP extension to reside within it. NDS imposes these restrictions through the containment defined for each object class. The "Named By" attribute defines how the object is named within the directory. Every component that makes up the hierarchy of the NDS tree is named by a naming component.
Let's go back to the file system analogy. The highest level in the file system is called the "Root." Extending from the Root are directories which can contain other directories or files. We always call "file containers" directories and the "leaf-nodes" files. There is no need to give a file directory a naming attribute because they all do the same thing. They hold files with no file-system imposed restriction on the types of files they can contain.
What if we could restrict a directory to only contain a certain type of file such as graphics files or text files? Then it would make sense to give a naming attribute to the directory so we know what type of files it can contain. We often identify a file type by its extension (largely unaccurate) due to a lack of a naming attribute for a file.
NDS does have a need to identify all containers and leaf-objects with a naming attribute to further discriminate what type of objects they are. A directory service is of little value if all objects appear the same with no way to differenciate form and function between objects.
An understanding of the meaning of naming attributes greatly clarifies what an object is and where it fits in the organization of the tree. For example, CN=BOB.OU=ENGINEERING.OU=PROVO.O=NOVELL.C=US. This string, while at first look appears cryptic, actually reveals a lot about the object it references due to its naming attributes. Let's expand it out. An object of Common Name "Bob" works in an Organizational Unit named "Engineering" in Provo in an Organization named Novell which resides in the Country of the US.
Super Classes
Super Classes define the structure relationship of object class definitions within the NDS schema itself, not the directory tree. The NDS schema is structured in hierarchical fashion with all object class definitions derived from the abstract class Top.
As demonstrated in Figure 1, Top encompasses a base set of attributes (demonstrated as circles in the figure) that all object class definitions in the schema will have because all object class definitions must be derived from Top. A second class definition Device, derived from Top, has inherited the Top's attribute class definitions and defines some attributes of its own. Finally, the base class Computer has inherited all of the attribute class definitions in its object class lineage and adds some attributes of its own.
Figure 1: NDS Schema Class Hierarchy.
The Computer class is considered an "Effective" class which means that it can be instantiated in the NDS tree. Other object classes such as Top and Device are abstract classes that cannot be instanciated. Classes that cannot be used to create an object in the tree are called "Non-Effective" classes. Non-Effective classes are used to provide a base set of attributes for a new class.
This greatly simplifies extending the base schema (schema that NDS ships with) quickly and effectively. If you are designing a Service class the Non-Effective class Server probably already encompasses most of the attributes relevant for your service. This Server class can then serve as your base for deriving your own custom server class.
Mandatory Attributes
The Mandatory Attributes of the object class dictate that values for these attributes must be supplied upon instantiation of the object. Failure to provide values for these attributes upon object creation using NWDSAddObject() will cause the Add Entry operation to fail. The object class definition should only define mandatory attributes in cases where they are essencial for the definition to be useful.
Optional Attributes
The Optional Attributes of the object class need not be supplied on instantiation of the object but are useful for defining possible characteristics of the class definition. Optional attribute values can be provided at object creation using NWDSAddObject() or after the object has been created by using the function NWDSModifyObject().
Attribute Syntaxes
The Attribute Syntaxes define the values an attribute can have. The syntax is a datatype that describes the nature of the data. The syntax definitions are the only component of the NDS schema that are not extensible. If none of the defined data types appear suitable for a custom attribute type definition the octet string or stream syntaxes can be used to store data in a format defined by the application developer. Following is a list of the syntaxes supported by Novell Directory Services.
SYN_UNKNOWN
SYN_DIST_NAME
SYN_CE_STRING
SYN_CI_STRING
SYN_PR_STRING
SYN_NU_STRING
SYN_CI_LIST
SYN_BOOLEAN
SYN_INTEGER
SYN_OCTET_STRING
SYN_TEL_NUMBER
SYN_FAX_NUMBER
SYN_NET_ADDRESS
SYN_OCTET_LIST
SYN_EMAIL_ADDRESS
SYN_PATH
SYN_REPLICA_POINTER
SYN_OBJECT_ACL
SYN_PO_ADDRESS
SYN_TIMESTAMP
SYN_CLASS_NAME
SYN_STREAM
SYN_COUNTER
SYN_BACK_LINK
SYN_TIME
SYN_TYPED_NAME
SYN_HOLD
SYN_INTERVAL
(The syntaxes listed above-as well as the example code lisitngs referenced later in this article-are all linked to the web copy of this article located at http://developer.novell.com/nds/schema.htm.)
Along with the data type description of each syntax is a set of matching rules. The matching rules allow the directory service to determine if the data presented by a user via the application software correctly matches the corresponding data value in the directory. This matching is required when resolving a name and for returning accurate results from a search or compare operation.
These operations must use some criteria to determine a match with the search filter criteria and the data in the directory. The matching rules define this criteria. Each of the matching rules needs to specify the following:
The Attribute Syntax that the matching rule applies to.
How the comparison is to be performed and the conditions under which a match is found to be true.
There are five built-in matching rules supported by the Novell Directory Service:
present: This matching rule returns TRUE if there is an attribute present in the entry which matches the value supplied by the application (only accessible using NWDSSearch() with the FTOK_PRESENT token).
equality: This matching rule matches a application-supplied attribute value with a stored attribute value to verify that they are the same. Each attribute syntax must have its own equality matching rule defined. The additional information provided for each syntax link above describes the equality match for each attribute syntax.
substrings: This matching rule verifies that the application-supplied string is a substring of a larger stored attribute string value. Substring matching rules only apply to string attributes
ordering: This matching rule specifies the ordering of all possible values of the syntax. The application can then determine if a stored attribute value is greater than or equal to or less than or equal to a presented value.
approximate: This matching rule determines whether a presented value is approximately the same as a NDS stored value. The meaning of approximate must be determined by individual implementations of each syntax (only accessible using NWDSSearch()with the FTOK_APPROXIMATEtoken).
Attribute Types
The Attribute Types are constructed from one of the syntax types to define the categories of information stored in an NDS object. For example, Surname, Telephone Number and Title are examples of some attribute types. All of these attribute types are built from one of the 28 pre-defined syntaxes and one or more attribute constraints. Following is a list of the constraints that can be included as part of the attribute definition:
Constraint
|
Description
|
DS_HIDDEN_ATTR |
This constraint flags the attribute for NDS server use only. The client cannot set ormodify this constraint flag and thus cannot see or modify the attribute. |
DS_NONREMOVABLE_ATTR |
This flag prevents the attribute from being removed from an object class definition. The client cannot set or modify this constraint flag and thus cannot modify theattribute. All base schema attribute type definitions have the non-removable flag set (On). |
DS_READ_ONLY_ATTR |
The user cannot modify the attribute because a name server has created the attribute and maintains it. |
DS_PUBLIC_READ |
This constraint indicates that anyone can read the attribute without read privileges being assigned. You cannot use inheritance masks to prevent an object from reading attributes with this constraint. |
DS_SERVER_READ |
With this constraint set (On) for an attribute, server class objects can read the attribute even though the privilege to read has not been inherited or explicitlygranted. You cannot use inheritance masks to restrict servers from reading attributes with this constraint. The client cannot set or modify this constraint flag and thus cannot modify the attribute. |
DS_PER_REPLICA |
If this constraint is set (On) the information in the attribute is not synchronized on other replicas. The client cannot set or modify this constraint flag and thus cannot modify the attribute. |
DS_SYNC_IMMEDIATE |
When modifications are made to an attribute with this constraint flag set (On), other replicas containing the object are synchronized immediately rather than at the next synchronization interval. |
DS_STRING_ATTR |
These attributes are of string type. You can use attributes of this type as naming attributes. |
DS_SIZED_ATTR |
The attribute has an upper and lower bound. This can be the length for strings or the value for integers. |
DS_SINGLE_VALUED_ATTR |
The attribute has a single value only with no order implied. |
DS_WRITE_MANAGED |
Users must have "managed rights" on the object that contains this attribute before they can change the attribute's value. |
NDS Schema APIs
Now that you've learned the details about the structure and layout of the NDS schema, you are prepared to begin adding extensions using the NDS functions provided in the Novell SDK. Novell strongly recommends that you register your schema extensions before you release a product that extends the NDS schema. All schema object and attribute class definitions must be unique to the NDS tree.
If you do not register your schema extension and another product installation has already defined an attribute or class definition of the same name as those defined in your product, the attempt to add your schema extensions will fail. For this reason Novell maintains an NDS schema registry to avoid these kinds of collisions.
Design Considerations
Should you extend the base NDS schema to accommodate your own custom class definitions? Following are some guidelines to help you determine the best course of action.
The schema that is included with Novell Directory Services has a large selection of predefined object and attribute class definitions. Review these existing "base" class definitions to determine if one of them is well suited for the resource you wish it to represent in NDS. If an existing base class definition exists this can simplify your product installation and you can avoid supplying your own administration utility or NWAdmin Snapin to configure an object of this class in NDS.
If you determine that no base schema definitions exist that will suit your needs exactly, determine whether one of the base class definitions encompasses most of the characteristics (attribute class definitions) to accurately represent your resource. You can reference the NDS Schema Reference included in the online documention of the Novell SDK or use a utility to dump the current contents of the schema to review the base schema definitions.
If you find an object class definition that encompasses most of the characteristics to accurately represent your resource, determine whether this object class is an effective or non-effective class. If the object class is effective you have two options:
You can add your own custom attribute definitions to this effective class.
Derive your own object class from this base class and then add your custom attribute definitions to the derived class.
For example, suppose you wish to represent a Fax Service as an object in the NDS tree. The base schema does not include a Fax Service object; however, there is a non-effective class Server that contains within its definition many of the attributes relevent to a server class of any kind. The Server class is therefore well suited to serve as the base for deriving a new custom schema extension Fax Server.
If the object class if non-effective, you must derive your object class from this base schema class and define the derived class as effective. If none of the base schema object class definitions are well suited to represent your resource, you can derive your object class from Top, the class from which all class definitions are derived.
The next step is to determine whether the base schema attribute type definitions can be used as part of your new object class definition. Remember that each Attribute Type is constructed from one of twenty-eight syntaxes or data types and one or more attribute constraints. If the combination of the attribute name, syntaxes and constraints do not accurately represent the function of the attribute types required for your new object class you will need to define your own attribute type (class definition).
First, determine what the data type should be by reviewing the available syntax definitions. Remember that the syntax definitions are not extensible. Once a suitable syntax is found review the available constraints to determine which attribute constraints make sense. Choose a name that accurately describes the function of the new attribute type definition.
Recall that the object class definition is composed of five pieces of information. In order to create an effective object class it must define a Super Class, Containment and Naming Attribute. At a minimum there will be two mandatory attributes (Naming Attribute and Containment). No Optional Attributes or additional Mandatory Attributes need to be defined; however, your object class will have little usefulness without them.
If your new object class is derived from an object class that already has the Naming Attribute and Containment defined, there is no need to redefine these components unless it makes sense for the derived class definition. Let's take a closer look at each and the considerations that should be made at design time with regards to each:
Super Class-The super class designates the base class from which the new class will be derived. This should be a class that already encompasses most or all of the attribute definitions that accurately represent your resource. If the new object class is derived from Top, you must also define the Naming Attribute and Containment. These are not defined by Top.
Containment-This defines the NDS containers under which objects of the new class can reside. In most cases it makes sense to define containment to the standard containers (O, OU, etc), however, there are cases where addition restrictions may apply. For example, you may wish to define a new containment object class called DB under which only object classes of type DB containment can reside. Objects defined with DB containment would most likely be database objects.
Naming Attribute-This defines the naming component for instances of this class. Most leaf-node classes use CN (common name) for the naming attribute. Be aware that the "Default Typing Rule" does not apply to custom Naming Attributes. This forces any users that wish to access instances of this object class to always specify the Naming Attribute as part of the name.
Mandatory Attributes-This defines those attributes for which values MUST be supplied before and instance of this class can be created. Use sparingly. Remember that the network administrator must supply information for these values before the object can be created.
Optional Attributes-This defines those attributes that are necessary to add usefulness to the object class definition but are not required upon creation of the object.
Following is a list of possible pros and cons to consider when incorporating NDS schema extensions in to your design:
Pros:
The network resource can be represented accurately in the NDS tree.
Database search queries can be built to easily locate objects of a custom class.
Standard NDS access control mechanisms can be used to govern access to custom objects.
The categories of information stored in a object can be suited to match the needs of a custom resource.
Cons:
Only an Admin-equivalent object can extend the schema. Since schema definitions are global to the entire tree this requires that all organizations in the tree rely upon someone with Admin-equivalent (all rights to the [Root] of the tree) to run the installation utility that extends the schema. For this reason, Novell recommends that the portion of the install utility that extends the schema be separate from the main install. This allows the Admin-equivalent user to run a very simple install to extend the schema and a "Container Admin" to run the main install. A future release of NDS will provide a Non-global schema which will allow an object with rights granted at a specific container to extend the schema. The Non-global schema will also remove the need to sychronize all schema extensions to all servers in the tree. Only servers with like partitions will maintain the schema extentions relevant for that part of the tree.
Extensions made to the base (shipping) schema cannot be removed. This limitation will be lifted in a future release of NDS. For example, if a new attribute class were added to the "User" object class definition, this attribute definition could not be removed without re-installing NDS. If a new attribute class is added to an extended object class, "DBUser" derived from "User," this new object class could be removed from "DBUser" and the derived "DBUser" object class could also be removed.
Operation
|
NDS Function
|
Add Object Class Definition |
NWDSDefineClass() |
Modify Object Class Definition |
NWDSModifyClassDef() |
Read Object Class Definition |
NWDSReadClassDef() |
Remove Class Definition |
NWDSRemoveClassDef() |
Add Attribute Class Definition |
NWDSDefineAttr() |
Read Attribute Class Definition |
NWDSReadAttrDef() |
Remove Attribute Class Definition |
NWDSRemoveAttrDef() |
Read Syntax ID |
NWDSGetSyntaxID() |
Read Syntax Definitions |
NWDSReadSyntaxes() |
NWDSDefineClass()
The function NWDSDefineClass()is used to add a new object class definition to the NDS schema.
NWDSDefineClass() Function Prototype
NWDSCCODE N_API NWDSDefineClass( NWDSContextHandle context, pnstr8 className, pClass_Info_T classInfo, pBuf_T classItems)
context : (IN) Specifies the Directory context for the request. Currently, the context handle is not used, however, a valid context handle must be supplied.
className : (IN) Points to the name of the new object class.
classInfo : (IN) Points to the class flags and ASN.1 ID for the new class. The primary use for this structure is to identify the new class as an EFFECTIVE or NON-EFFECTIVE class.
classItems : (IN) This input buffer contains the five required components for an object class. You must specify a Super Class. The other four components can be left the same as the Super Class or modified to suit the new class definition. If you want the other four components to be the same as its superclass, use NWDSBeginClassItem() to mark the components' existence in the buffer and do not specify a new value with NWDSPutClassItem(). All mandatory attribute definitions MUST be specified when defining the object class and cannot be added later with NWDSModifyClassDef(). This information MUST be specified in the following order:
Super Class
Containment
Naming Attribute
Mandatory Attributes
Optional Attributes
See: Function Flow Chart Diagram for NWDSDefineClass() Example Code For NWDSDefineClass()
NWDSModifyClassDef()
NWDSModifyClassDef() is used to add optional attribute definitions to an existing object class.
NWDSModifyClassDef() Function Prototype
NWDSCCODE N_API NWDSModifyClassDef( NWDSContextHandle context, pnstr8 className, pBuf_T optionalAttrs);
context : (IN) Specifies the Directory context for the request.
className : (IN) Points to the object class name whose definition is to be modified.
optionalAttrs : (IN) Points to a request buffer containing the names of attributes to be added to the object-class definition's Optional Attribute Names list.
See: Example Code For NWDSModifyClassDef()
NWDSReadClassDef()
NWDSReadClassDef() is used to retrieve all of the object class definition information for all classes or for specific specified classes. This information includes the five object class components, Super Class, Containment, Naming Attribute, Mandatory Attributes and Optional Attributes along with the objectInfo structure which reveals whether the class is a container, effective or non-effective class.
NWDSReadClassDef() Function Prototype
NWDSCCODE N_API NWDSReadClassDef( NWDSContextHandle context, nuint32 infoType, nbool8 allClasses, pBuf_T classNames, pnint32 iterationHandle, pBuf_T classDefs);
context : (IN) Specifies the Directory context for the request.
infoType : (IN) Specifies the information type desired (names only, or names and definitions).
allClasses : (IN) Specifies whether information for every object class should be returned. This parameter can be set to TRUE or FALSE. If set to TRUE, all class definitions will be returned. If set to FALSE, the input buffer classNames must contain the names for the specific class definitions to be returned.
classNames : (IN) Points to a request buffer containing the names of the object classes whose information is to be returned. This should be set to NULL if allClasses is set to TRUE.
iterationHandle : (IN/OUT) Points to information needed to resume subsequent iterations of NWDSReadClassDef.
classDefs : (OUT) Points to a result buffer containing the requested object-class names and/or definitions.
See: Example Code For NWDSReadClassDef()
NWDSRemoveClassDef()
NWDSRemoveClassDef() is used to remove an object class definition. NDS base class schema definitions (shipping NDS schema) cannot be removed. Only extensions to the base schema can be removed. If an object of the class you are attempting to remove exists in the NDS tree, the class definition cannot be removed. All instances of the class must be removed before the class definition can be removed.
NWDSRemoveClassDef() Function Prototype
NWDSCCODE N_API NWDSRemoveClassDef( NWDSContextHandle context, pnstr8 className);
context : (IN) Specifies the Directory context for the request.
className : (IN) Points to the class name to be removed.
See: Example Code For NWDSRemoveClassDef()
NWDSReadAttrDef()
NWDSReadAttrDef() is used to return the syntax (data type) upon which the attribute type is constructed, the attribute flags (constraints), and the upper and lower bounds constraints for a specific attribute type.
NWDSReadAttrDef() Function Prototype
NWDSCCODE N_API NWDSReadAttrDef( NWDSContextHandle context, nuint32 infoType, nbool8 allAttrs, pBuf_T attrNames, pnint32 iterationHandle, pBuf_T attrDefs);
context : (IN) Specifies the Directory context for the request.
infoType : (IN) Specifies the information type desired (names only, or names and definitions).
allAttrs : (IN) Specifies whether information for all attributes or for selected attributes should be returned. This parameter can be TRUE or FALSE. If set to TRUE, all attribute definitions will be returned and the value of attrNames is ignored. If set to FALSE, the input buffer attrNames must contain the specific attribute definitions to be returned.
attrNames : (IN) Points to a request buffer containing the attribute names whose definitions are to be returned.
iterationHandle : (IN/OUT) Points to information needed to resume subsequent iterations of NWDSReadAttrDef.
attrDefs : (OUT) Points to a result buffer that receives the requested attribute names and/or definitions.
See: Example Code For NWDSReadAttrDef()
NWDSDefineAttr()
NWDSDefineAttr() is used to add a new attribute type definition to NDS. An Attribute Type definition is composed of a syntax and attribute contraints. This information is set in the Attr_Info_T structure.
NWDSDefineAttr() Function Prototype
NWDSCCODE N_API NWDSDefineAttr( NWDSContextHandle context, pnstr8 attrName, pAttr_Info_T attrDef);
context : (IN) Specifies the Directory context for the request.
attrName : (IN) Points to the name for the new attribute.
attrDef : (IN) Points to the remaining information for the new attribute definition.
See: Example Code For NWDSDefineAttr()
NWDSRemoveAttrDef()
NWDSRemoveAttrDef() is used to remove an attribute class definition. BE AWARE that any attribute class definitions that are added to a base schema object class definition cannot be removed. If the attribute class definition is added to an object class that is derived from a base schema definition it can be removed.
NWDSRemoveAttrDef() Function Prototype
NWDSCCODE N_API NWDSRemoveAttrDef( NWDSContextHandle context, pnstr8 attrName);
context : (IN) Specifies the Directory context for the request.
attrName : (IN) Points to the name of the attribute definition to be removed.
See: Example Code For NWDSRemoveAttrDef()
NWDSGetSyntaxID()
NWDSGetSyntaxID()returns the syntax identifier of a given attribute type.
NWDSGetSyntaxID() Function Prototype
NWDSCCODE N_API NWDSGetSyntaxID( NWDSContextHandle context, pnstr8 attrName, pnuint32 syntaxID);
context : (IN) Specifies the Directory context for the request.
attrName : (IN) Points to the attribute name whose syntax ID you want to determine.
syntaxID : (OUT) Points to the syntax ID of the attribute.
NWDSReadSyntaxes()
NWDSReadSyntaxes() Function Prototype
NWDSCCODE N_API NWDSReadSyntaxes( NWDSContextHandle context, nuint32 infoType, nbool8 allSyntaxes, pBuf_T syntaxNames, pnint32 iterationHandle, pBuf_T syntaxDefs);
context : (IN) Specifies the Directory context for the request.
infoType : (IN) Specifies the type of information desired (names only, or names and syntax definitions).
allSyntaxes : (IN) Specifies the scope of the request (all or selected syntaxes).
syntaxNames : (IN) Points to a request buffer containing the names of the syntaxes for which information is to be returned.
iterationHandle : (IN/OUT) Points to information needed to resume subsequent iterations of NWDSReadSyntaxes.
syntaxDefs : (OUT) Points to a result buffer containing the requested syntax names and/or definitions.
* Originally published in Novell AppNotes
Disclaimer
The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.