ADSI user validation with COM+ reduces maintenance and ensures fresh data

When you need to obtain user validation from an application, avoid maintaining a duplicate database. Use ADSI to go right for the Active Directory to be sure that you get valid user info. This example shows you how easy it really is.

Active Directory is one of the core features of Windows 2000 and will remain so in .Net. Microsoft has essentially encapsulated the Exchange address book and the users and groups of Windows NT to produce a powerful, centralized database, readily accessible via COM+ through the Active Directory Services Interface, or ADSI.

ADSI also includes support for Lightweight Directory Access Protocol (LDAP) and most of its organizational structure units. Thanks to this structure, the entire corporate organization chart is accessible to your intranet applications. LDAP is an industry standard protocol, with many third-party applications available for management and maintenance.

I was recently in a position to use ADSI to authenticate users to a corporate intranet. Previously, these users were authenticated using a separate SQL Server database table of users and passwords. Changing the system to use ADSI made a single point of contact for sysops to change passwords or disable users.

It was an n-tier system that used the RDS::Datafactory for a data layer. The database was SQL Server, and the environment was mixed Windows 2000 and Windows NT. The intranet is written in ASP, and the browser is IE 5.0 with a stipulation for no browser-specific code.

Analyzing the situation
Originally, the main page of the intranet requested a login and then passed the username and password to the local Security COM+ object with a method called clsSecuritySelect::Login. This method would accept the username and password and then return the first name and last name. The ASP page would determine whether the user was authenticated and save the returned information in a homemade state object.

My goal was to modify the Login method so that the AD database was used instead of the SQL database—and to do it in such a way that none of the ASP or other related methods needed to be changed. I used the ADSI interface to replace the call to the DataFactory and return the name from the ADs LDAP schema.

ADSI basics
ADSI is an application programming interface to the Active Directory. Microsoft provides three methods to access the information in the directory:
  • LDAP query string
  • ADO
  • The ADSI object model

Although the ADO model is excellent for complex searches, and the object model is fantastic for updates, I decided to use the LDAP method here for clarity.

To use ADSI with COM+, you must reference the Active DS Type Library (Figure A).

Figure A
Referencing Active DS Type Library

We’ll use three classes of the Active Directory interface: IADs, OpenDSobject, and Container:
Dim oRootDSE As IADs
Dim oDSObj As IADsOpenDSObject
Dim oAuth As IADsContainer

First, we’ll query the Active Directory system for the domain. Since Active Directory is an amorphous system with no real primary controller, the overall directory needs to be queried as to the closest current directory controller. According to the documentation, you should never bind an Active Directory object to a server name. The IADs interface, which we’ll be using, defines the basic object features of any Active Directory object. It supports serverless binding, which allows us to not supply the name of any domain controller.
' —- Retrieve the current domain —-
Set oRootDSE = GetObject("LDAP://RootDSE")
StrNamingContext = oRootDSE.Get("defaultNamingContext")
Set oRootDSE = Nothing

The Get method of the IADs objects allows us to pull a known value out of the Active Directory schema based on the level of the hierarchy where our object sits. The defaultNamingContext lives at all levels of the hierarchy.

Our next step is authentication of the user. We don’t need the user login to get information from the schema since our object runs as an Administrator, so we’ll just authenticate and check for errors.
' —- Validate against the namespace —-
Set oDSObj = GetObject("LDAP:")
Set oAuth = oDSObj.OpenDSObject("LDAP://" & _
    strNamingContext, strUsername, _

If this throws an error, our user is denied access, and we pass the error up to the ASP code. If there is no error, we kill our objects and continue.

Finally, we use the ADO interface to ADSI to get the AdsPath and user name. The AdsPath is the LDAP reference to the user and can be used to generate a User object.
adoConnection.Provider = "ADSDSOObject"
Set adoRecordset = adoConnection.Execute("<LDAP://" & _
    strNamingContext & ">;(sAMAccountName=" & _
    strUsername & ");AdsPath, cn")

Now we have a recordset with the cn, or common name, which we can parse into a first and last name and return to the ASP page. Since the user is authenticated through ADSI, we can assume he or she has permission to use the pages. The common name can be used for customization of the user experience or other functionality.

No more double maintenance
Now that you can programmatically access the Active Directory, you do not need to maintain a separate database with user information. This eliminates extra maintenance and synchronization problems, and your apps can be assured of getting the freshest data available.

Editor's Picks