Like class objects in the schema, attributes are defined using objects. As mentioned earlier in this chapter, an attribute object is the object in the Schema container that defines a particular attribute. An attribute object is created from the attributeSchema class. Each attributeSchema object in the Schema container represents a single attribute definition.
The attributeSchema object defines the name, object identifier, and data type for an attribute. Like classSchema objects, an attributeSchema object has the naming attributes cn (Common-Name) and lDAPDisplayName. When referencing an attribute programmatically, always use lDAPDisplayName.
In addition to specifying the syntax to use for an attribute, the attributeSchema object defines whether the attribute accepts multiple values and the range of each value, including a minimum and maximum. Table 9-9 lists the attributes of the attributeSchema class.
attributeSchema Attribute | Mandatory or Optional | Syntax | Description |
---|---|---|---|
attributeID | Mandatory | OID | The OID for this attribute. This value Identifier must be unique among all the attributes defined in the schema. |
attributeSecurityGUID | Optional | OctetString | A GUID stored as an octet string. This is an optional GUID that identifies the attribute as a member of an attribute grouping (also called a property set). You can use this GUID in access control entries to control access to all attributes in the property set; that is, to all attributes that have the specified GUID set in their attributeSecurityGUID property. |
attributeSyntax | Mandatory | OID | The OID of the syntax for this attribute. The combination of the attributeSyntax and oMSyntax properties determines the type of data stored by instances of the attribute (the syntax). |
classDisplayName | Optional | Directory- String Multivalued | Not used. It appears that this attribute is erroneously included in the attributeSchema class. This attribute is intended for the displaySpecifier class. |
cn | Mandatory | Directory- String | The name of the attribute, in LDAP form (mixed-case, first letter lowercase, no dashes). |
extendedChars- Allowed | Optional | Boolean | Not used. Exists for backward compatibility with Microsoft Exchange Server. If set, this attribute indicates that extended characters are allowed in attributes with a syntax of String (Teletex). |
isDefunct | Optional | Boolean | Set to True to disable an attribute. Prevents new instances of an attribute from being created. |
isEphemeral | Optional | Boolean | True if this object cannot be replicated. |
isMemberOfPartial- AttributeSet | Optional | Boolean | True if the attribute is replicated to global catalog servers. |
isSingleValued | Mandatory | Boolean | True if the attribute accepts only one value; False if the attribute is multivalued. |
lDAPDisplayName | Mandatory | Directory- String | Name used by LDAP clients, including ADSI, to refer to this attribute. |
linkID | Optional | Integer | A number that indicates that the attribute is part of a linked pair. An even number indicates a forward link; an odd number indicates a backward link. |
mAPIID | Optional | Integer | Used by Messaging API (MAPI) clients to identify this attribute. |
oMObjectClass | Optional | OctetString | When oMSyntax is 127, the correct OM class must be set here. |
oMSyntax | Mandatory | Integer | The XDS/XOM syntax for this attribute. |
rangeLower | Optional | Integer | Specifies the lowest value for numeric attribute types or the smallest size for string attribute types. |
rangeUpper | Optional | Integer | Specifies the highest value for numeric attribute types or the largest size for string attribute types. |
schemaFlagsEx | Optional | Integer | Internal use only. |
schemaIDGUID | Mandatory | OctetString | GUID for this attribute. |
searchFlags | Optional | Enumeration | Used to control indexing and other behavior. See the next section, "Types of Attributes," for more information. |
systemOnly | Optional | Boolean | If True, this object cannot be modified. |
Table 9-9 Attributes of the attributeSchema class.
Active Directory defines several types of attributes. Attributes that are to be included in the global catalog have the isMemberOfPartialAttributeSet attribute set to True. The searchFlags attribute uses the least significant bit to indicate whether the attribute should be indexed, which, as I've mentioned elsewhere, helps the server search for information more quickly.
Another attribute, systemFlags, defines additional characteristics for the attribute. These are defined with values from the ADS_SYSTEMFLAG_ENUM enumeration, shown here:
typedef enum {
ADS_SYSTEMFLAG_DISALLOW_DELETE = 0x80000000,
ADS_SYSTEMFLAG_CONFIG_ALLOW_RENAME = 0x40000000,
ADS_SYSTEMFLAG_CONFIG_ALLOW_MOVE = 0x20000000,
ADS_SYSTEMFLAG_CONFIG_ALLOW_LIMITED_MOVE = 0x10000000,
ADS_SYSTEMFLAG_DOMAIN_DISALLOW_RENAME = 0x08000000,
ADS_SYSTEMFLAG_DOMAIN_DISALLOW_MOVE = 0x04000000,
ADS_SYSTEMFLAG_CR_NTDS_NC = 0x00000001,
ADS_SYSTEMFLAG_CR_NTDS_DOMAIN = 0x00000002,
ADS_SYSTEMFLAG_ATTR_NOT_REPLICATED = 0x00000001,
ADS_SYSTEMFLAG_ATTR_IS_CONSTRUCTED = 0x00000004
} ADS_SYSTEMFLAG_ENUM
Notable among these values are ADS_SYSTEMFLAG_ATTR_NOT_REPLICATED, which indicates that the attribute is not replicated to other domain controllers. For example, the attributes lastLogon and lastLogoff are not replicated between domain controllers since the information is relatively dynamic and changes frequently.
The ADS_SYSTEMFLAG_ATTR_IS_CONSTRUCTED flag denotes a special attribute that is not stored within an object but is one that the server will "construct" the value for when requested. Constructed attributes usually have values that the server must evaluate before returning. A good example is the distinguishedName attribute, which contains all the relative distinguished names for an object and its containers. Instead of storing the distinguished name in each attribute, Active Directory uses an internal method to figure out the object's location in the directory and returns that value when requested. Another example is the modifyTimeStamp attribute.
Not listed in the ADS_SYSTEMFLAG_ENUM enumeration are category 1 and category 2 attributes that are defined by Active Directory. Category 1 attributes have the 0x10 bit of the systemFlags value set. Category 2 attributes are extensions to the schema and do not have this bit set. You can't explicitly set this category bit.
The IADsProperty interface is used to gather information about attributes. (ADSI uses the term properties, although it's more accurate to say attributes in this context). Most of the important information about an attribute is accessible through the IADsProperty interface, with the exception of information such as the system flags that are specific to Active Directory. Table 9-10 lists the properties of the IADsProperty interface.
IADsProperty Property | Data Type | Description |
---|---|---|
MaxRange | Long | The maximum value for this attribute. For an attribute containing a string, this value would be the maximum number of characters allowed. |
MinRange | Long | The minimum value for this attribute. |
MultiValued | Boolean | Is True if attribute accepts multiple values. |
OID | String | The object identifier for this attribute. |
Syntax | String | The name of the syntax object in the schema that this attribute uses. |
Table 9-10 Properties of the IADsProperty interface.
The IADsProperty interface also defines a Qualifiers method that is not implemented with Active Directory.
Listing 9-5, from the SchemaBrowser sample, uses the properties of IADsProperty and IADsSyntax (discussed later in this chapter) to display a dialog box with information about a particular attribute.
` ADsAttributeInfo.frm - Attribute information for schema
` Shows the IADsProperty and IADsSyntax interfaces
`
`
Option Explicit
Private Sub Form_Load()
On Error Resume Next
` Get information about the attribute to show
` Bind to the attribute object in the schema
Dim adsProperty As IADsProperty
Set adsProperty = GetObject("LDAP://schema/" & frmClassInfo.Tag)
` Fill in text fields with information from IADsClass
txtDisplayName.Text = adsProperty.Name
txtOID.Text = adsProperty.OID If adsProperty.MultiValued Then
` Set the checkbox field
chkMultiValued.Enabled = True
chkMultiValued.Value = 1
Else
chkMultiValued.Enabled = False
chkMultiValued.Value = 0
End If
` Set the syntax info
txtSyntax.Text = adsProperty.Syntax
` Get the range values
If adsProperty.MinRange Then
txtMinValue.Enabled = True
txtMinValue = adsProperty.MinRange
Else
txtMinValue.Enabled = False
End If
` Get the range values
If adsProperty.MaxRange Then
txtMaxValue.Enabled = True
txtMaxValue = adsProperty.MaxRange
Else
txtMaxValue.Enabled = False
End If
` Set the syntax name
txtSyntax.Text = adsProperty.Syntax
` Use IADsSyntax to return the data type
Dim adsSyntax As IADsSyntax
Set adsSyntax = GetObject("LDAP://schema/" & adsProperty.Syntax)
txtDataType.Enabled = True
txtDataType.Text = TypeName(adsSyntax.OleAutoDataType)
` Restore error handling
On Error GoTo 0
End Sub
Listing 9-5 Code from the SchemaBrowser sample showing how to use the IADsProperty and IADsSyntax interfaces to obtain information about an attribute.
In the SchemaBrowser sample, when you select an attribute in the Class Properties dialog box and click the Properties button, a dialog box similar to Figure 9-8 is displayed.
Figure 9-8 The Attribute Properties dialog box showing information obtained using the IADsProperty and IADsSyntax interfaces.
As I mentioned earlier in this chapter, Active Directory uses syntaxes to validate the correct type of data being written for a particular attribute. Active Directory also uses syntaxes as a set of rules to compare attribute values. So, given a search query such as (mail=*HOTMAIL.COM), Active Directory will check the syntax of the mail attribute, which is a DirectoryString, and discover that it's a case-insensitive Unicode string. Therefore, an e-mail address such as chuckop@hotmail.com and ChuckOP@HoTMaiL.COM will correctly match the search query.
Several syntaxes have their roots in the X.500 specification, but some, like the NTSecurityDescriptor syntax are specific to Active Directory. Active Directory recognizes many of the syntaxes defined in the X.500 and LDAP specifications, but it makes no attempt to validate them. While the PrintableString syntax is designed to include characters of the printable character set, usually US-ASCII characters from 32 to 127, Active Directory does not enforce this syntax and will accept characters outside the 32 to 127 range. Future versions of Active Directory might enforce these syntaxes, so it's important that your application choose the correct syntax and work within the constraints defined for that syntax.
A program can enumerate the abstract schema for each syntax. Unlike classes and attributes, however, syntaxes are hard coded into Active Directory. It would be nice if there were a "syntaxSchema" object, but there isn't at the present time.
Table 9-11 lists the syntaxes that Active Directory recognizes. Each syntax is created by an attributeSyntax and oMSyntax pair. The attributeSyntax attribute specifies the OID for the syntax, and the oMSyntax attribute is an integer that supplies a more refined syntax definition. This definition is an alternative syntax description from the X/Open Object Model (XOM) and the X.400 API Association (XAPIA). Syntaxes with an oMSyntax value of 127 also include a value for the oMObjectClass attribute with an encoded object identifier. You don't have to worry about figuring out these values yourself. Just refer to Table 9-11 for the correct syntax and use the values provided. Note that the LDAP ADSI provider supports several more syntaxes than Active Directory does. Only the syntaxes recognized by Active Directory are listed.
Syntax | Identifiers and Data Types | Description |
---|---|---|
AccessPointDN | attributeSyntax: 2.5.5.14 | Used by X.400; not supported by Active Directory. |
Boolean | attributeSyntax: 2.5.5.8 | Boolean, either TRUE or FALSE. |
CaseExactString | attributeSyntax: 2.5.5.3 | Case-sensitive string. |
CaseIgnoreString | attributeSyntax: 2.5.5.4 | Case-insensitive string; also known as a Teletex string. |
DirectoryString | attributeSyntax: 2.5.5.12 | Case-insensitive Unicode string. |
DN | attributeSyntax: 2.5.5.1 | String containing a distinguished name. |
DNWithBinary | attributeSyntax: 2.5.5.17 | Octet string containing binary data with a distinguished name maintained by Active Directory. Use IADsDNWithBinary to work with this syntax. |
DNWithString | attributeSyntax: 2.5.5.14 | String with a distinguished name value maintained by Active Directory. Use IADsDNWithString to work with this syntax. |
Enumeration | attributeSyntax: 2.5.5.9 | Defined by ITU, but not used in Active Directory. |
GeneralizedTime | attributeSyntax: 2.5.5.11 | Time value in Generalized- Time format. |
IA5String | attributeSyntax: 2.5.5.5 | IA5 String. The IA5 character set is defined in the T.50 specification of the ITU and is equivalent to the US-ASCII character set. Active Directory ignores this syntax. |
Integer | attributeSyntax: 2.5.5.9 | 32-bit integer value. |
INTEGER8 | attributeSyntax: 2.5.5.16 | 64-bit integer value. Use IADsLargeInteger to work with this syntax. |
NTSecurityDescriptor | attributeSyntax: 2.5.5.15 | Octet string containing a Windows NT security descriptor. Use IADsSecurityDescriptor to work with this syntax. |
NumericString | attributeSyntax: 2.5.5.6 | String containing numeric characters only. Active Directory ignores this syntax. |
OctetString | attributeSyntax: 2.5.5.10 | Array of bytes for storing binary data. |
OID | attributeSyntax: 2.5.5.2 | Object identifier string. |
ORName | attributeSyntax: 2.5.5.7 | Used by X.400; not supported by Active Directory. |
PresentationAddress | attributeSyntax: 2.5.5.13 | String containing an Open Systems Interconnection (OSI) presentation address (RFC 1278). |
PrintableString | attributeSyntax: 2.5.5.5 | String containing printable characters. |
ReplicaLink | attributeSyntax: 2.5.5.10 | Used internally by Active Directory. |
Sid | attributeSyntax: 2.5.5.17 | Octet string containing a Windows security identifier. |
UTCTime | attributeSyntax: 2.5.5.11 | Time value in UTC-Time format. |
Table 9-11 Syntaxes recognized by Active Directory.
The IADsSyntax interface is one of the simplest interfaces of the ADSI set. It contains just one property, OleAutoDataType, that returns a value representing the data type of the syntax. It's unfortunate that this interface does not return more information, such as the OID or the OM syntax number, but it's very useful for determining how ADSI will return values of a particular attribute. The value of OleAutoDataType is similar to the Visual Basic and VBScript VarType function and can be passed to the TypeName function to return a string with the name of the type. Table 9-12 lists the IADsSyntax property.
IADsSyntax Property | Data Type | Description |
---|---|---|
OleAutoDataType | Long | Returns the Automation data type. The value is defined by COM Automation as a VARTYPE. Pass this number to the Visual Basic TypeName function for a name string. |
Table 9-12 Property of the IADsSyntax interface.
The SchemaBrowser sample uses the IADsSyntax interface when retrieving the information about an attribute. To place a descriptive string in the edit box for display, the program uses the Visual Basic TypeName function as follows:
txtDataType.Text = TypeName(adsSyntax.OleAutoDataType)
C and C++ developers can reference the VARENUM enumeration defined in the header file WTypes.h for the particular VARTYPE.