I've moved my blog to!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Tuesday, October 16, 2007

Enumerate Effective Base Permissions

I created this command because I was trying to debug an issue I was having. If you go through the central admin and remove a site permission for a web app (central admin > app management > user permissions for web applications) certain functionality that should work regardless of what setting you’ve removed no longer functions. For example – I removed “Apply Themes and Borders” for my intranet app:

I then went to site settings for one of my site collections and clicked the “Related Links Scope Settings” under Site Administration. The result is that I get an access denied error. The reason is that the code for this feature is checking whether the user's Effective Base Permissions match the FullMask permission:

“0x7fffffffffffffffL” corresponds to the SPEffectiveBasePermissions.FullMask permission. In my case my account is a site collection administrator and site owner so I should always have full control over the site but because I’ve denied the apply themes and borders permission the permissions that come back for my user are as follows:

C:\>stsadm -o gl-enumeffectivebaseperms -url "http://intranet/hr"

ViewListItems, AddListItems, EditListItems, DeleteListItems, ApproveItems, OpenItems, ViewVersions, DeleteVersions, CancelCheckout, ManagePersonalViews, ManageLists, ViewFormPages, Open, ViewPages, AddAndCustomizePages, ApplyStyleSheets, ViewUsageData, CreateSSCSite, ManageSubwebs, CreateGroups, ManagePermissions, BrowseDirectories, BrowseUserInfo, AddDelPrivateWebParts, UpdatePersonalWebParts, ManageWeb, UseClientIntegration, UseRemoteAPIs, ManageAlerts, CreateAlerts, EditMyUserInfo, EnumeratePermissions
If I invert to see what permissions I’m missing I get the following:
C:\>stsadm -o gl-enumeffectivebaseperms -url "http://intranet/hr" -invert

ApplyThemeAndBorder, FullMask

The gl-enumeffectivebaseperms command is something I threw together to help debug this particular issue. If I go back to the central admin and add the apply themes and borders permission and run the commands above again then I’ll get back that my account now has everything but the FullMask associated with it and I still cannot get into the related links scope settings.

It seems like if I've got everything then the FullMask should kick in - unfortunately not. Fortunately I had another command in my toolkit which allowed me to solve the problem. I had long ago created the gl-enableuserpermissionforwebapp command - all I had to do was to run this command passing in the FullMask parameter:

C:\>stsadm -o gl-enableuserpermissionforwebapp -fullmask -url http://webapp

After running this I can now get into the pages. This is basically all the above command is doing:

   1: SPWebApplication wa = SPWebApplication.Lookup(new Uri(url)); 
   2: wa.RightsMask = wa.RightsMask | SPBasePermissions.FullMask;
   3: wa.Update() 

When reassigning the permissions via the browser it's not considering the case in which all items are selected and therefore the FullMask permission should be added back. There's no way to fix this via the browser but this custom command that I’d created some time ago does the trick.

This smells very much like a bug to me – the code should not be relying on that FullMask or the check should at least consider whether I have access to all permissions that are available and not whether I have all permissions. Because of this issue I'm unable to get into certain parts of the site collection administration without temporarily changing the user rights settings. The only other item I've come across is the site collection policies which has the same issue.

I'm not sure if there are others beyond these two - reflector won’t let me search for the 0x7ff…L string so I’m not sure of the scope of the problem. If anyone out there knows of something that I'm missing I'd love to hear it.

I'm not really sure how useful this command will be to others out there - I originally was just going to delete it once I was done with it but then changed my mind last minute - mainly because I know others must have bumped up against this same issue so hopefully this post, if not the command itself, will save someone some time.

The code is pretty simple - getting the permissions for the current user is real easy - just call the EffectiveBasePermissions property on an SPWeb object. To get the same for a user other than the one logged in is a bit more tricky. I had to use some reflection to call SPUtility.GetPermissions() method - it's the only method that I could find that would allow me to pass in a login (of course I had to call another internal method in order to get the SPUserToken object to pass into this method). If anyone knows of a simpler way, again, please let me know.

   1: public override int Run(string command, StringDictionary keyValues, out string output)
   2: {
   3:  output = string.Empty;
   5:  InitParameters(keyValues);
   7:  string url = Params["url"].Value;
   8:  if (url != null)
   9:   url = url.TrimEnd('/');
  11:  SPBasePermissions perms;
  13:  using (SPSite site = new SPSite(url))
  14:  using (SPWeb web = site.AllWebs[Utilities.GetServerRelUrlFromFullUrl(url)])
  15:  {
  16:   if (Params["user"].UserTypedIn)
  17:   {
  18:    PropertyInfo requestProp = web.GetType().GetProperty("Request",
  19:             BindingFlags.NonPublic |
  20:             BindingFlags.Instance |
  21:             BindingFlags.InvokeMethod |
  22:             BindingFlags.GetProperty);
  23:    object request = requestProp.GetValue(web, null);
  25:    MethodInfo getUserToken =
  26:     request.GetType().GetMethod("GetUserToken",
  27:            BindingFlags.NonPublic | BindingFlags.Public |
  28:            BindingFlags.Instance | BindingFlags.InvokeMethod);
  30:    try
  31:    {
  32:     SPUserToken token = new SPUserToken((byte[])getUserToken.Invoke(request, new object[] { web.Url, Params["user"].Value }));
  34:     MethodInfo getPermissions =
  35:      typeof(SPUtility).GetMethod("GetPermissions",
  36:              BindingFlags.NonPublic |
  37:              BindingFlags.Public |
  38:              BindingFlags.Instance |
  39:              BindingFlags.InvokeMethod |
  40:              BindingFlags.Static);
  42:     perms = (SPBasePermissions)getPermissions.Invoke(null, new object[] { token, web });
  43:    }
  44:    catch (TargetInvocationException ex)
  45:    {
  46:     throw ex.InnerException;
  47:    }
  48:   }
  49:   else
  50:   {
  51:    perms = web.EffectiveBasePermissions;
  52:   }
  53:  }
  55:  if (!Params["invert"].UserTypedIn)
  56:   output += perms.ToString();
  57:  else
  58:  {
  59:   List<string> permsInverted = new List<string>();
  61:   foreach (SPBasePermissions perm in Enum.GetValues(typeof(SPBasePermissions)))
  62:   {
  63:    if ((perms & perm) != perm)
  64:    {
  65:     permsInverted.Add(perm.ToString());
  66:    }
  67:   }
  68:   output += string.Join(", ", permsInverted.ToArray());
  69:  }
  70:  if (output == string.Empty)
  71:  {
  72:   output += "No permissions were found.";
  73:  }
  75:  return 1;
  76: }
The syntax of the command can be seen below:
C:\>stsadm -help gl-enumeffectivebaseperms

stsadm -o gl-enumeffectivebaseperms

Lists the effective base permissions for a user.

        -url <web url>
        [-user <DOMAIN\name>]
        [-invert (shows what base permissions the user is missing)]
Here's an example of how to return the effective base permissions for the currently logged in user:
stsadm –o gl-enumeffectivebaseperms -url "http://intranet/hr"
The results of running this command are shown above.


Joel Oleson said...

Very cool. Just linked to this from my recent post on permissions and roles.

I've also given support a heads up.

Anonymous said...

Yep - we discovered that issue a few months back. It should be officially bugged in the PSS database

Anonymous said...

Yes - we ran into this same issue a couple months ago. It is offically bugged in the PSS database.

Gianbattista said...

Great! I wait for the stsadm extension zip update :)

Gary Lapointe said...

I fixed a minor bug which resulted in the display sometimes coming back as the numerical mask rather than the actual names of the permissions.

Gianbattista said...

I've downloaded the stsadm extension zip but I haven't found this new command. Is it normal?

Gary Lapointe said...

Sorry for not getting back to you sooner (had an issue where I wasn't getting any comments). Normally I try to post the code before adding the post but sometimes my script that does the upload fails for one reason or another and I don't always pay close enough attention to that. I usually detect the problem pretty quickly though so if it happens again just check back shortly and I'll have it up there. Sorry for the confusion.

Kathy Wright said...

I am having an issue accessing Related Links Scope and Site Collection Polices. I am using the Site Collection administration which also has full control account.

These links work fine in our other web application with client integration turned on. But this web application has client integration turned off.

When I run the -enumeffectivebaseperms on the web application I get Full Mask

When I use the -invert flag I get UseClientIntegration, FullMask

Any ideas how to resolve this?

Gary Lapointe said...

Make sure you are running at least the August Cumulative Update - there's a fix in that update (best to install SP2 if you can).

Konami2k said...

Hi Gary,

I have downloaded and installed a new wss server hotfix from here :
and got a message "Ambigous match found." when i execute this command

It seems that the following part doesn't work anymore with this hotfix ...

MethodInfo getPermissions =
BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.InvokeMethod |

Do you have any idea to fix this ? Thanks

Gary Lapointe said...

Unfortunately they've changed the signature of that method so my code is breaking. I'm not currently at a point where I can deploy the update myself to fix the issue but will try to get to it when I can.

Stian Kirkeberg said...


MethodInfo getPermissions =
BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.InvokeMethod |
new Type[] {typeof(SPUserToken), typeof( ISecurableObject)},

This happens because getPermissions() now has some overloads and you need to tell GetMethod which one you would like.

Mark Wicker said...

Came across this post when investigating option to disable permissions for Theme and Border in SP2010.

Looks like this is fixed in SP2010. (Probably earlier too).