ADSI LDAP Search

Thanks to Tim Heidinger for providing this code sample -- this document is a reproduction of Tim's original

CalNet Directory Services and Microsoft Active Directory Service Interfaces (ADSI)

Aron Roberts' excellent article in Winter 2000 BC&C foretold of innovative applications that would soon exploit a new single, reliable source of information about campus people and certain campus resources. Now that the CalNet Directory Service is a reality, this article gives the Windows developer on campus basic information about how to develop CalNet Directory Service enabled applications.

Accessing CalNet Directory Service from Windows

The CalNet Directory Service is based on the Lightweight Directory Access Protocol (LDAP), an Internet standard, so your favorite Windows programming/scripting language must become LDAP aware. One option is to use Microsoft's Active Directory Service Interfaces (ADSI) . This article describes the use of ADSI 2.5 which is available for all versions of Windows and compatible with CalNet Directory Services. (Note: Microsoft recently released an upgrade to ADSI 2.5 called Active Directory Client Extension. It solved problems for me, especially after I upgraded MDAC and started getting "Class not registered" errors. Everything in this article uses ADSI 2.5). According to Microsoft:

Active Directory Service Interfaces abstract the capabilities of different directory services from different network vendors to enable developers of scripts or C/C++ applications to easily query for and manipulate directory service objects. The standard Active Directory Service Interfaces objects, or providers, are found within multiple namespaces, typically directory services for various network operating systems. Providers enable communication between the server or client. ADSI 2.5 includes providers for Lightweight Directory Access Protocol (LDAP).

In other words, you can use ADSI to get your Windows machine to talk with the CalNet Directory Service. The following Microsoft resources provide the basics of how it works:

Using an ActiveX Data Object (ADO) to Bind to ADSI Providers

http://msdn.microsoft.com/library/en-us/netdir/adsi/using_an_activex_dat...

Note, the "ADSI Flag" property described is not available when using ADO/ADSI 2.5 (it might be different with the Active Directory Client Extension but I haven't tried it). As a result most of the detail about using the ADS_AUTHENTICATION_ENUM enumeration to specify different authentication options like ADS_USE_SSL is not available. However, it appears that specifying the LDAP ssl port number in the bind is all that is needed to communicate with CalNet Directory Services via SSL. Make sure to set the "Encrypt Password" property to False as detailed in How to Use ADSI to Query a Third-Party LDAP Server.

Use ADO to Access Objects Through an ADSI LDAP Provider

http://support.microsoft.com/support/kb/articles/Q187/5/29.asp

By default ADO can not process multiple values (variant) LDAP fields which are some of the most useful fields in the CalNet Directory Service, see ARRAYCONVERT.EXE Variant Conversion Functions to fix the problem. See Searching Active Directory with ADO for more detailed information about searching syntax. Note: I had problems with ADSI once I upgraded to MDAC 2.6, it now appears there is a fix although I haven't tried it, see ADSI 2.5 and MDAC 2.6 Compatibility Issues.

Sample Code

The following example uses Visual Basic Script contained within Active Server Pages(ASP) hosted by Internet Information Server (IIS) 4.0 on Windows NT Server 4.0. However, developers using any version of Windows with a COM compliant language such as Visual Basic, Java, and Visual C++ or even Visual FoxPro can benefit.

<%@LANGUAGE=VBSCRIPT
@ENABLESESSIONSTATE false%>
<%
Option explicit
response.buffer = True
'----------------------------------------------------------------
' CalNetADSI.ASP
'----------------------------------------------------------------
 
call Main
 
Sub Main
    Dim cnnLDAP,rstLDAP,strSQL,strUser,strPass,cnvtLDAP,i,j,intLoop,strName,intRecNum,strSSL
 
    strUser = ""
    strPass = ""
 
    set cnnLDAP         = Server.CreateObject("ADODB.Connection")
    set rstLDAP         = Server.CreateObject("ADODB.RecordSet")
    Set cnvtLDAP        = Server.CreateObject("ADs.ArrayConvert")
    cnnLDAP.Provider    = "ADSDSOObject"
    cnnLDAP.Properties("User ID")         = strUser
    cnnLDAP.Properties("Password")        = strPass
    cnnLDAP.Properties("Encrypt Password")    = False
    cnnLDAP.Open
 
    if Request.Form("strLastName")="" then
        strName="heidinger"
    else
        strName=Trim(Request.Form("strLastName"))
    end if
    if Request.Form("strSSL")="Y" then
        strSSL=":636"
    else
        strSSL=""
    end if
 
    '------------------------------------------------------------------------
    'For SSL connections with the original ADSI 2.5 just specify port 636
    'in the bind statement, see below:
    '------------------------------------------------------------------------
 
    strSQL="<LDAP://ldap.berkeley.edu" & strSSL & "/ou=people,dc=berkeley,dc=edu>;(cn=" & _
    strName & "*);displayname,mail,cn;subtree"
 
    response.write("<html><title>CalNet ADSI Test</title><body><a href="https://calnetweb.berkeley.edu/%3C/code%3E%3Ccode%3E"ldapadsi.htm"">" & _
        "<font size=""+1"">CalNet Directory " & _
        "Services via Microsoft's ADSI</font></a> <br>" & _
        "<form action="https://calnetweb.berkeley.edu/%3C/code%3E%3Ccode%3E"calnetadsi.asp"" method=""post"">" & _
        "Common Name starts with: <input type=""text"" name=""strLastName"" size=""10""> " & _
        "<input type=""CheckBox"" name=""strSSL"" value=""Y""> Use SSL " & _
        "<input type=""submit"" Value=""Lookup in CalNet Directory Services""></form>" & _
        " Started at " & now & _
        ", looking for <b>" & strName & "</b> with this syntax:<br><i><" & mid(strSQL,2) & _
        "<br><br>(Up to three records displayed)</i><br><br>")
 
    rstLDAP.open strSQL,cnnLDAP,0,1,&H0001
        '0=adOpenForwardOnly,1=adLockReadOnly,&H0001=adCmdText
 
    if rstLDAP.eof then
        response.write("Nothing found<br>")
    else
        intRecNum=0
        Do while not rstLDAP.eof
            For intLoop = 0 To rstLDAP.Fields.Count - 1
                response.write(rstLDAP(intLoop).name & "=")
                if rstLDAP(intLoop).type=12 and not isnull(rstLDAP(intLoop).value) then
                  '---------------------------------------------------------------------
                  ' Here's the tricky part, getting ADO to recognize the Variant type
                  ' see http://support.microsoft.com/support/kb/articles/Q250/3/44.ASP
                  '---------------------------------------------------------------------
                  i=cnvtLDAP.CStrArray(rstLDAP(intLoop).value)
                  for each j in i
                    response.write(j & "<br>")
                  next
                else
                  response.write(rstLDAP(intLoop).value & _
                  " <i>(not variant)</i><br>")
                end if
            next
            response.write("<hr>")
 
            '-------- Display only first couple ---------
            intRecNum=intRecNum+1
            if intRecNum>2 then
                exit do
            else
                rstLDAP.MoveNext
            end if
        loop
    end if
    rstLDAP.close
    cnnLDAP.close
    response.write("Ended at " & now & "</body></html>")
end sub
%>