SharePoint User Profile Access Issues

Today I will feature a problem recently encountered with updating and reading user profile information when connected to SharePoint as an anonymous user.

The Task
…create a custom login page for forms based authentication that validates specific user profile information prior to allowing the user to log into SharePoint.  In this instance a user would enter in their usename and password and then click on the login button.    The button’s click event will launch a method that validates user profile information for the specified username prior to completing the actual log in process.   This is needed to make sure a user has read and agreed to the site terms of service prior to be given any access to the system.   A simple user profile field that is not editable directly by the user holds the flag that indicates if the user has agreed to the terms of service.   If the user has previously agreed to the terms then the log in process completes.  If the user has not previously agree to the terms then the terms are displayed and the user has a chance to agree and login or disagree and leave the site.

The Problem
I need to read and update user profile information even though the user has yet to log into SharePoint through forms authentication.   The initial thought was to use SPSecurity.RunWithElevatedPriviliges to provide the elevated permissions needed to read and write to the profile store.    Elevated permissions are required because anonymous users do not have any permissions on the profile store.

During testing of this solution I still continued to receive access denied errors when the exists method of the UserProfileManager object was executed.  This made no sense since the application pool account had full permissions to make modification to the user profile store.   (Note: when using RunWithElevatedPriviliges under a forms authenticated site the actual account used is the application pool account).

The Cause
With help from Lutz Roeder’s .NET Reflector utility I was able to peak into the actual SharePoint assemblies and view what was going on when I was calling the exists method on the UserProfileManager object.   I eventually tracked down the exact piece of code that was causing the access denied error.   It was in a static method called GetCurrentUserName in the UserProfileGlobal class.

Since this class is protected under Microsoft copyright I will not be reproducing the actual code but I will explain at a high level what the GetCurrentUserName method does and why it caused the access denied errors.

The GetCurrentUserName class inspects the HttpContext.Current property and if it is not null it uses it to determine the current user and validates that the user can be authenticated and then returns the username.    If the HttpContext.Current property is null then the current WindowsIdentity is used to determine the current user and then returns that username.

So back to our problem… the access denied error.    When using RunWithElevatedPriviliges impersonation takes place and the WindowsIdentity object now returns the SharePoint system account (which in our case is the application pool account).   RunWithElevatedPriviliges does not modify the HttpContext, so in our case since our user is not authenticated the HttpContext.Current.User property is set to an empty string.  This means that when the GetCurrentUserName method from the UserProfileGlobal class executes it sees the the HttpContext.Current property is not null and then attempts to validate the username which is an empty string.   This causes the method to return an access denied error.

The Workaround
Because our HttpContext.Current object is not null we keep falling into the part of the GetCurrentUserName method that tries to validate the username what happens to be an empty string because we do not have any user logged in yet.   What we really want is the HttpContext object to be null so that the method instead uses the SharePoint system account which does have full permissions over the user profile store.    If we just set HttpContext.Current = null we would cause the login page to no longer work.   This is because ASP.NET needs the HttpContext in order to properly process and render a web page.

To prevent the ASP.NET page from no longer working it is important to store the value of the current context in a variable prior to setting the current context to null so that way it can be restored when done reading or writing from the user profile store.   Below is an example.

 

HttpContext myContext = HttpContext.Current;
HttpContext.Current = null;

…do profile read / writes here …

HttpContext.Current = myContext;

Remember that the above code is run within a RunWithElevatedPriviliges code block.   It is also important to make sure you do not try to use any objects or methods that need access to the HttpContext object between the time you set the current context to null and when you restore it to the original value.

Results
So far this work around appears to be performing very well in a development environment.   If you do use this please be aware that future changes to the SharePoint API through service packs, hotfixes or other updates may cause code relying on this work around to no longer function as intended.   In other words, use at your own risk! 

0 thoughts on “SharePoint User Profile Access Issues”

Leave a Reply