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;
- SPListItem.DoesUserHavePermissions(SPUser user, SPBasePermissions permissionMask)
- SPList.DoesUserHavePermissions(SPUser user, SPBasePermissions permissionMask)
- 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.

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;

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; }
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;
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