Implementing Active Directory Services in ASP.NET 2.0

Thursday Jan 10th 2008 by Quin Street

Share:

With the introduction of ASP.NET 2.0 and Visual Studio 2005, many of the security tasks required to connect an application's authentication and authorization mechanisms to Active Directory have been dramatically simplified. This article shows how to perform both Active Directory (LDAP:\\) and local member server (WinNT:\\) tasks.

By Peter Nichols

The advent of ASP.NET 2.0 and Visual Studio 2005 Express has kicked the door wide open for creating integrated
applications. Many of the security tasks required for an applications authentication and authorization mechanisms
to be hooked into Active Directory have been dramatically simplified.

The information below will provide a "cookbook" of recipes to perform both Active Directory integration
tasks (using the "LDAP:\\" provider) as well as local member server tasks (using the "WinNT:\\" provider).
Each section can be cut and pasted directly into its own aspx.vb code page, and is self sufficient. Remember that you
must use challenge response for any of this code to work, so that AD or the local member server has the correct
permissions to perform the action. Setting the encryption level and other such communication details between the
member server running the code and the directory service domain controller is beyond the scope of this article.

Although Microsoft provides a deep mechanism for providing security into an application, your business model may
not allow independent authentication and or authorization stores. All of these examples were created because a
customer did not want to create extranet Sharepoint users in Active Directory (AD mode), so the local machine
was the next best alternative.

Verify That a User is in a Active Directory Group

The first recipe is to verify a user that has connected to an ASP.NET 2.0 page is in an Active Directory group.
Add a new item (Web Form) to your site, called "Test.aspx", placing the code in a separate file.
Go to design mode, and double click on the page, which should give you a blank Page_Load subroutine.
Paste this code directly over the existing code:

REM This is equivalent to adsRoot=GetObject("LDAP://OU=ADSI,DC=ds,DC=microsoft,DC=com")
REM and opens a connection to the root of the directory that you
REM would want to search. Replace the string with your directory service root.
Dim adsRoot As New DirectoryEntry("LDAP://OU=ADSI,DC=ent,DC=ds,DC=microsoft,DC=com")

REM This sets up the filter to be used in searching for the user in AD.
Dim adsSearch As DirectorySearcher = New DirectorySearcher(adsRoot)

REM Grab the User ID of the person pulling the page, or the sAMAccountName
strUser = Page.User.Identity.Name

REM Search Active Directory For the user via
REM System.DirectoryServices.DirectorySearcher
Try
REM We'll load the filter with the items we want to fetch,
REM similar to a SQL statement.
REM The first is what we are looking for, the sAMAccountName.
adsSearch.PropertiesToLoad.Add("sAMAccountName")
REM We will also need the group membership of the user once
REM we have found the user.
adsSearch.PropertiesToLoad.Add("memberof")
REM We are likely to also need the common name, although
REM it's not needed for this example.
adsSearch.PropertiesToLoad.Add("cn")
REM We don't need the .FullName property for this example,
REM but you might, so I show it here.
adsSearch.PropertiesToLoad.Add("FullName")
REM build the search filter (looking for the user with a login
REM name that matches who connected to the page.
adsSearch.Filter = "sAMAccountName=" & strUser

REM Get some variables ready to receive the results
Dim oResult As SearchResult
Dim RetArray As New Hashtable()
Dim adsGrpcn As String
binFlag = False

REM Now get the results (just one), what you get back is
REM an object that points to the found user
oResult = adsSearch.FindOne
REM You can now loop through the list of groups
For Each adsGrpcn In oResult.GetDirectoryEntry().Properties("memberof").Value
REM You'll want to splice this string a bit to match a specific group
REM Then test to see if it matches your application group. Make sure to
REM use TRIM() to avoid embedded spaces in the common name of the group.
Response.Write(adsGrpcn)
If adsGrpcn = "MyGroup" Then binFlag = True
Next
Catch ex As Exception
Response.Write("I got the following error while trying to authenticate you: " _
& ex.Message)
Response.End()
End Try
If binFlag Then
Response.Write("You are authorized!")
Else
Response.Write("You are not authorized!")
End If
End Sub
End Class

List All Users

This recipe is for iterating all users on a local machine, which can then populate a list box. This would also
likely find its way to the "Page_Load" sub, but is shown separately. It presumes a list control
called 'lstUser' is on the page. It will populate the list showing the Full Name, but have the User ID as the value.

Dim lblDMBase As String
lblDMBase = "WinNT://LocalMachine"
REM This is equivalent to adsUser=GetObject("WinNT://LocalMachine/User") and
REM opens a connection to the local machine. It does not necessarily need to be
REM the machine the web page is running on.
Dim adsComputer As New DirectoryEntry(lblDMBase)
Dim adsUser As DirectoryEntry
REM This works identically to classic asp, iterate through the parent object.
For Each adsUser In adsComputer.Children
REM If it is a user, then add it to the list box.
If adsUser.SchemaClassName = "User" Then
lstUser.Items.Add(New ListItem(adsUser.Properties("FullName").Value, adsUser.Name))
End If
Next

Create a New User

The next recipe is for creating a user on the local machine, easily tested by dropping a button on the previous page and double clicking on it. Remember that the user running the page has to have the permissions create a user (be in the local Administrators group).

Dim lblDMBase As String
lblDMBase = "WinNT://LocalMachine"
REM This is equivalent to adsUser=GetObject("WinNT://LocalMachine/User") and
REM opens a connection to the local machine. It does not necessarily need to be
REM the machine the web page is running on.
Dim adsComputer As New DirectoryEntry(lblDMBase)
Dim adsUser As DirectoryEntry
REM Open a connection to the Group
Dim adsGroup As New DirectoryEntry(lblDMBase & "/Users")
REM You can also open the object by:
'Dim adsGroup As DirectoryEntry
'adsGroup.Path = lblDMBase & "/Users"
Try
REM Add a user to the defined computer object
adsUser = adsComputer.Children.Add("TestUser", "User")
REM Populate the FullName and Description Properties
adsUser.Properties("FullName").Add("Test Full Name")
adsUser.Properties("Description").Add("Test Description")
REM Set the password (a random password function would be good here).
adsUser.Invoke("SetPassword", "password")
REM Identical to .SetInfo
adsUser.CommitChanges()

The next recipe is to view attributes of a local user. This example builds upon the previous example by using the user selected in the previous examples list box. Create a "View User" button on the form, double click on the button, and add this code into the sub:

Simple check for a user in a local group, it presumes you've added the user iteration code above and created another button:

Dim lblDMBase As String
REM The object opened here is a local group, but could be an AD Group or OU
lblDMBase = "WinNT://LocalMachine"
REM This is equivalent to adsUser=GetObject("WinNT://LocalMachine/User") and
REM opens a connection to the local machine. It does not necessarily need to be
REM the machine the web page is running on.
Dim adsUser As New DirectoryEntry(lblDMBase & "/" & lstUser.SelectedItem.Value)
REM This is equivalent to adsUser=GetObject("WinNT://LocalMachine/Group")
Dim adsGroup As New DirectoryEntry(lblDMBase & "/Administrators")
If adsGroup.Invoke("IsMember", adsUser.Name.ToString()) Then
Response.Write "You're a Member!"
Else
Response.Write "You're a not a Member!"
End If

Iterate All Computers in a Container

This next recipe is more for auditing. It iterates all the computers in a container and then connects to the local administrator of that server to get the account policy.