MSDN Deep Dive: Building Killer ASP.NET Applications...

Lot's of requests for the code from the Forms Login demo... here it is...

LDAPAuth.cs:

using System;

using System.Text;

using System.Collections;

using System.DirectoryServices;

using System.Web;

namespace FormsAuthAD

{

public class LDAPAuthentication

{

private string _path;

private string _filterAttribute;

private string domainusername, password, rootdse, uname;

public LDAPAuthentication()

{

}

public LDAPAuthentication(string path)

{

_path=path;

}

public bool IsAuthenticated(string domain, string username, string pwd)

{

string domainAndUsername = domain + @"\" + username;

uname=username;

domainusername=domainAndUsername;

password = pwd;

rootdse=_path;

DirectoryEntry entry = new DirectoryEntry( _path, domainAndUsername, pwd);

try

{

// Bind to the native AdsObject to force authentication.

Object obj = entry.NativeObject;

DirectorySearcher search = new DirectorySearcher(entry);

search.Filter = "(SAMAccountName=" + username + ")";

search.PropertiesToLoad.Add("CN");

SearchResult result = search.FindOne();

if(null == result)

{

return false;

}

// Update the new path to the user in the directory

_path = result.Path;

_filterAttribute = (String)result.Properties["cn"][0];

}

catch (Exception ex)

{

throw new Exception("Error authenticating user. " + ex.Message);

}

return true;

}

public string GetGroups(LDAPAuthentication adAuth)

{

//does not work for users not part of at least 2 groups

//for example, will not work for users that are only part of the

//domain users group

string resultset="";

StringBuilder groupNames = new StringBuilder();

try

{

//bind to AD on the new path

DirectoryEntry entry2 = new DirectoryEntry( rootdse, domainusername, password);

//create a directorysearcher object off the directoryentry object

DirectorySearcher mysearch = new DirectorySearcher(entry2);

//find the username under the person category

mysearch.Filter= "(&(anr="+ uname + ")(objectCategory=person))";

//we want to load the memberOf property

mysearch.PropertiesToLoad.Add("memberOf");

//find an instance

SearchResult result = mysearch.FindOne();

String dn;

int equalsIndex, commaIndex;

int propertyCount = result.Properties["memberOf"].Count;

//enumerate through the properties loaded

for(int propertyCounter = 0; propertyCounter < propertyCount; propertyCounter++)

{

dn = (String)result.Properties["memberOf"][propertyCounter];

equalsIndex = dn.IndexOf("=", 1);

commaIndex = dn.IndexOf(",", 1);

if(-1 == equalsIndex)

{

return null;

}

//create a list seperated by | character

groupNames.Append(dn.Substring((equalsIndex + 1), (commaIndex - equalsIndex) - 1));

groupNames.Append("|");

}

}

catch(Exception ex)

{

throw new Exception("Error obtaining group names. " + ex.Message);

}

return groupNames.ToString();

}

}

}

 

web.config:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<system.web>

 

<compilation defaultLanguage="c#" debug="true" />

<customErrors mode="RemoteOnly" />

<authentication mode="Forms">

<forms loginUrl="logon.aspx" name="adAuthCookie" timeout="60" path="/">

</forms>

</authentication>

<authorization>

<deny users="?" />

<allow users="*" />

</authorization>

<identity impersonate="true" />

<trace enabled="false" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" />

<sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"  cookieless="false" timeout="20" />

<globalization requestEncoding="utf-8"  responseEncoding="utf-8" />

<httpHandlers>

<add verb="*" path="*.sqlx" type="ProAspNet.CS.Ch23.QueryHandler,QueryHandler"/>

</httpHandlers>

</system.web>

</configuration>

 

Logon.aspx.cs:

private void btnLogon_Click(object sender, System.EventArgs e)

{

// Path to you LDAP directory server.

// Contact your network administrator to obtain a valid path.

string adPath = "LDAP://mycompany.com/DC=mycompany,DC=com";

LDAPAuthentication adAuth =

new LDAPAuthentication(adPath);

try

{

if(true == adAuth.IsAuthenticated(txtDomainName.Text,

txtUserName.Text,

txtPassword.Text))

{

string groups = adAuth.GetGroups(adAuth);

// Create the authetication ticket

FormsAuthenticationTicket authTicket =

new FormsAuthenticationTicket(1, // version

txtUserName.Text,

DateTime.Now,

DateTime.Now.AddMinutes(60),

false, groups);

// Now encrypt the ticket.

string encryptedTicket = FormsAuthentication.Encrypt(authTicket);

// Create a cookie and add the encrypted ticket to the

// cookie as data.

HttpCookie authCookie =

new HttpCookie(FormsAuthentication.FormsCookieName,

encryptedTicket);

// Add the cookie to the outgoing cookies collection.

Response.Cookies.Add(authCookie);

// Redirect the user to the originally requested page

Response.Redirect(FormsAuthentication.GetRedirectUrl(txtUserName.Text,

false));

}

else

{

lblError.Text = "Authentication failed, check username and password. Path is : " + adPath + " ";

}

}

catch(Exception ex)

{

lblError.Text = "Error authenticating. " + ex.Message;

}

}

global.asax.cs:

// Extract the forms authentication cookie

string cookieName = FormsAuthentication.FormsCookieName;

HttpCookie authCookie = Context.Request.Cookies[cookieName];

if(null == authCookie)

{

// There is no authentication cookie.

return;

}

FormsAuthenticationTicket authTicket =

null;

try

{

authTicket = FormsAuthentication.Decrypt(authCookie.Value);

}

catch(Exception ex)

{

// Log exception details (omitted for simplicity)

return;

}

if (null == authTicket)

{

// Cookie failed to decrypt.

return;

}

// When the ticket was created, the UserData property was assigned a

// pipe delimited string of group names.

String[] groups = authTicket.UserData.Split(

new char[]{'|'});

// Create an Identity object

GenericIdentity id =

new GenericIdentity(authTicket.Name,

"LdapAuthentication");

// This principal will flow throughout the request.

for (int i=0;i<groups.Length;i++)

{

string cn = "CN=";

groups[i]=groups[i].Substring(3);

groups[i]=groups[i].Substring(0, groups[i].IndexOf(","));

}

GenericPrincipal principal =

new GenericPrincipal(id, groups);

// Attach the new principal object to the current HttpContext object

Context.User = principal;