Saturday, October 31, 2009

SharePoint: Form-Based Authentication with ADAM

what is ADAM?

Active Directory Application Mode is a lightweight version of Active directory. Active directory setup requires much infrastructure, investment and management. ADAM runs as non-operating system service whereas Active Directory(AD) runs as OS service. So whereas only one AD exists per OS, there may be multiple ADAM running in a single OS.

 

Why ADAM instead of AD?

ADAM and AD both uses LDAP protocol and can be used to manage user information and for authentication. First of all think that you have a custom developed application (say app1) which is already working with AD (so LDAP provider). The application App1 is used for internal purpose and you are using AD for managing internal users of you enterprise. Now you need to develop another custom application (say app2) which will work with LDAP but for external users (say you need to manage buyers of your company’s product). In that case you want to use LDAP provider but surely you don’t want to manage those external user’s information in your AD. The best solution in that case is to use ADAM as this will keep the external users (so buyers) information not in AD but in ADAM and your existing system (app1) can work with ADAM (as ADAM and AD both use LDAP).

image

Figure 1: How AD and ADAM can exists side by side.

As shown in the figure 1,internal system uses AD primarily but it can also access ADAM. Also ADAM and AD can be synchronized which provides room for better integration between AD and ADAM.

Now you may ask I could use other way to manage external users like asp.net authentication provider or custom user management with database. But if you do so then it’ll be difficult for existing application (which is using AD now) to access those external user’s information as current system just only support LDAP provider. So ADAM is best choice for those who are using AD already and need AD like system to mange users for another system but don’t want to use AD directly.

Install and Configure ADAM

Download ADAM from here and install. Once you have installed ADAM click ADAM –> “Create an ADAM Instance”. Select next in the first window. Then make sure you have selected “A unique instance” option is selected in the Setup Options step. In the Instance Name step, enter a meaningful name. In the Ports step keep the default port and click next. In the Application Directory Partition select “Yes, create an application directory partition” and put something as shown in the figure 2. Remember the partition name as we will need this later to connect to ADAM.

image

Figure 2: ADAM setup.

Here in the above image, DC means Domain Controller (which I think may be your company name), OU means Organizational Unit (which I think the department the application will be used) and CN stands for Common Name (which may be your product name).

Now click next and you’ll move to File Locations step. Click next now and you may be prompted for a security warning and select yes if you see this warning window. You are now ADAM administrators step and take the default option of “currently logged in user….” Click next and you’ll be in the Import LDIF files step. Select

image

Figure 3: Import LDIF file

Now click next until you finish.

Create an User in ADAM

Navigate to the “ADAM ADSI Edit” from ADAM under start menu. You’ll prompted for the following screen. If the screen doesn’t appear automatically then click Action –> Connect to. Make sure you have put the server name and port. The partition name should be entered in the DN field.

image

Figure 4: Connect to ADAM

Once you have connect to the ADAM right click on the node labeling your partition name (so, CN=MyProduct,OU=Management,DC=MyCompany) and click new –> Object. You’ll get the select a class window and select user from that window and click next. In this window put a name for user and finish the wizard.

After creating user you need to enable the user account (as its disabled by default) and reset the password.

1. In the properties window select the properties msDS-UserAccountDisabled and set its value to false. By default the account is disabled.

2. Set user principal name to the username. To do so set the property userPrincipalName of the user to user name.

3. Reset the password by right clicking the user and  clicking Reset Password

4. Sometimes the user authentication doesn’t work without adding the user to group/role. you can add the user to ADAM groups (Administrators, Users, Readers) which is available under CN=Roles node. To add an user to a group first get the distinguished name of the user from properties window. The property name is distinguishedName. Now move to the CN=Roles node and click on any roles/group you want the user to add. Bring the properties window of the role and find the member property and click. You’ll find a window as shown below. Here in this window you will click the Add ADAM account and paste the distinguished name.

image

Figure 5: Add an ADAM user to group/role.

Configure ADAM in SharePoint

Once you have ADAM set up you can use ADAM. Now to configure ADAM for a site you need to modify the web config file for both for that site and for the central administration site. At first you need to add a membership section under <system.web> section.

 

  <membership defaultProvider="ADAMProvider">

    <providers>

      <add name="ADAMProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider,System.Web,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ADAMProviderConnectionString"            connectionUsername="CN=srana,CN=MyProductUser,CN=MyProduct,OU=Management,DC=MyCompany" connectionPassword="srana" enableSearchMethods="true" connectionProtection="None" />

    </providers>

  </membership>

Here the connectionProtection value may be Secure or others based on your server’s configuration.

Also in the connection string section add the connection string to the ADAM as shown below:

  <connectionStrings>

    <add name="ADAMProviderConnectionString" connectionString="LDAP://servername:port/CN=MyProductUser,CN=MyProduct,OU=Management,DC=MyCompany" />

  </connectionStrings>

So you are done. But remember to add the same membership and connection string section both in the site you want the ADAM authentication and central administration. If you don’t put the configuration in the central administration web.config file then it’ll not work.

Now you need to check if the ADAM authentication works. Just go to the central administration => Application Management => Site Collection Administrators (under SharePoint site management).  Select your site from dropdown list and then put the ADAM user name in the primary or secondary site collection administrator’s box. If user is found then you are done. But if it doesn’t find the user then you need to find the error. Go to the folder like “C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\LOGS” and find the latest log. You’ll find error description there and based on the error message you can easily figure out the reason.

Few suggestions:

1. After Configuring the ADAM you may find that its not working. The user is not showing valid in the SharePoint PeopleGroup Picker. In that you can search the SharePoint log files to get the root cause. The log files exists in a folder like “C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\LOGS”.

2. Its better to create web site first then before creating any site collection configure the site for ADAM authentication. If you convert a windows authenticated site to ADAM then existing permission for windows user (as well as windows users) will no longer be in use.

3. By default the ADAM provider uses the userPrincipalName as the user name. So when user enter any username in username box (say srana) in sharepoint  site, the provider try to find any entry in the ADAM with userprincipalname srana. So when you will create any user in ADAM set the userPrincipalName to the user name.

Friday, October 30, 2009

SharePoint: Dynamically change master page

SharePoint branding is possible with either modification of default master or modification of default CSS. When we develop asp.net application we may need to change master page dynamically based on some criterion. For example in your site you have three groups of users: anonymous, authenticated normal user and site admin(s). Now based on the user group you need to change the site layout. So this requirement can easily be met with changing master page dynamically based on user’s login status.

How to change Master Page in asp.net?

SharePoint is an asp.net application developer by Microsoft. So let’s see how we can change master page in an asp.net application. If you develop an asp.net application then you can easily change the master page dynamically by changing the master page in PreInit event. You can check user’s login status in every page’s PreInit event. But if you have hundreds of pages then you don’t want to write the same code again and again in every page. So in the case the one approach might be to create a class which will inherit from Page class. Say the class is MyBasePage. In MyBasePage’s Page_PreInit event you can change the master page based on condition. The class is shown below:

 

public class MyBasePage : System.Web.UI.Page

{

    protected void Page_PreInit(object sender, EventArgs e)

    {

        if (CurrentUser.UserType == UserType.Admin)

        {

            MasterPageFile = "~/MasterPages/AdminMaster.master";

        }

        else if (CurrentUser.UserType == UserType.NormalAuthenitcated)

        {

            MasterPageFile = "~/MasterPages/NormalAuthenticatedMaster.master";

        }

        else

        {

            MasterPageFile = "~/MasterPages/AnonymouskMaster.master";

        }

    }

}

 

Now every web page can inherit from MyBasePage. To do so you need to modify web page’s code behind file to change the inheritance from System.Web.UI.Page to MyBasePage as shown below.

public partial class HomePage : MyBasePage

{

      protected void Page_Load(object sender, EventArgs e)

      {

 

      }

}

 

How to change Master Page in SharePoint?

Knowing the concept of “Change master page in PreInit event” you may think I’m done. Wait! Don’t stop reading. I have not started yet for SharePoint. SharePoint has its own pages which have installed as part of the SharePoint installation. Those pages’ code behind is complied in dll. How you are going to modify code in those pages’ code behind file? So you can’t override PreInit event as it was possible in asp.net application as you don’t have source code for SharePoint pages. Don’t give up. There’s a workaround where’s a problem. We’ll use HttpModule to hook our code in SharePoint Pages’ PreInit event. But first of all let explain in brief how Asp.net pipeline processing works.

 

Asp.net Pipeline Processing

Asp.net request processing is based on pipeline model as shown in figure 1. Each request is gone through a number of modules where each module can modify the request. Finally the request is passed to a Handler which processes the final request.

 

image

Figure 1: Asp.net Request/response pipeline.

Usually each request is handler by multiple modules and one handler. In the above request/response pipeline we don’t want to process the final request but want to modify the request to add a PreInit event handler for asp.net page.

 

Develop a HttpModule to attach PreInit event handler

To develop a custom HttpModule we need to write a class which will inherit from System.Web.IHttpModule . IHttpModule defines two methods to implement: Init and Dispose. Init method will be invoked when the module will be initialized and dispose method will be invoked when the module will be disposed.

public class DynamicMasterPageModule : IHttpModule

{

    public void Dispose()

    {

 

    }

 

    public void Init(HttpApplication context)

    {

        context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);

    }

 

    void context_PreRequestHandlerExecute(object sender, EventArgs e)

    {

        Page page = HttpContext.Current.CurrentHandler as Page;

        if (page != null)

        {

            page.PreInit += new EventHandler(page_PreInit);

        }

    }

 

    void page_PreInit(object sender, EventArgs e)

    {

 

    }

}

As shown in the above code block, In Init method I have hooked an event handler for PreRequestHandlerExecute event. The PreRequestHandlerExecute fires just before Asp.net starts executing a page. In the context_PreRequestHandlerExecute method we can get the current executing page by accessing the CurrentHandler object. If current handler is for a page, then the page variable will not be null. In case of other request (say web service request) the CurrentHandler casting to page object will return null. Now we can modify the master page in the page_PreInit method. So you are thinking it’s done. Wait! There’s more to make it working in SharePoint. Let’s take a tour on how SharePoint guides us to change master page.

 

SharePoint Master Page

SharePoint has a default master page. But if you want you can develop your own master page for SharePoint in SharePoint designer. To refer a master page from content page in SharePoint, there are four tokes available. In normal asp.net application we set the master page from content page by setting the Page.MasterPageFile property of the page to the location of the master page as shown below:

Page.MasterPageFile="path to master file";

But in SharePoint, this MasterPageFile property is used differently. It may be or may be not the location of the master page file in SharePoint. In SharePoint MasterPagefFile can take four predefined values (called tokens). Based on the token values SharePoint determine which master page to use for the page (default, custom, site collection etc). The four tokes fall into two categories: dynamic and static. Static token used by itself to define the master page location. The two static token is described below:

  • Token "~site/MasterPageName.master"

In this token the ~site refer to site relative url and MasterPageName.master is the name of the master page. So if you provide the token “~site/masterpages/MyMaster.master” and your site url is like http://sitecollection/site1 then the master page url will be http://sitecollection/site1/masterpages/MyMaster.master.

  • Token "~sitecollection/MasterPageName.master"

Here the token ~sitecollection points to the site collection url. So if you site collection url is http://sitecolleciton then the master page location will be http://sitecolection/MasterPageName.master.

Dynamic token doesn’t provide the master page location rather it points where to get the master page file location. Dynamic tokens are exact values and you can’t change it like static token. The two dynamic token are described below:

  1. Token "~masterurl/default.master"

When you’ll set the value like Page.MasterPageFile= "~masterurl/default.master", SharePoint will use the master page provided in SPWeb.MasterUrl property. So if you set master page file to this token you need to ensure that you have provided the master page location to SPWeb.MasterUrl property. FYI, the dynamic token is not changeable and you need to provide exact “~masterurl/default.master”.

  • Token "~masterurl/custom.master"

When you will use this token then SharePoint will use SPWeb.CustomMasterPageUrl property. So along with setting this token, you also need to set the SPWeb.CustomMasterPageUrl property. FYI, the dynamic token is not changeable and you need to provide exact “~masterurl/default.master”.

 

Develop Custom Master Page in SharePoint

1. Create a custom master page

For Developing SharePoint master page we can use SharePoint designer. The easiest way to create a master page is to copy an existing master page in SharePoint Designer and past it and then modify it.

2. Make the master page you developer as Custom master page

But once you have developed a master page in SharePoint it doesn’t become custom master page automatically. You can set your master page as custom one from SharePoint Designer. Open you site in SharePoint Designer and then navigate to _catalogos > Masterpage and then select the your master page. Finally right click on the master page to bring the context menu and click “Set as Custom Master page” from the context menu. To following figure shows how to set a master page as custom one from SharePoint designer.

image

Figure 2: How to set custom master page in SharePoint Designer.

3. User your custom Master Page in PreInit event.

In the following code block I have read the page number from web.cofing and if I find page no to 1 then I use the custom1.master, for page number 2 I use custom2.master. Either I use default master page. In real life scenario you will change the master page based on some other criterion.

void page_PreInit(object sender, EventArgs e)

    {

        Page page = sender as Page;

        string pageNo = ConfigurationManager.AppSettings["MasterPageNo"];

 

 

        if (page != null)

        {

            if (pageNo.Equals("1"))

            {

                page.MasterPageFile = "~masterurl/custom.master";

                if (SPContext.Current != null)

                {

                    SPContext.Current.Web.CustomMasterUrl = "/_catalogs/masterpage/custom1.master";

                }

            }

            else if (pageNo.Equals("2"))

            {

                page.MasterPageFile = "~masterurl/custom.master";

                if (SPContext.Current != null)

                {

                    SPContext.Current.Web.CustomMasterUrl = "/_catalogs/masterpage/custom2.master";

                }

 

            }

            else

            {

                page.MasterPageFile = "~masterurl/default.master";

                if (SPContext.Current != null)

                {

                    SPContext.Current.Web.MasterUrl = "/_catalogs/masterpage/default.master";

                }

            }

 

        }

 

In the code block above if you can just comment out the else section. I have just used this to show to you can also set the default master page. You can skip this code block as you don’t need to set the default master page as it’s already set by default.

Integrate the custom module with SharePoint site

Once you have developed the custom module you need to use it in SharePoint site. You can do so by adding an entry in the web.config file. Open the web.config file of the SharePoint web application (which is normally in c:\inetpub\wwwroot\wss\VirtualDirectories\Port) and add your http module name in the <httpModules> </httpModules> block.

Tuesday, October 27, 2009

SharePoint List and ListTemplate

If you ever need to find the list template associate with a list you can do so by accessing the list property “TemplateFeatureId”. This property is of type guid and if a list doesn’t have the list template then value is Guid.Empty.

How to find a list template’s id?

Say you have a list template “CustomerListTemplate” and you need to find out the list template id. You can do so by getting the current SPWeb’s list template collection. Then you can loop through the list templates to find the “CustomerListTemplate”. This is shown in the following code snippet.

            //get all list templates in current site.

            SPListTemplateCollection listTemplates = SPContext.Current.Site.GetCustomListTemplates(SPContext.Current.Web);

            Guid myTemplateId = Guid.Empty;

 

            //loop throug all templates and find your template id

            foreach (SPListTemplate template in listTemplates)

            {

            //if template is hidden or unique then skip

            if (template.Unique || template.Hidden)

                continue;

 

                if (template.Name.Equals("CustomerListTemplate"))

                    myTemplateId = template.FeatureId;

                break;

            }

I have a list template, how I’ll find the lists created by this template?

At first you need to know the template’s ID which is described in section “How to find a list template’s id?”. Once you have your list template’s id you can loop through all lists in your site and compare the list’s “TemplateFeatureId” with the list template id you have for your list template.

foreach (SPList list in SPContext.Current.Web.Lists)

            {

                if (list.TemplateFeatureId == myListTemplateId)

                {

                    //this list is created by your template

                }

            }

Friday, October 16, 2009

ClickOnce manifest signing

Introducing Certification

When you get a Software Publisher Certificate (SPC) from a third party CA (say verisign)that is authorized by Microsoft to issue certificate, you actually get two files: a pvk and spc or cer file.

.pvk file which contains the private key information.

.spc or .cer file contains public key information.

In my discussion later I’ll consider having pvk and spc file for signing clickonce application.

To use this certificate you need to generate a Personal Information Exchange file (pfx). You can generate a pfx file from pvk and spc. For details information on how to generate pfx file follow here.

 

Visual Studio Support for Clickonce Manifest signing

Now I’ll consider that you have pfx file and you are ready to sign the clickonce manifest. To sign the clickonce manifest open the properties window of the project and then select signing tab. Then select the “Sign the ClickOnce maifests” check box and you’ll be prompted for selecting the pfx file. Open the pfx file and you’ll be prompted for password. After putting your password set the timestamp server url to http://timestamp.verisign.com/scripts/timstamp.dll. The following figure shows the process in a whole.

image

Figure: Sign clickonce manifests.

 

Separate Signing from Developement

You are done! But this is not not I”m writing this post. The problem is in this process that if you have automated build management system (say using NAnt) then during build you’ll be prompted for password and you don’t want this. You actually want to separate the signing from development. You only need to bother about signing when you deploy on production server. if put directly select pfx file from Visual Studio then every developers will be prompted for the password during build (at least once, then VS will cache it). So you need some way to sign the Clickonce manifest, say in post build event. But Publish doesn’t have any event. So you can’t write post build event in Visual Studio project. So one way is to write batch script and run the script after production deployment build.

ClickOnce Manifest Internals

But before diving deep into the signing procedure let’s discuss about how actually ClickOnce manifest works. If you publish your clickonce app in E:\Clickonce folder then the root folder will contains a file named YOURAPPNAME.applicaiton. Then there will be a folder “Application Files\YOURAPPNAME_VERSION” and there’ll be sever files.

image

Figure: ClickOnce deployment folder structure.

Clickone has two types of manifests: application manifest and deployment manifest. Deployment manifest has one of the three extensions:

1) .applicaiton for executable applicaiton

2) .vsto for office applicaiton

3) .xbap for browser hosted wpf applicaiton

Application manifest has only one extension and that is .manifest.

 

Now if you want to manually sign clickonce manifest then you need to sign both application and deployment manifests. Clickonce manifests contains hashes of all/related files. The following figure shows how application and deployment manifest share hash value.

image

Figure: Application and Deployment manifest hash value calculation.

Sign application and deployment manifests

So if you sign clickonce manifest then you need to sign the application manifest first as deployment manifest depends on application manifest file. So lets’ start with then signing process:

1. Signthe application manifest:

If your application manifest has a .manifest extension and you’ll get the application manifest file in “Applicaiton Files\Application_VERSION” folder. The following command sign a manifest file with pfx.

mage -update MyApplicaiton.exe.manifest -certfile pfxfile.pfx -Password pfxfilepassword

The mage command will be available if you have .net SDK installed.

2. Sign the deployment manifest:

mage -update MyAppName.application -appmanifest MYAppName.exe.manifest -certfile pfxfile.pfx -Password pfxFilePassword

So you are done. But there few points to remember. If your application’s dlls are renamed with deploy then you need to remove this option either the sign will not work. You can remove the .deploy option from visual studio property window > Publish Tab > Options.

image

Figure: Remove .deploy extension from Visual Studio.

Now you can write a batch file which you can run after build and the batch file will sign the clickonce manifests and your manifest signing is totally different from your development.

Wednesday, October 14, 2009

Make Standard use of Cookie with Cookie Compact Privacy Policy (P3P)

Few days ago our QA team was testing a web site and found a bug. So they informed me that my page is not working properly. So I went over their desk and found that my page is not setting cookie on the QA's PC (which is Windows Vista). So I thought it might be some error in my code. So I tried to figure it out but I found the same page is setting cookie on other machines. Then I thought it might be some security issues and then I had come to know about the Cookie Compact Privacy Policy.

 

What is Cookie Compact Policy?

Cookie Compact Privacy Policy (also know as P3P) is a way to tell the browser about web sites' cookie policy. The policy describes what type of data web site is keeping in Cookie, how its using these data, how much time its keeping data in cookie etc. The cookie compact policy looks like:

IDC DSP COR CURa ADMa  OUR IND PHY ONL COM STA

Here each group of three character word has explanation that can be found here. To set this cookie policy there are few options you have. You can set the cookie policy from your asp.net page by adding the following tag in your page's header section (Usually master page).

<meta http-equiv="P3P" content='CP="IDC DSP COR CURa ADMa  OUR IND PHY ONL COM STA"'>

So all you need to now to define cookie policy for your company. You can define the cookie policy be going through the definition from here. Be sure your cookie policy reflects your real purpose. Say you collect personal data but your cookie policy doesn't specify that then you are anyway violating cookie policy and for which you may be charged (but I'm not sure how). You can find few Compact Policy builders. With these tools you can build P3P policy.

So now you can think if you ever need to concern about the P3P policy. If you think that users who will visit your site will have under heavy security (usually enterprise users have so) then there's a probability that your cookie without P3P Compact Policy will not work. So if your developing your site for enterprise/business users who will use the site for business applications then you should use Cookie Compact Policy. For web sites targeted for public users you may not need to care for Cookie policy. But it's always better to add P3P header to ensure you cookie works under browser protected mode.

 

How to find Cookie Compact Policy Related settings in IE?

If you go to Internet Explorer > Tools > Internet Options > Privacy, then you will find option on how IE will handle cookie as shown below:

image

If from any site cookie is blocked for any reason (most probably due to absence of P3P header) you will find the following icon in IE status bar.

image

Double clicking on the red icon you will get full report of blocked cookie. Clicking on the red eye you will find a report as shown below:

image

So make best and proper use of cookie in your site to make most compatibility with browsers.

Tuesday, October 13, 2009

Application.Current.Shutdown Doesn't close Immediately

When you call Application.Current.Shutdown you may expect to shutdown the  application immediately. But it doesn't. Application.Shutdown is async in nature. So it may take few moments to shut down application. So not to execute code after shutdown call, you need to put a return statement. This return statement will make sure that the code returns. You can also use Thread.Sleep so that the shutdown method gets time to close the application. But you can't grantee how much time you will keep the thread sleep.

The issue is already reported  here.