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:
- http://www.simple-talk.com/dotnet/windows-forms/configuring-forms-authentication-in-sharepoint-2007/
That being said, I did not use the following articles, even though they are related to my scenario and they looked interesting:
- http://msdn.microsoft.com/en-us/library/bb975136.aspx
- http://msdn.microsoft.com/en-us/library/bb975135.aspx
Creating the SharePoint Website
Here are the brief details on how I created my SharePoint website:
- 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:
- 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
- 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>
- 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.
- I opened SharePoint 3.0 Central Administration to convert my website into a SharePoint 2007 site:
- 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.
- 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.
- 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.
- 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.
- When that completed, I needed to restart IIS before continuing. (NOTE: I used "iisreset" from a command line.)
- 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.
- I clicked the Application Management tab, then clicked Create or extend Web application, and then clicked Create a new Web application:
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:
- I opened a command prompt.
- I changed directory to the path where my SharePoint global web.config file was located; for example:
cd C:\inetpub\wwwroot\wss\VirtualDirectories\6087
- 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
- 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:
- I opened a command prompt.
- I changed directory to the SharePoint website's path; for example:
cd C:\Inetpub\SharePointSite\wwwroot
- I changed permissions for the "bin" folder; for example:
icacls bin /grant IIS_IUSRS:r
- 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:
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()
andProcessImnMarkers()
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:
That being said, I like my solution better.