I recently had a project for a client where I needed to authenticate with their implementation of Microsoft’s ADAM. Behind the scenes they are syncing their Active Directory as well as their SAP data with ADAM.
Using ColdFusion, I was able to to do something like the following:
<cfif Trim(selUsername) neq "" and Trim(selPassword) neq "">
<cfset SearchFilter="sAMAccountName=#selUsername#">
<cfset viewfieldlistLookup = "sAMAccountName,distinguishedName,cn,memberOf">
<cftry>
<cfldap action="QUERY"
name="qry_authenticate_user"
attributes="#viewfieldlistLookup#"
filter="#SearchFilter#"
sort="sn"
start="#LDAPBase#"
server="#LDAPServer#"
username="#selUsername#"
password="#selPassword#">
<cfcatch type = "Any">
<cfset err = "#err#
<li>Unable to find that username and password.</li>
">
</cfcatch>
</cftry>
<cfelse>
<cfset err = "#err#
<li>Username and Password are required fields</li>
">
</cfif><strong>
</strong>
<cfif err eq "" and IsDefined( "qry_authenticate_user.recordcount" ) and qry_authenticate_user.recordcount gt 0>
<cfset variables.IsValid = 1>
<cfelse>
<cfset variables.IsValid = 0>
</cfif>
...
The above works because all users of this organization has read only and search access for ADAM.
Now here is the interesting thing. For a different client, they don’t have an implementation of MS’ ADAM directory so I’m tasked with authenticating directly with their Active Directory. This company does not give the user read only or search capability of their Active Directory. So, I ended up having to rewrite it a bit by doing the following:
<cfset variables.IsValid = 0>
<cfset err = "">
<cfif Trim(selUsername) neq "" and Trim(selPassword) neq "">
<cfset SearchFilter="sAMAccountName=#selUsername#">
<cfset viewfieldlistLookup =
"cn,displayName,distinguishedName,givenName,homeDirectory,name,objectClass,sAMAccountName,sn,mail">
<cftry>
<cfldap action="QUERY"
name="qryCheckUser"
attributes="#viewfieldlistLookup#"
start="#LDAPBase#"
server="#LDAPServer#"
username="#LDAPUsername#"
password="#LDAPPassword#"
filter="#SearchFilter#">
<cfif qryCheckUser.RecordCount gt 0>
<cfset curDisplayName = "#qryCheckUser.displayName#">
<cfldap action="QUERY"
name="qryAuthenticateUser"
attributes="#viewfieldlistLookup#"
start="#LDAPBase#"
server="#LDAPServer#"
username="#curDisplayName#"
password="#selPassword#">
<cfif qryAuthenticateUser.RecordCount gt 0>
<cfset variables.IsValid = 1>
<cfelse>
<cfset err = "#err#
<li>Username and/or Password Failed</li>
">
<cfset variables.IsValid = 0>
</cfif>
<cfelse>
<cfset err = "#err#
<li>Username and/or Password Failed</li>
">
<cfset variables.IsValid = 0>
</cfif>
<cfcatch type = "Any">
<cfset err = "#err#
<li>Technical Error Connecting to LDAP Server. Please notify IT.</li>
">
<cfset variables.IsValid = 0>
</cfcatch>
</cftry>
<!---
Here is a list of Active Directory errors you could optionally test for:
525 - user not found
52e - invalid credentials
530 - not permitted to logon at this time
532 - password expired
533 - account disabled
701 - account expired
773 - user must reset password
--->
<cfelse>
<cfset err = "#err#
<li>Username and Password are required fields</li>
">
</cfif>
Because the user doesn’t have permission to query AD via LDAP, I must use a system account to look up that person to find out if they exist first. There is an interesting twist in this second example. After a long time searching, I gave up on trying to find out why I couldn’t pass their NT username (sAMAccountName) and their password like I did in the first example with ADAM. After much trial and error, the only way I figured out how to do it is by looking up their DisplayName and passing that along with their supplied password instead. I think looking up their Distinguished Name (DN) works as well.
I’m assuming that AD blocks the passing of their NT Username for some obscure reason or more likely I have made a mistake. I’ll look to others reading this post for any insight. The current code works but I’m not 100% sure it’s the best solution.
I suppose another option is to set NT Authentication as a requirement for the directory within IIS and handle it that way. A colleague showed me a quick example which seemed like a pretty graceful way to handle things. I just am unclear on the compatibility of this option with other browsers, Servers like Apache, as well as possible problems from remote connections via VPN.
So, there you go, a couple of examples of how I have used ColdFusion to connect to two different LDAP compliant directories.
I’m looking for suggestions to make this better as well as any feedback you might have regarding the use of NT Authentication at the Web server level instead.
Vince Collins