Extending Site Server 3.0 Using the AUO Framework
Microsoft Site Server Team
Microsoft® Site Server version 3.0 introduces the ActiveX® User Object (AUO) framework. The purpose of the AUO framework is to enable a single unified access mechanism for all user profile information, removing the need for script authors to worry about specifying user identification and profile storage location. With Site Server 3.0 and AUO, a site administrator can configure a machine to define a virtual namespace of profile information, laying out the configuration information needed to access profile data from a variety of sources.
AUO is technically a container of ActiveX Directory Service Interface (ADSI) objects. An administrator defines the number of objects that the AUO will contain and provides the information for managing these objects. This allows an extensible model for "plugging in" new storage providers or alternate profile sources. By writing or reusing ADSI providers, you can access profile information that exists in any store.
Hopefully, this article will make it easier for you to plug in existing ADSI providers and to write new ADSI providers that plug into Site Server 3.0.
Using or Writing an ADSI Provider for the AUO Framework
AUO needs to know how to instantiate a profile object for the current user. This means that AUO needs to build an ADsPath for the object, and then call ADsGetObject.
In the admin configuration, you can specify a constant ADsPath prefix and a choice of suffixes: user name, cookie identification, or user property. At run time, the suffix will be appended to the prefix by the AUO framework to construct the ADsPath.
Here's how it works for each of the three suffix options:
AUO appends the user's name, as recorded in the "REMOTE_USER" Active Server Pages (ASP) server variable, to the static prefix. The string is converted to ADsPath syntax of the form ou=domain/cn=username, as in the following example:
prefix = LDAP://machine/o=microsoft/ou=members 1) remote_user = Fred 1a) final ADsPath = LDAP://machine/o=microsoft/ou=members/cn=Fred 2) remote_user = Washington\Fred 2a) final ADsPath = LDAP://machine/o=microsoft/ou=members/ou=Washington/cn=Fred 3) remote_user = Washington\Redmond\Fred 3a) final ADsPath = LDAP://machine/o=microsoft/ou=members/ou=Washington/ou=Redmond/cn=Fred
AUO looks for a cookie named "Site Server" and, if found, uses the globally unique identifier (GUID) stored in this cookie to create the full ADsPath. This feature was designed to enable automatic profiles for users not using any form authentication; for example:
prefix = LDAP://machine/o=microsoft/ou=members 1) SiteServer cookie value = ID=50c8853e37dffb289158e86ada78cd51 1a) final ADsPath = LDAP://machine/o=microsoft/ou=members/ou=AnonymousUsers/cn=50c8853e37dffb289158e86ada78cd51
AUO fetches the value of the specified property and uses it to build the final path, as in the following example:
prefix = LDAP://machine/o=microsoft/ou=members/ou=postalcodes/cn= configured to use postalCode from the default provider (no alias) 1) User.postalCode = 98005 1a) final ADsPath = LDAP://machine/o=microsoft/ou=members/ou=postalcodes/cn=98005
AUO then calls ADsGetObject with this path, which causes the provider to be loaded using the OLE moniker framework. The provider is expected to return an object that, at minimum, supports the IADs interface.
AUO will optionally use ADsOpenObject if user credentials are necessary during instantiation. This allows the provider to have access to the credentials in order to pass along the security context to the back-end store.
Optionally, if the call to ADsGetObject (or ADsOpenObject) fails, AUO will assume that the object does not exist and attempt to autocreate the object, as follows:
- AUO strips off the leaf section from the far right of the full ADsPath.
ADsPath = LDAP://machine/o=microsoft/ou=members/ou=Washington/ou=Redmond/cn=Fred
AUO strips this down to:
- It calls ADsGetObject (or ADsOpenObject) for the IADsContainer interface.
- It calls IADsContainer::Create to create the leaf node.
- If none of the containers exist in the store, AUO attempts to create them all. (In this example if
ou=Redmonddid not exist, it would be created. Similarly, if
ou=Washingtondid not exist, it would be created as well.)
- After creating the leaf node, AUO will call IADs::Put twice: once for the GUID property and once for the naming property (in this example "cn"). It will ignore any error codes at this time because Site Server's member class has a single required property of GUID. Any other required properties must be set by the script (or another client of AUO) for the autocreate to succeed.
Providing Schema Information
In addition to collecting per-user profile objects, the framework also collates the schema object for the classes of these user objects. This is not a per-user schema object, but the schema that defines what is common across all users.
Stored in the configuration is an ADsPath to a schema object. The framework uses this for autocreating new objects (see above) and for schema exploration. Site Server 3.0 uses this feature in its authoring tools to allow authors to "browse" the profile schema and select properties they wish to use.
The tree control in Site Server 3.0 calls ADsGetObject on the full ADsPath store in the site's configuration database and requires the IADsClass interface. It calls IADsClass::MandatoryProperties and IADsClass::OptionalProperties to get the lists of strings, which are names of attributes in this profile store.
The tool then constructs a new ADsPath for the attribute object based on the original path, as in the following example:
ADsPath for class: LDAP://machine/o=microsoft/ou=admin/cn=schema/cn=member IADsClass::MandatoryProperties returns: "GUID" constructred ADsPath for attribute: LDAP://machine/o=microsoft/ou=admin/cn=schema/cn=guid
ADsGetObject is called with the constructed paths for every attribute. These objects must support IADs. The tool calls IADs::Get for two properties: attributeSyntax and displayName. These are optional properties on attributes that enable the tools to display displayName instead of the cn and to perform syntax checking.