My SharePoint 2007 Custom Membership Provider Adventure

Sometime last year I wanted to set up a SharePoint 2007 website for my family members to exchange information. That being said, I was using a custom membership provider, and I ran into a few issues while I was setting things up. I had kept detailed notes while I was configuring my server and troubleshooting the problems that I encountered, and with that in mind, I thought that I would share my experiences. ;-]


Getting Started

Specifying My Environment

My web server is only an older 32-bit Windows Server 2008 computer, so I couldn't install SharePoint 2010 (which is 64-bit only) and I had to install SharePoint 2007. Taking that into account, there were a few additional considerations that I had for my environment:

  • I wanted to use Forms-Based Authentication (FBA). Even though I run my own active directory domain, I avoid giving out physical accounts if I don't have to, so FBA seemed like a great idea.
  • I didn't want to use the built-in ASP.NET membership and roles provider. This is for two reasons:
    • I was already using the built-in ASP.NET membership provider on other websites, and I didn't feel like researching whether I should share the membership database between SharePoint and my other websites, or if I should set up unique membership databases.
    • If you've been reading my previous blogs and you think that I'd be content with using a built-in provider, then you haven't been paying attention. Usually I find myself wanting to do things the hard way, and other times I simply want to write code, but either way I decided to use the sample read-only XML membership and role providers that I documented in the following article:
  • I decided that I could use FBA over HTTP, and therefore I didn't worry about setting up SSL. (I run my own certificate server, so I could have issued myself a certificate and given the root CA certificate to everyone; but this wasn't necessary, so I didn't bother with it.)

Researching My Scenario

With my specific considerations in mind, I took a look at the following article to get started:

That being said, I did not use the following articles, even though they are related to my scenario and they looked interesting:


Creating the SharePoint Website

Here are the brief details on how I created my SharePoint website:

  1. I followed the steps in the following walkthrough in order to create and register the read-only XML membership and role providers with IIS 7:
  2. I created the following physical paths for my website:
    • Website root folder: C:\Inetpub\SharePointSite\wwwroot
    • Application data folder: C:\Inetpub\SharePointSite\wwwroot\App_Data
  3. I created the following user/role XML file for my website:
    • I created an XML file in the location: C:\Inetpub\SharePointSite\wwwroot\App_Data\MyUsers.xml
    • I added the following XML to the file:
      <Users>
         <User>
            <UserName>Alice</UserName>
            <Password>P@ssw0rd</Password>
            <EMail>alice@contoso.com</EMail>
            <Roles>Admins</Roles>
         </User>
         <User>
            <UserName>Bob</UserName>
            <Password>P@ssw0rd</Password>
            <EMail>bob@contoso.com</EMail>
            <Roles>Authors</Roles>
         </User>
      </Users>
  4. I opened the Internet Information Services (IIS) Manager and created a new website; I used the C:\Inetpub\SharePointSite\wwwroot folder for the home directory.
  5. I opened SharePoint 3.0 Central Administration to convert my website into a SharePoint 2007 site:
    1. I clicked the Application Management tab, then clicked Create or extend Web application, and then clicked Create a new Web application:
      • In my case I chose Use an existing IIS web site because I had already created the website that I wanted to use.
      • I chose Create new application pool, I used "Network Service" for the identification, and then I specified all of the requisite database information.
    2. When that completed, I clicked the Application Management tab, and then clicked Create site collection:
      • I specified all options, and I used a valid Active Directory account as the administrator for now.
    3. Once the site was created, I modified the web.config file for the website and the SharePoint Central Administration web.config file. (See the following notes for the details.) Note: The SharePoint Central Administration website needs to know the information about your membership provider in order to add administrators.
    4. After that, I clicked the Application Management tab, and then clicked Authentication Providers:
      • I verified that I was using the correct "Web Application" in the drop-down menu.
      • I clicked on the Default zone.
      • I set the Authentication Type to Forms.
      • I specified the appropriate Membership provider name and Role manager name.
    5. When that completed, I needed to restart IIS before continuing. (NOTE: I used "iisreset" from a command line.)
    6. After IIS had restarted, I clicked the Application Management tab, and then clicked Site collection administrators:
      • I added a user (like Alice or Bob) from the membership provider.

Web.Config Entries

There are a few additions that you have to make to your website's web.config file, as well as the SharePoint Central Administration web.config file for SharePoint 2007:

  • Here's the XML that you need to add to the <system.web> section of your website's web.config; in my example that file would be located at "C:\Inetpub\SharePointSite\wwwroot\web.config":
    <!-- added on 05/31/2011 -->
    <membership defaultProvider="ReadOnlyXmlMembershipProvider">
      <providers>
        <add name="ReadOnlyXmlMembershipProvider"
          type="ReadOnlyXmlMembershipProvider, ReadOnlyXmlMembershipProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=426f62526f636b73"
          description="Read-only XML membership provider"
          xmlFileName="~/App_Data/MyUsers.xml" />
      </providers>
    </membership>
    <roleManager enabled="true" defaultProvider="ReadOnlyXmlRoleProvider">
      <providers>
        <add name="ReadOnlyXmlRoleProvider"
          type="ReadOnlyXmlRoleProvider, ReadOnlyXmlRoleProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=426f62526f636b73"
          description="Read-only XML role provider"
          xmlFileName="~/App_Data/MyUsers.xml" />
      </providers>
    </roleManager>
    <!--/added on 05/31/2011 -->
  • Here's the XML that you need to add to the <system.web> section of your SharePoint Central Administration web.config file; on my server that file is located at "C:\inetpub\wwwroot\wss\VirtualDirectories\6087\web.config":
    <!-- added on 05/31/2011 -->
    <membership defaultProvider="ReadOnlyXmlMembershipProvider">
      <providers>
        <add name="ReadOnlyXmlMembershipProvider"
          type="ReadOnlyXmlMembershipProvider, ReadOnlyXmlMembershipProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=426f62526f636b73"
          description="Read-only XML membership provider"
          xmlFileName="~/App_Data/MyUsers.xml" />
      </providers>
    </membership>
    <!--/added on 05/31/2011 -->

IMPORTANT!!!

SharePoint Central Administration needs to be able to find the MyUsers.xml file, so I created an App_Data folder under physical path of the SharePoint Central Administration website, and I added a symbolic link in that folder that pointed to the physical MyUsers.xml file. Here's how I did that:

  1. I opened a command prompt.
  2. I changed directory to the path where my SharePoint global web.config file was located; for example:
    cd C:\inetpub\wwwroot\wss\VirtualDirectories\6087
  3. I created a symbolic link to the physical path of the XML file; for example:
    mklink MyUsers.xml C:\Inetpub\SharePointSite\wwwroot\App_Data\MyUsers.xml
  4. I closed the command prompt.

Note: I could have copied the XML file, but I preferred to use the symbolic link instead of having to manage two copies of the file.

Optional People Picker Settings

If you were installing a membership provider that can perform lookups, you could add an additional entry to your SharePoint Central Administration web.config file:

<PeoplePickerWildcards>
  <clear />
  <add key="AspNetSqlMembershipProvider" value="%" />
  <!-- added on 05/31/2011 -->
  <add key="ReadOnlyXmlMembershipProvider" value="%" />
  <!--/added on 05/31/2011 -->
</PeoplePickerWildcards>

Problems that I Encountered

Okay - I admit that I everything that I did so far was probably making things harder that they needed to be, but I love a good challenge. ;-]

That being said, I ran into some problems that I thought would be worth mentioning, just in case someone else ran into them.

HTTP 403 Errors

When browsing to my SharePoint website, I received several HTTP 403 errors. I used Process Monitor to troubleshoot the problem, and I discovered that IUSR could not access the "bin" folder in my website. (I'm still not quite sure why it was trying.) To resolve these errors, I used the following steps:

  1. I opened a command prompt.
  2. I changed directory to the SharePoint website's path; for example:
    cd C:\Inetpub\SharePointSite\wwwroot
  3. I changed permissions for the "bin" folder; for example:
    icacls bin /grant IIS_IUSRS:r
  4. I closed the command prompt.

In my situation the problem was for IUSR, but if you are using a different anonymous identity or your application pool is running as a unique identity then it might be a different user. In any case, Process Monitor will let you know who needs permissions.

Later I discovered the following blog post by John Powell:

http://blogs.msdn.com/b/johnwpowell/archive/2008/05/23/sharepoint-intermittent-403-forbidden-errors.aspx

In that blog, John suggests adding the following permissions for the "bin" folder:

icacls bin /grant users:r

I'm not sure if that's necessary, but it's worth pointing out.

HTTP 404.8 Errors

When browsing to my SharePoint website, I received several HTTP 404.8 errors. Those errors mean that the built-in IIS 7 Request Filtering feature was blocking something, so I did the following:

  • I opened my website's web.config file; on my server that file was located at "C:\Inetpub\SharePointSite\wwwroot\web.config":
  • I added the following XML before the closing </configuration> tag:
    <system.webServer>
      <!-- added on 05/31/2011 -->
      <security>
        <requestFiltering>
          <hiddenSegments>
           <clear />
           <add segment="web.config" />
           <add segment="bin" />
           <add segment="App_code" />
           <add segment="App_GlobalResources" />
           <add segment="App_LocalResources" />
           <add segment="App_WebReferences" />
           <add segment="App_Data" />
           <add segment="App_Browsers" />       
          </hiddenSegments>
        </requestFiltering>
      </security>
    <!--/added on 05/31/2011 -->
    </system.webServer>
  • I saved and closed the web.config file.

Note: This removes all of the hidden segments from the global IIS 7 Request Filtering settings, which may be overkill. I have a lot of custom global request filtering settings, and I didn't want to go through each individual setting to see which setting was blocking files that I needed, so I used settings for my website that cleared the inherited request filtering settings and added the default settings.

Annoying Message: "The Web site wants to run the following add-on: 'Name ActiveX Control'"

When browsing to my SharePoint website, the information bar in Internet Explorer kept prompting me with the following message:

The Web site wants to run the following add-on: 'Name ActiveX Control' from 'Microsoft Corporation'. If you trust the Web site and the add-on and want to allow it to run, click here...

This message was highly frustrating, so I did some digging around the Internet and discovered that I could hack the INIT.JS file for SharePoint to suppress this message. Here's how I did that:

  • I opened my server's INIT.JS file; on my server that file was located at "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\TEMPLATE\LAYOUTS\1033\INIT.JS".
  • I located the ProcessImn() and ProcessImnMarkers() functions, and I remarked out the contents. Here's what this looked like when I was done:
    function ProcessImn()
    {
    // if (EnsureIMNControl() && IMNControlObj.PresenceEnabled)
    // {
    // imnElems=document.getElementsByName("imnmark");
    // imnElemsCount=imnElems.length;
    // ProcessImnMarkers();
    // }
    }
    function ProcessImnMarkers()
    {
    // for (i=0;i<imnMarkerBatchSize;++i)
    // {
    // if (imnCount==imnElemsCount)
    // return;
    // IMNRC(imnElems[imnCount].sip,imnElems[imnCount]);
    // imnCount++;
    // }
    // setTimeout("ProcessImnMarkers()",imnMarkerBatchDelay);
    }
  • I saved and closed the INIT.JS file.

I should note that this solution is unsupported; and a few months I hacked my INIT.JS file, Microsoft published the following Knowledge Base article with a couple of different methods:

KB 931509: Message in the Information bar in Internet Explorer 7 when you browse to a Windows SharePoint Services 3.0 site or to a SharePoint Server 2007 site: "The Web site wants to run the following add-on: 'Name ActiveX Control'"

That being said, I like my solution better. ;-]

No Comments