Forms Authentication with Active Directory in ASP.Net2.0

Saturday, November 22, 2008

What Is Active Directory
An active directory is a directory structure used on Microsoft Windows based computers and servers to store information and data about networks and domains.
ASP.Net 2.0 has membership feature which we can use for forms authentication. This feature provides an abstraction for the underlying data store that is used to maintain user credentials like as user name, password. The membership feature includes an API that helps you to easily validate user credentials and manage the user database.
To use Active Directory (AD) in ASP.Net, First we have to create “Instance For ADAM” and configuration of it. Then I will create a sample application and explain how to interact ADAM with ASP.Net.
Creating Instance and Configuration:
Install ADAM software. Then select “Create an ADAM Instance” in the start menu.

Select “A Unique Instance”, then give some name to the Instance

Give a Port number, leave default SSL port number.

Select “Yes, to create an application directory partition”.

Here O,DC are the LDAP attributes.
Leave default values for location of data file and backup file.

Provide the account under which this account runs.

It will prompt for confirmation,select “YES”. Then select System

Import all LDIF Files.

Copy contents and paste in a file(notepad, etc)

Click Next which will Instantiate new Instance.

Click Finish. And open ADAM ADSI Edit in start menu.

Right click on ADSI root node and click connect to. And enter the details as.

Right click on Distinguished name and select New -> Object in that one select “Organizational Unit” which for Admin.

Click on Finish. Now right click on the Organizational Unit which is created and select Properties, in that properties select distinguished name property and copy into a notepad.

Right click on new OU(Organizational Unit) and create a new User

Now right click on CN=TestAdminUser and select properties, in that properties change the User Principal Name as

And make the “Don’tExpirePassword” property to false.

Now right click on TestAdminUser and reset the password.

Go to CN=Roles and choose CN=Administrators and choose properties and go to member property.

Click on Add ADAM Account and add the user created above.
Create another Organizational Unit (OU) for the user to create login account programmatically.

Go to Roles and choose CN=Readers and choose properties and then select “member” property. Add ADAM account.

Now open ADAM command prompt and run command “dsmgmt.exe”

Now configure ADAM schema – Password policy setting. Type as “mmc /a”.

Click on File – Add/Remove snap – In and then click on ADD and select ADAM Schema

Select ok, ok. Right Click on ADAM Schema and change the server details. Enter the details of ADAM Instance.

Right click on Attributes and click on create attribute.

Go to Classes Node in the Adam Schema, select User and right click and choose properties and select Attributes tab.

Add all the properties created above.
Go to ADAM ADSI Edit , select the instance and right click on it. In that select Update Schema Now.

Note:I created a document of "ADAM Instance Creation" with all of screen shots. You can find below.
Interacting with ADAM with ASP.Net
Step 1: Create a website and add three pages to the application with name Registration.aspx, Login.aspx, Welcome.aspx.
Step 2: Open Registration.aspx page. Drag “CreateUserWizard” control and set the “ContinueDestinationPageUrl” as “~/login.aspx”.
Step 3: Now open login.aspx page. Drag “Login” control and set the “DestinationPageUrl” property to welcome.aspx. and set “DisplayRememberMe” to false.
Step 4: Now open the web.config file and add the blow mentioned code.

  1. <appSettings>

  2.         <add key="connectionUsername" value="CN=TestAdminUser,OU=TestAdmin,O=Admtest,DC=Testing,DC=COM"/>

  3.         <add key="connectionPassword" value="hello"/>

  4.         <add key="connectionServer" value="localhost"/>

  5.         <add key="connectionPortNumber" value="50001"/>

  6.         <add key="connectionRoot" value="O=Aest,DC=Testing,DC=COM"/>

  7.         <add key="connectionUsersContainer" value="TestUsers"/>

  8.         <add key="defaultRoles" value="Readers"/>

  9.         <add key="connectionRolesContainer" value="Roles"/>

  10.         <add key="connectionAdminsContainer" value="TestAdmin"/>

  11.     </appSettings>

  1. <appSettings>

  1. <connectionStrings>

  •         <add name="TestCon" connectionString="LDAP://localhost:50001/OU=TestUsers,O=Aest,DC=Testing,DC=COM"/>

  •     </connectionStrings>

    1. <appSettings>

    1.  <machineKey validationKey="51AF1A8093A19043C4AAEC218BC36D9C3299C06AA823DDDAC5877431E7AFE90C1B053057EA760CA4DCC15D3CED15035588BB1B461C959434DE68B8381CDF99AA" decryptionKey="BCE825E1668990B11CE01AFF01C4AFE8D63AE300958A85EC" validation="SHA1"/>

    2.         <membership defaultProvider="MyADAMMembershipProvider">

    3.             <providers>

    4.                 <add name="MyADAMMembershipProvider"

    5.          type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=, Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"

    6.           connectionStringName="TestCon"

    7.           connectionUsername="CN=TestAdminUser,OU=TestAdmin,O=Aest,DC=Testing,DC=COM"

    8.           connectionPassword="hello"

    9.           connectionProtection="None"

    10.           enableSearchMethods="true"

    11.           requiresUniqueEmail="false"

    12.           enablePasswordReset="true"

    13.           requiresQuestionAndAnswer="true"

    14.           attributeMapPasswordQuestion="PasswordQuestion"

    15.           attributeMapPasswordAnswer="PasswordAnswer"

    16.           attributeMapFailedPasswordAnswerCount="BadPasswordAnswerCount"

    17.           attributeMapFailedPasswordAnswerTime="BadPasswordAnswerTime"

    18.           attributeMapFailedPasswordAnswerLockoutTime="BadPasswordAnswerLockoutTime"

    19.         minRequiredNonalphanumericCharacters="0"

    20.       passwordStrengthRegularExpression = "(?=.{7,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@`~$%^+=!*])"/>

    21.             </providers>

    22.         </membership>

    Once you modified web.config as like above. Then run the registration page and create a User. If user created succesfully you can find a file created with user name in "TestUsers" section of ADAM.
    Note:Add "System.DirectoryServices" assembly reference to your application.
    Download ADAM

    Download creating ADAM Instance Step by step Document.

    Download Sample Application

    Using Typed DataSet in .Net

    Tuesday, November 4, 2008
    A typed DataSet is a strongly-typed container for collections of business entity data and inherits from the System.Data.DataSet class. However, unlike the standard DataSet that features late binding and weak typing, typed DataSets provide strongly-typed access to data.

    This means that they expose table, column, and relation objects as named properties, rather than generic collection elements. Essentially, we can write code as MyRow.ClientName, as opposed to MyRow["ClientName"].

    Here we can see how to create a Typed DataSet

    Typed DataSets also give us the ability to use IntelliSense for statement completion
    and type checking at compile time. In the context of reporting, they aid in data-driven report design and provide an excellent form of self-documentation of stored procedure result sets.

    Benefits of Typed DataSets
    Typed DataSets have many benefits over standard DataSets. Let’s take a look at how they simplifyand enhance the development process.

    Strong Typing
    Typed DataSets give you the ability to use IntelliSense for statement completion and type checking at compile time. Where as in Untyped we must type the column name correctly (or receive a runtime error), and you must cast the column object to the correct DataType.
    Typed DataSets also make it easy to define default column values when you add new

    Easier Traversing of Hierarchical Data
    If a typed DataSet contains table relations, the XML Schema Definition tool adds methods to the typed DataSet class for traversing hierarchical data

    Using Find
    Additionally, if you define a primary key for any table in a typed DataSet, the class exposes a FindXXX() method associated with the column name. We can perform a find against a table with a primary key of OrderID by using the pregenerated method FindByOrderID(). If the primary key is a concatenation of two columns (for example, CustomerID and OrderID), then it can be as FindByCustomerIDOrderID().

    Null Value Handling
    Working with null values is a little easier when using typed DataSets. Each typed DataRow contains a method to check whether a column value is null: MyRow.IsAddressNull(). The DataRow also contains a second method to set a column value to null: MyRow.SetAddressNull().

    Typed DataSets are valuable in the reporting process. We can design report content from a typed DataSet definition, even before the stored procedures are constructed. Once again, the typed DataSet becomes a form of documentation for the report DataSource.

    Since a typed DataSet is a class that inherits from System.DataSet, we can further subclass a typed DataSet to add validation code or any other methods. Alternatively, we can also take advantage of the new partial class capability in Visual Studio 2005 to add method code for our DataSet.

    Simplify Data Binding
    Typed DataSets can simplify design-time data binding—we can define a DataSource in the property sheet. Because our sample application stores all typed DataSets as a separate project DLL, we can add the DLL to the Visual Studio toolbox, and then drop an instance of the typed DataSet onto a form as a precursor to design-time data binding. Developers who define DataSources in code still gain the advantage of table/column name discovery through IntelliSense.

    DataSet Operations

    Adding Rows to Dataset

    1. //Adding Rows to Dataset

    2. Company cm = new Company();

    3. cm.Employee.AddEmployeeRow("123", "Ram", "Krishna", "9866", "Hyderabad","22/08/2008");

    4. //Adding Rows in Another way.

    5. Company.EmployeeRow enewRow = cm.Employee.NewEmployeeRow();

    6. enewRow.EMPID = "235";

    7. enewRow.FName = "Ashfaq";

    8. enewRow.LName = "Sajju ";

    9. enewRow.Mobile = "123";

    10. enewRow.City = "Sec-Bad";

    11. cm.Employee.AddEmployeeRow(enewRow);

    12. GridView1.DataSource = cm;

    13. GridView1.DataBind();

    Looping Through Dataset

    1. Company cmp = new Company();

    2. // cmp = Genertate some method for rows();

    3. foreach (Company.EmployeeRow rw in cmp.Employee.Rows)

    4. {

    5. string name = rw.FName;

    6. }

    Using Date Math

    1. // To Check the emps who joined b/w 3 months from current date

    2. Company com1 = new Company();

    3. int totDays;

    4. TimeSpan ts;

    5. DateTime today = DateTime.Today;

    6. foreach (Company.EmployeeRow erow in com1.Employee.Rows)

    7. {

    8. ts = today.Subtract(erow.DOJ);

    9. totDays = ts.Days;

    10. if (totDays > 90)

    11. {

    12. // Process Here

    13. }

    14. }

    Defining Primary key programmetically

    1. Company com2 = new Company();

    2. com2.Employee.PrimaryKey = new DataColumn[]

    3. {

    4. com2.Employee.EMPIDColumn

    5. };

    6. com2.Employee.PrimaryKey = new DataColumn[]

    7. {

    8. com2.Employee.Columns["EMPID"]

    9. };

    10. // If the EMPID is defined are primary key..

    11. // Then only we get FindByEMPID

    12. Company.EmployeeRow eow = com2.Employee.FindByEMPID("123");

    13. string empid = eow.EMPID;


    1. // We can apply filtering for Dates, Strings, Numerics, etc

    2. Company cm = new Company();

    3. string cFilterExpr = "";

    4. // Date handling

    5. cFilterExpr = "DOJ >= '01/31/2006' AND InvoiceDate <= '02/28/2006'";

    6. cFilterExpr = "DOJ >= #01/31/2006";

    7. // Full and partial text searches

    8. cFilterExpr = "FName = 'Ram'";

    9. // Search for "Construction" anywhere

    10. cFilterExpr = "FName LIKE '%Ram%'";

    11. // Numeric data

    12. cFilterExpr = "Sal > 1000";

    13. // Combining AND/OR

    14. cFilterExpr = "(FName LIKE '%Ram%' OR FName LIKE %Krishna%') AND Sal > 5000";

    15. // Using the IN function

    16. cFilterExpr = "Sal IN (1000,2000,3000)";

    17. cFilterExpr = "FName IN ('Ram','Rama')";

    18. cFilterExpr = "FName NOT IN ('Ashfaq','Sajju')";

    19. // Using the ISNULL function

    20. cFilterExpr = "ISNULL(InvoiceAmount,-9999)=-9999";

    21. // Using the PARENT (or CHILD) function

    22. cFilterExpr = "Parent(RelName) = like '%NATIONAL'";

    23. DataRow[] dr = cm.Employee.Select(cFilterExpr);

    We can peroform more operations like Data Relations, etc with Typed Datasets.