If your schema needs are not adequately met by existing, predefined schemas, you will need to define your own object classes and attributes. It is fairly easy to define your own schemas, but as with all design tasks , there are pitfalls and trade-offs to be considered . In this section we describe an approach that should produce good results. We do not discuss defining new attribute syntaxes because in most implementations adding new syntaxes requires code to be written to support it, usually by the directory software vendor. Choosing Names for New Attribute Types and Object ClassesYou should choose a naming scheme for the new object classes and attributes you define. All names should be made as meaningful as possible but not too long or cumbersome. Attribute names and object classes are generally hidden from end users, but directory service administrators and applications developers will work with them extensively. It is also important to make some effort to avoid collisions with the names chosen by other parties (standards committees , directory vendors, other software vendors , and so on). Remember, the entire attribute namespace is flat. The same is true for the object class namespace. A good strategy is to prefix the names of all the schema elements you define with something that resembles your organization's name . If you do that, collisions with other definitions are unlikely . For example, the ACME Corporation might use the prefix "acme" and create attributes and object classes such as these: acmePerson (object class) acmePrinter (object class) acmeID (object class) acmeHoursAllowedAccess (attribute type) Tip If you define schema elements that you intend to publish widely and submit to a standards body such as the IETF, there is no need to prefix the names of the attributes and object classes with a string that identifies your organization. In fact, acceptance of your schema by others will likely be hindered if the names include something specific to your organization! Obtaining and Assigning Object IdentifiersRecall that each LDAP object class or attribute type must be assigned a unique name and OID. One of the biggest stumbling blocks faced by people new to LDAP directories is how to obtain OIDs for the new attribute types and object classes they want to define. Simply put, OIDs can be obtained from anyone who has one. In fact, one OID is sufficient to meet all your schema needs; you can simply add another level of hierarchy to create new branches, or OID arcs , for your attributes and object classes. An OID arc is an OID that has been reserved for use as a container for defining additional OIDs. The process of assigning an arc of the OID space to another party is called delegation . As already mentioned, an OID can be obtained from anyone who has one; that person just needs to delegate it to you as an arc and record this fact so that he does not use the OID for any other purpose. The Internet Assigned Numbers Authority (IANA) gives out OIDs to any organization that asks. IANA calls the OIDs enterprise numbers because it gives them out primarily for use with Simple Network Management Protocol (SNMP). The IANA OIDs work fine for LDAP as well because any one OID is as good as another. A form for obtaining an OID from IANA can be accessed on the World Wide Web at http://www.isi.edu/cgi-bin/iana/enterprise.pl. Other organizations known to give out OIDs are the American National Standards Institute (ANSI) for U.S. organizations and the British Standards Institution (BSI) for U.K. organizations. General information on OIDs maintained by a gentleman named Harald Alvestrand can be accessed at http://www.alvestrand.no/objectid. As an example of obtaining and assigning OIDs, consider the University of Michigan (U-M) directory services team, which contacted IANA to obtain an OID arc (1.3.6.1.4.1.250) for its own use. The team then created the OID arcs shown in Table 8.6 for use in defining its own directory schema. The team assigned the OID 1.3.6.1.4.1.250.1.1 to the first attribute type it defined, 1.3.6.1.4.1.250.1.2 to the second, and so on. Tip After you obtain an OID, you should maintain a registry similar to Table 8.6 to ensure that no OID is ever used for more than one purpose (the registry can just be a text file, perhaps maintained by a revision control system). You should then publish the list of OIDs with your schemas (more on this topic later). Although it may seem unimportant to establish an OID registry when you're just getting started, you will want to have one because the number of attributes and object classes that you or others in your organization create may be quite large in the end. Table 8.6. University of Michigan OID Schema Arcs
Modifying Existing Schema ElementsIt may be tempting just to alter some predefined schema elements to meet your needs, perhaps by adding a new attribute to a predefined object class. At first glance, this seems like a reasonable thing to do, but it isn't. Do not modify existing schema elements! Changing existing schemas will break some directory servers and clients , and it will probably lead to a lot of confusion. If the person object class that everyone knows about is different within each directory service deployment, chaos will rule. Also be careful when completely deleting object classes or attributes from any of your directory server software's preinstalled schemas. It's OK to do this as long as you're sure that the schema is not used internally by any of the directory service software. However, you may run into trouble when you upgrade your software because the upgrade process may require that all the vendor's schemas be present. In general, there is no reason to remove schema elements even if you do not plan to use them; there is little or no penalty associated with leaving the schema elements installed. Subclassing an Existing Object ClassIt is fairly common to extend, or subclass , an existing, predefined object class to add new attribute types to it. To do this, define a new object class that is a subclass of the existing one by indicating in the definition of the new class that the existing class is its superior . You also need to define the new attribute types and include them in the new object class as required or as optional attributes. When you subclass an existing object class in this way, the new class should generally be used to represent the same type of object as the class from which it was derived. For example, you might create a new subclass of a printer object class called hpPrinter that allows additional attributes specific to Hewlett-Packard printers. The hpPrinter class is still used to represent printers; it just holds more information. Also the new object class should be the same kind as the class from which it is derived. For example, if you are subclassing a structural object class, the new class should also be a structural class. Note that in some directory service implementations, such as Netscape's, little or no distinction is made between different kinds of object classes during schema checking, so this may not be something you have to worry too much about initially. As another example of subclassing an existing object class, suppose that the Example Corporation is developing a directory-enabled application called the Birthday Notification Service (to which managers will subscribe so that they remember to take each employee out to lunch on his or her birthday). The designers of the Example directory might define a new attribute to hold the day and month a person was born. The attribute type definition might look like this: ( bday-OID NAME 'exampleBirthday' DESC 'birthday day-month' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) Note that bday-OID would need to be replaced by a real OID assigned by the Example directory administrators. An extension of the inetOrgPerson class that allows the new exampleBirthday attribute to be included in entries could be defined as shown in Listing 8.17. Listing 8.17 The examplePerson Object Class ( examplePersonOID NAME 'examplePerson' DESC 'Example Corporation extended person' SUP inetOrgPerson STRUCTURAL MAY exampleBirthday ) Note that examplePersonOID should be replaced by a real OID assigned by the directory administrators. The examplePerson class would be used whenever a user entry was created inside Example Corporation. The following is a sample entry: dn: uid=jcarter,ou=People,dc=example,dc=com objectclass: top objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson objectclass: examplePerson cn: John Carter sn: Carter uid: jcarter userPassword: secret exampleBirthday: 29-February Now we all know why John claims to be so much younger than he looks; he must be counting birthdays instead of elapsed time! Note In the LDIF just shown, values for all the superior object classes ( top , person , organizationalPerson , and inetOrgPerson ) are explicitly shown in the entry's objectClass attribute. This is how an entry will look when you retrieve it from an LDAP server. When you add or modify an entry, however, you should need to provide only the most specific superior object class value (for example, inetOrgPerson ). On the other hand, some LDAP server implementations do require all of the superior object class values to be listed, so it is safest always to do so. Adding Auxiliary Information to a Directory ObjectSometimes it is preferable to create an auxiliary object class that allows attributes to be added to any type of LDAP entry, regardless of the kind of real-world object it represents. A class like this is sometimes called a mix-in class because it allows additional attributes to be "mixed into" an existing class. A mix-in class may be added to a wide range of entry types ”a much simpler approach than creating a subclass for each object class in which you want to allow the new attributes to appear. To create an auxiliary object class, simply define a new class that is not subclassed from any existing object class (it should have the special class top as its superior). Typically, all the attributes in the auxiliary class should be optional rather than mandatory. That way, the auxiliary object class itself can be associated with an entry regardless of whether any values for its attributes are present. As a result, the burden on directory clients is reduced because they do not have to worry about removing the object class value itself when the auxiliary attributes are removed. Listing 8.18 shows an example of a useful auxiliary object class. This class can be added to any LDAP entry to allow a dc (domain component) attribute value to be included in the entry. Listing 8.18 The dcObject Auxiliary Object Class( 1.3.6.1.4.1.1466.344 NAME 'dcObject' SUP top AUXILIARY MUST dc ) The following is an example of an organization entry that also contains domain components : dn: o=Example Inc.,c=US objectclass: top objectclass: organization objectclass: dcObject o: Example Inc. dc: example As another example of an auxiliary object class, suppose that you develop a directory-enabled application that keeps track of your organization's network- related inventory by storing information in the following custom attribute types: ( OID NAME 'inventoryID' DESC 'numeric inventory ID' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1. 1466.115.121.1.27 SINGLE-VALUE ) ( OID NAME 'inventoryDatePlacedInService' DESC 'date item was placed into service' EQUALITY generalizedTimeMatch ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115. 121.1.24 SINGLE-VALUE ) ( OID NAME 'inventoryContactPerson' DESC 'pointer to entry of the person responsible for item' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) ( OID NAME 'inventoryComments' DESC 'comments related to inventory status' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) All the instances of OID would, of course, need to be replaced with real object identifiers you assign. Because you want to mix these attributes into several different types of entries (printers, hosts , and so on), you could handle your schema needs by defining an auxiliary object class such as the inventoryItem class shown in Listing 8.19. Listing 8.19 The inventoryItem Auxiliary Object Class( OID NAME 'inventoryItem' DESC 'auxiliary object class to hold inventory information' SUP top AUXILIARY MUST inventoryID MAY ( inventoryDatePlacedInService $ inventoryContactPerson $ inventoryComments ) ) Here is a sample printer entry that has inventory information attached to it: dn: cn=2nd floor HP LaserJet 8150dn,ou=printers,dc=example,dc=com objectclass: top objectclass: printer objectclass: inventoryItem cn: 2nd floor HP LaserJet 8150dn pagesPerMinute: 32 inventoryID: 129055581 inventoryDatePlacedInService: 20011201000000Z inventoryContactPerson: uid=bjensen,ou=people,dc=example,dc=com inventoryComments: on loan to the art department Accommodating New Types of ObjectsIf you cannot find a predefined object class that is similar to the type of object you need to represent in your directory service, simply define a new structural class to hold whatever attributes are appropriate. This task is similar to creating a new auxiliary class, except that the structural class can stand on its own as the primary object class for an entry. For example, if our friends at the Example Corporation plan to use their directory to track company-owned and -operated telephone sets, they might reuse some predefined attributes but create a new object class such as the one shown in Listing 8.20. Listing 8.20 The exampleTelephone Object Class ( OID NAME 'exampleTelephone' DESC 'Example Corporation telephone' SUP top STRUCTURAL MAY ( cn $ telephoneNumber $ owner $ L ) ) Here is a sample entry: dn: cn=Mark Smith's phone,ou=phones,dc=example,dc=com objectclass: top objectclass: exampleTelephone cn: Mark Smith's phone telephoneNumber: 3477 owner: uid=bkady,ou=people,dc=example,dc=com L: security office Note that when you create an object class for an entirely new kind of object, you will need to put some thought into which attribute will most likely be used to form the RDNs of entries that belong to the class. For most object classes, the cn (common name) attribute is a good, generic choice. In the exampleTelephone object class we just looked at, the telephone number itself might actually be a better choice for naming phone entries simply because it is more likely to be unique. The sample entry we just used might instead be defined like this: dn: telephoneNumber=3477,ou=phones,dc=example,dc=com objectclass: top objectclass: exampleTelephone cn: Mark Smith's phone telephoneNumber: 3477 owner: uid=bkady,ou=people,dc=example,dc=com L: security office The only difference is in the RDN (the first part of the DN), which uses the telephoneNumber attribute instead of cn to name the entry. See Chapter 9, Namespace Design, for more information on entry naming. Tips for Defining New SchemasDefining a good schema is as much art as science, and the more you do it, the easier the process becomes. The following are some tips that will help you produce better results:
|