
Setting up different Authentication models in WSS 3.0 - Part 1

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..