Setting up different Authentication models in WSS3.0 Currently WSS 3.0 supports following authentication models for intranet, extranet and Internet environments for their partner applications.   1. Windows 2. Forms   Forms authentication is preferred over windows authentication for extranet environments. The WSS 3.0 supports following Authentication providers.   1. Active Directory Membership Provider 2. SQL Membership provider 3. Single Sign On - SSO   In this article I have made an attempt to depict Authentication using Active Directory membership provider. And another challenging functionality implemented is, if for some reason, user can not logon, the page would display the reason along with logon details like account locked, expiry date and disabled features as shown in screen shot below:     Hence I have divided this article as 3 parts. Part 1: Develop and Implement Custom Login for Active Directory Part 2: Develop and Implement Custom Change Password for Active Directory  

Part 1: Develop and Implement Custom Login for Active Directory

In order to implement this part, firstly I would need to create a class library which would override Login page of sharepoint provided page.   3. From Visual Studio, create a class library and name the project  as SharePoint.Authentication.Custom.ActiveDirectory.   4. Since I need to deploy onto SharePoint, I have thought that, its better idea to put this dll into GAC. So, from the properties of the Project, under Signing tab, create a new snk and check delay sign.     5. Rename the class1.cs to CustomLoginPage.cs 6. Add a reference from .Net  tab, select Windows SharePoint Services as shown below:     7. Add a reference from .Net  tab and browse to Microsoft.SharePoint.ApplicationPages.dll located in C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\CONFIG\BIN\     8. Add a reference  from COM tab, Active DS Library.     9. Add a reference from .Net tab, System.Web, System.DirectoryServices   10. In class CustomLoginPage.cs, add the following entries in the namespace section Using Microsoft.SharePoint; Using ActiveDs; 11. Now, we are ready to start making some changes to the class file. 12. Inherit the class from Microsoft.SharePoint.ApplicationPages.LoginPage class 13. Since we are interested to know logon details on error, I am gonna override on error event of Login page.as shown below  
 

protected override void OnLoad(EventArgs e)

        {

           
this.login.LoginError += new EventHandler(login_LoginError);

           
base.OnLoad(e);

        }

 

      void
login_LoginError(object sender, EventArgs e)

        { }

14. In the above event handler, I would need to call login details if user can not logon. Hence for the simplicity, I have created a simple class and then populated data. Below show is the class.
public class ADResult

    {

        public
ADResult() { }

 

        private bool
_locked;

 

        public bool
Locked

        {

            get {
return _locked; }

            set {
_locked = value; }

        }

 

        private bool _disabled;

 

        public bool
Disabled

        {

            get {
return _disabled; }

            set {
_disabled = value; }

        }

 

        private string
_lastLogon;

 

        public string
LastLogon

        {

            get {
return _lastLogon; }

            set {
_lastLogon = value; }

        }

 

        private
int  _expiresIn;

 

        public
int  ExpiresIn

        {

            get {
return _expiresIn; }

            set {
_expiresIn = value; }

        }

 

        private string
_errorMessage;

 

        public string
ErrorMessage

        {

            get {
return _errorMessage; }

            set {
_errorMessage = value; }

        }

 

        private bool
_valid;

 

        public bool
Valid

        {

            get { return _valid; }

            set {
_valid = value; }

        }

 

    }

15. Next step is create a method GetLoginDetails() which actually would query Active Directory and return the ADResult object as shown below:   Also, You might wonder on one thing, why i have used SPSecurity.RunXXX(delegate()), this is because the code will not run until it is trusted in Sharepoint, hence it is required to elevate code security level.  
private ADResult GetLoginDetails(string username)

        {

            ADResult
adResult = new ADResult();

            try

            {

                string
strLoginName = username;

                int iPosition =
strLoginName.IndexOf("\\") + 1;

               
strLoginName = strLoginName.Substring(iPosition);

 

               
DirectoryEntry entry = new DirectoryEntry(ADPATH); //ADPATH is path of
Active dir


               
DirectorySearcher search = new DirectorySearcher(entry);

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

              

               
Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(delegate()

                    {

                       
SearchResult result = search.FindOne();

                       
entry = result.GetDirectoryEntry();

                   
});

              

                try

                {

                   
Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(delegate()

                    {

                       
object o = entry.InvokeGet("IsAccountLocked");

 

                       
if (o != null)

                       
{

                           
bool locked = (bool)o;

                           
adResult.Locked = locked;

                       
};

 

                       
bool isDisabled;

                       
isDisabled =
((int)entry.Properties["userAccountControl"].Value &
(int)ADS_USER_FLAG.ADS_UF_ACCOUNTDISABLE) != 0;

                       
adResult.Disabled = isDisabled;

 

                       
object lastlogon = entry.InvokeGet("LastLogin");

                       
if (lastlogon != null)

                       
{

                           
DateTime LastLogon = (DateTime)lastlogon;

                           
adResult.LastLogon = "Last Logon was at : " +
LastLogon.ToLongDateString() + " 
" + LastLogon.ToShortTimeString();

                       
}

 

 

                       
LargeInteger liAcctPwdChange = entry.Properties["pwdLastSet"].Value
as LargeInteger;

 

                       
// Convert the highorder/loworder parts of the property pulled to a
long.

                       
long dateAcctPwdChange = (((long)(liAcctPwdChange.HighPart) << 32)
+ (long)liAcctPwdChange.LowPart);

 

                       
// Convert FileTime to DateTime and get what today's date is.

                       
DateTime dtNow = DateTime.Now;

                       
DateTime dtAcctPwdChange = DateTime.FromFileTime(dateAcctPwdChange);

                        string strAcctPwdChange =
DateTime.FromFileTime(dateAcctPwdChange).ToString();

                       
string strAcctPwdExpires =
DateTime.FromFileTime(dateAcctPwdChange).ToString();

 

                       
// Calculate the difference between the date the pasword was changed,
and what day it is now and display the # of days.

                       
TimeSpan time;

                       
time = dtNow - dtAcctPwdChange;

 

                        adResult.ExpiresIn = time.Days;

                       
adResult.Valid = true;

                    }

                    );

                }

                catch
(Exception ex)

                {

                   
adResult.ErrorMessage = ex.Message;

                }

            }

            catch
(Exception e)

            {

               
adResult.ErrorMessage = e.Message;

            }

 

            return
adResult;

        }

16. That’s it, and finally I would need to build UI from the object returned. Hence I have created BuildLoginDetails(ADResult reault) method to build UI as shown below:   private string BuildLoginDetails(ADResult adResult)         {               if (!adResult.Valid)             {                 return string.Empty;             }               StringBuilder sb = new StringBuilder();               if (adResult.Disabled)             {                 sb.Append("Account Disabled");             }             else             {                 if (adResult.Locked)                 {                     sb.Append("Account Locked");                 }                   if (adResult.ExpiresIn == 0 && !adResult.Locked)                 {                     sb.Append("Password Expired: ");                     sb.Append("Change Password");                 }                 else if (!adResult.Locked)                 {                     sb.Append(String.Format("
Password expires in {0} days", adResult.ExpiresIn));                 }                   sb.Append("
" + adResult.LastLogon + "");             }               return sb.ToString();         }
  17. From the string returned from the above method,  can be displayed on UI with a simple label in the event handler as shown below:     void login_LoginError(object sender, EventArgs e)         {             System.Web.UI.WebControls.Label lblDetails = new System.Web.UI.WebControls.Label();             lblDetails.Text = this.BuildLoginDetails(this.GetLoginDetails(this.login.UserName));             this.login.Controls.Add(lblDetails);         }   18. Now, build the class library, and drop into GAC. 19. Finally, Login page should be created. Navigate to C:\Program files\Common files\Microsoft shared\web service extensions\12\layouts folder . Copy Login page and paste it as CustomLogin.aspx.. Couple of changes needed on the CustomLogin.aspx.  No one is , The aspx page should inherit from CustomLoginPage.cs which we had created earlier. <%@ Page Language="C#" Inherits="SharePoint.Authentication.Custom.ActiveDirectory.CustomLoginPage" MasterPageFile="~masterUrl/default.master"      %>   Second is, add an assembly entry to use the dll we had created. As     <%@ Assembly Name=" SharePoint.Authentication.Custom.ActiveDirectory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=12bc1ebe5d50d609"%>     in your case the PublicKeyToken will be different, make sure you copy right token from GAC or by using good old friend .Net Reflector   Third is, Importing namespaces. Add below shown entries.   <%@ Import Namespace="ActiveDs" %>   20. We have now developed CustomLogin page. Next part is customising SharePoint Application to use the Custom Login page. 21. The following steps provide modification needed to Web.config file for using ASP.NET forms authentication to use an Active Directory service membership provider.   1.Open web.config file from the path: c:\Inetpub\wwwroot\web.config and add below shown entries under section of web.config file   <connectionStrings>   <add name="ADConnectionString"    connectionString=     " LDAP://ptr.headquarters.com/DC=ptr,DC=headquarters,DC=com" /> </connectionStrings> 2. And add below shown membership provider under section      <membership defaultProvider="ADirectoryMembershipProvider">       <providers>         <add connectionStringName="ADConnectionString" name="ADirectoryMembershipProvider"          type="System.Web.Security.ActiveDirectoryMembershipProvider,  System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"          enableSearchMethods="true" attributeMapUsername="sAMAccountName"      />       </providers>     </membership> 4. From the Central Administration page of WSS 3.0, navigate to Application management. 5. Create New Intranet application from the link Create/Extend New Web Application as shown in fig below:           6. Now, Extend this Intranet application to external users by clicking on Application Management\Create new or extend web application link from the Central Administration Page. 7. The details should be provided in order to extend the intranet application is shown fig below:     8. Click OK to continue to extend web application. 9. Navigate to Application Management\Authentication Providers and select Extranet Zone and provide details as show in fig below:     10. Click Ok to continue. 11. open IIS Manager, select the Web site choosen for authentication, go to properties by Right click. And then to ASP.Net tab. Click on Edit Configuration settings and move to Authentication tab.  On this screen, modify Login.aspx to CustomLogin.aspx as show in screen shot.       by doing so, we are instructing the Membership provider to use CustomLogin.aspx. and the rest IIS.   22. From the browser, enter the URL of extranet web application, which should display the login screen as shown below:     for some reasons, user can not logon, the following UI would be displayed.     I think this article make sense to implement and delve challenges.   ufff.. thts it..! In next part, will explain how to implement change password, until then bye..