SharePoint: Checking Arbitrary User Permissions and why SPListItem.DoesUserHavePermissions is a Big Fat Dirty Liar


To clarify, the object model method isn’t a big fat dirty liar, but the documentation certainly is.

This blog post is about checking permissions of arbitrary users against SharePoint objects which expose the ISecurableObject (this interface is marked obsolete in 2010 in favour of the new SPSecurableObject class) interface using the overloaded DoesUserHavePermissions method of;

  1. SPListItem.DoesUserHavePermissions(SPUser user, SPBasePermissions permissionMask)
  2. SPList.DoesUserHavePermissions(SPUser user, SPBasePermissions permissionMask)
  3. SPWeb.DoesUserHavePermissions(string login, SPBasePermissions permissionMask)

The MSDN documentation for the SPWeb and SPList methods indicates that an UnauthorizedAccessException will be thrown if the user calling the method (not the user you are checking permission for) does not have the EnumeratePermissions right. The documentation for the SPListItem method makes no such assertion.

In testing however, calling the SPListItem method as a user who doesn’t have the EnumeratePermissions right does cause the UnauthorizedAccessException;

UnauthorizedAccessException
at Microsoft.SharePoint.Utilities.SPUtility.GetPermissions(...)
at Microsoft.SharePoint.Utilities.SPUtility.DoesUserHavePermissions(...)
at Microsoft.SharePoint.SPListItem.DoesUserHavePermissions(...)
.

Given a stock SharePoint security model, this means that only Owner type users can call this method, you might think that running the code elevated would work, however it doesn’t, the exception is still thrown

SPSecurity.RunWithElevatedPrivileges(()=> {
	chk = item.DoesUserHavePermissions(someUser, SPBasePermissions.ApproveItems);
	chk = list.DoesUserHavePermissions(someUser, SPBasePermissions.ApproveItems);
	chk = web.DoesUserHavePermissions(someUser, SPBasePermissions.ApproveItems);
});

The reason for this (after perusing the code with JustDecompile) is that the securable objects EffectiveBasePermissions are checked to see if it includes the EnumeratePermissions right, and if not the exception is thrown.

Note that all calls to the abritrary user variation of the DoesUserHavePermissions(…) methods end up calling the SPUtility.GetPermissions(SPUserToken token, ISecurableObject securableObject) method.

using effectivebasepermissions

using effectivebasepermissions

What this means is that you have to get a reference to the securable object you want to check permissions against, under an elevated security context, and then use that elevated reference to call the DoesUserHavePermissions() method, as shown below which creates an elevated SPWeb as example;

using (var esite = new SPSite(SPContext.Current.Site.ID, SPContext.Current.Site.SystemAccount.UserToken))
using (var eweb = esite.OpenWeb(SPContext.Current.Web.ID))
{
	chk = eweb.DoesUserHavePermissions(someUserLoginName, SPBasePermissions.ApproveItems);
}

Looking at the code in the SPUtility.GetPermissions(SPUserToken token, ISecurableObject securableObject) method, you might notice that the EnumeratePermissions right check is skipped if the arbitrary user to check permissions for, and the user doing the actual DoesUserHavePermissions() call, are the same, see below;

checking users

checking users

This is not the case, the permission check is always done, because the if statement shown above always returns false, a UserToken is an internal class in Microsoft.SharePoint.dll, which internally holds a byte aray contain the user token data, therefore what is being compared here is class instance references.

Since the UserToken class has a CompareUser(SPUserToken userTokenCheck) method, I’d have thought that this should be the method to call to check the equality of UserToken classes, as this method compares the internal byte array for equality.

So with this knowledge in mind it’s possible to write a generic wrapper method which allows you to check permissions for arbitrary users against ISecurableObject types, by using the SYSTEM account security token to create elevated securable objects to call the DoesUserHavePermissions(…) method against.

public static bool CheckPermissions(SPUser user, ISecurableObject securableObject, SPBasePermissions perms)
{
	var ret = false;

	SPWeb soWeb;
	var soListId = Guid.Empty;
	var soListItemId = 0;
	if (securableObject as SPList == null)
	{
		if (securableObject as SPListItem == null)
		{
			if (securableObject as SPWeb == null)
				throw new ArgumentException("securableObject must be an SPWeb, SPList or SPListItem", "securableObject");
			soWeb = (SPWeb)securableObject;
		}
		else
		{
			var li = (SPListItem)securableObject;
			var pl = li.ParentList;
			soWeb = pl.ParentWeb;
			soListId = pl.ID;
			soListItemId = li.ID;
		}
	}
	else
	{
		var pl = (SPList)securableObject;
		soWeb = pl.ParentWeb;
		soListId = pl.ID;
	}
	var soSite = soWeb.Site;

	using (var esite = new SPSite(soSite.ID, SPContext.Current.Site.SystemAccount.UserToken))
	using (var eweb = esite.OpenWeb(soWeb.ID))
	{
		if (securableObject is SPListItem)
		{
			var l = eweb.Lists[soListId];
			var li = l.GetItemById(soListItemId);
			ret = li.DoesUserHavePermissions(user, perms);
		}
		else if (securableObject is SPList)
		{
			var l = eweb.Lists[soListId];
			ret = l.DoesUserHavePermissions(user, perms);
		}
		else if (securableObject is SPWeb)
		{
			ret = eweb.DoesUserHavePermissions(user.LoginName, perms);
		}
	}
	return ret;
}
About these ads

About Phil Harding

SharePoint Consultant, Developer, Father, Husband and Climber.

3 Responses to “SharePoint: Checking Arbitrary User Permissions and why SPListItem.DoesUserHavePermissions is a Big Fat Dirty Liar”

  1. Very nice work !

    I’m facing a similar problem but with Anonymous users…
    “SPContext.Current.Site.SystemAccount” throws an Exception so I used “SPSecurity.RunWithElevatedPrivileges” and check
    if(eweb.AllowAnonymousAccess
    && (l.AnonymousPermMask64 & SPBasePermissions.ViewListItems) == SPBasePermissions.ViewListItems
    && li.ModerationInformation.Status == SPModerationStatusType.Approved)
    return true;

  2. Hey great blog!

    I had the same problem trying to check if a user already has permission before continuing to a registration page, however in the end I found it much simpler to just to a boolean check against EffectiveBasePermissions property like so:

    // First check if we have permissions already, if so just redirect to home
    if (SPContext.Current.Web.CurrentUser != null)
    {
    if ((SPContext.Current.Web.EffectiveBasePermissions & SPBasePermissions.ViewPages) == SPBasePermissions.ViewPages)
    SPUtility.Redirect(SPContext.Current.Web.Url, SPRedirectFlags.UseSource, Context);
    }

    Not sure if that would work equally for you, but someone else might read this blog with my simpler requirement find that useful…

    Martin

Trackbacks/Pingbacks

  1. SharePoint: Checking Arbitrary User Permissions and why … | ARB Security Solutions - March 8, 2012

    [...] Post From SharePoint Security – Google Blog Search: Given a stock SharePoint security model, this means that only Owner type users can call this [...]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 598 other followers

%d bloggers like this: