SharePoint Internals – Hristo Pavlov’s Blog

18 September, 2008

Implementing Custom Security Rights in SharePoint

Filed under: SharePoint — Tags: , , , , , — hristopavlov @ 11:55 pm

This is a blog entry about a customization that some of you will call a ‘hack’ but which power in my opinion is far greater that the support issues it poses. However before I start I want to make a disclaimer that if you follow my article you do it on your own risk and I am not going to provide support other than what you can find in this article. What I describe below has been implemented successfully in at least one large enterprise project I’ve worked on and works great!

What is the whole fuss about

Well you know how in SharePoint you have those users that can be granted various permission levels, which permission levels contain predefined user rights such as: Manage Lists, Override Check out, Add Items, Edit Items … etc … all those different rights that exist in SharePoint OOB. Those user rights are pretty much fixed and defined by Microsoft. Imagine you are building a custom application on the top of SharePoint and you want to define your own custom user rights. You can always do this in alternative ways but what if you wanted to extend the user rights in SharePoint by defining your own rights and this way keeping the security centralized? This would allow the new user rights to be managed by SharePoint the same way it manages the rest of the permissions. You would be able to grant the custom rights via the SharePoint Permission Levels from the UI and from the object model. You will be able to use the object model to test whether any of your custom rights are granted to a user and most improtantly you will have your custom rights and permissions going togather with the SharePoint permissions. And if anyone has the “Full Control” access granted they will also automatically get your new custom rights. Wouldn’t that be nice? Well this article is about how you can implement this!

How the user rights work

The rights used by SharePoint when defining Permission Levels are all defined in the Microsoft.SharePoint.SPBasePermissions enum. The enum is a flags enum each member of which is a 64 bit integer number that has one of its bits set. This allows a total of 64 different rights to be defined. The permissions granted to a SharePoint user is a single 64 bit permission mask represented as a SPBasePermissions object which corresponding bits/flags will be set for the user rights that have been granted to the user. You can cast this SPBasePermissions object to a 64 bit integer and check whether a specific bit = flag = user right is set.  At the moment the number of flags used by SharePoint is only 33 or 64, which means there is enough room for us to define an extra 31 custom user rights!

Our plan will be:

– Find a free enum value for our new user right
– Define a new Permission Level (using code) that contains our new user right
– Grant the new user right from the SharePoint UI
– Write code that checks whether a user has the new user right granted

How we implement custom user rights

The first step of our plan is to find a free enum value that is not already in use. We pretty much need to go through the existing members of the SPBasePermissions enum and pick one which is not used and which we don’t use already. The complete list of all possible values is shown below.

[Flags]

public enum SPBasePermissions

{

    EmptyMask =                 0x0000000000000000,

    ViewListItems =             0x0000000000000001,

    AddListItems =              0x0000000000000002,

    EditListItems =             0x0000000000000004,

    DeleteListItems =           0x0000000000000008,

    ApproveItems =              0x0000000000000010,

    OpenItems =                 0x0000000000000020,

    ViewVersions =              0x0000000000000040,

    DeleteVersions =            0x0000000000000080,

    CancelCheckout =            0x0000000000000100,

    ManagePersonalViews =       0x0000000000000200,

    UNUSED-VALUE =              0x0000000000000400,

    ManageLists =               0x0000000000000800,

    ViewFormPages =             0x0000000000001000,

    UNUSED-VALUE =              0x0000000000002000,

    UNUSED-VALUE =              0x0000000000004000,

    UNUSED-VALUE =              0x0000000000008000,

    Open =                      0x0000000000010000,

    ViewPages =                 0x0000000000020000,

    AddAndCustomizePages =      0x0000000000040000,

    ApplyThemeAndBorder =       0x0000000000080000,

    ApplyStyleSheets =          0x0000000000100000,

    ViewUsageData =             0x0000000000200000,

    CreateSSCSite =             0x0000000000400000,

    ManageSubwebs =             0x0000000000800000,

    CreateGroups =              0x0000000001000000,

    ManagePermissions =         0x0000000002000000,

    BrowseDirectories =         0x0000000004000000,

    BrowseUserInfo =            0x0000000008000000,

    AddDelPrivateWebParts =     0x0000000010000000,

    UpdatePersonalWebParts =    0x0000000020000000,

    UNUSED-VALUE =              0x0000000040000000,

    UNUSED-VALUE =              0x0000000080000000,

    UNUSED-VALUE =              0x0000000100000000,

    UNUSED-VALUE =              0x0000000200000000,

    UNUSED-VALUE =              0x0000000400000000,

    UNUSED-VALUE =              0x0000000800000000,

    UseClientIntegration =      0x0000001000000000,

    UseRemoteAPIs =             0x0000002000000000,

    ManageAlerts =              0x0000004000000000,

    CreateAlerts =              0x0000008000000000,

    EditMyUserInfo =            0x0000010000000000,

    UNUSED-VALUE =              0x0000020000000000,

    UNUSED-VALUE =              0x0000040000000000,

    UNUSED-VALUE =              0x0000080000000000,

    UNUSED-VALUE =              0x0000100000000000,

    UNUSED-VALUE =              0x0000200000000000,

    UNUSED-VALUE =              0x0000400000000000,

    UNUSED-VALUE =              0x0000800000000000,

    UNUSED-VALUE =              0x0001000000000000,

    UNUSED-VALUE =              0x0002000000000000,

    UNUSED-VALUE =              0x0004000000000000,

    UNUSED-VALUE =              0x0008000000000000,

    UNUSED-VALUE =              0x0010000000000000,

    UNUSED-VALUE =              0x0020000000000000,

    UNUSED-VALUE =              0x0040000000000000,

    UNUSED-VALUE =              0x0080000000000000,

    UNUSED-VALUE =              0x0100000000000000,

    UNUSED-VALUE =              0x0200000000000000,

    UNUSED-VALUE =              0x0400000000000000,

    UNUSED-VALUE =              0x0800000000000000,

    UNUSED-VALUE =              0x1000000000000000,

    UNUSED-VALUE =              0x2000000000000000,

    EnumeratePermissions =      0x4000000000000000,

    FullMask =                  0x7FFFFFFFFFFFFFFF,

}

We need to pick one unused value and let this be 0x0000000000002000. This 64 bit number is actually the decimal 8192 presented in hex. Let’s call our new user right “My Custom {SharePoint Internals} User Right” and grant it to users that will be allowed to use ‘tricks’ when developing SharePoint applications. The best way to grant our new right would be if we can add it as an extra check box to the editrole.aspx page. Doing this may or may not be possible and will definitely require very serious customizations to this SharePoint page. An easier alternative is to create a new Permission Level and setup its permission mask to contain only our new user right. This way we can grant this new permission level to everyone that we want to allow to use ‘tricks’.

To create our special Permission Level we will need to use code and we can “install” it for example as a feature receiver. One of the reasons for our ‘trick’ to work is because the permission levels are actually created from unmanaged code and the permission masks from managed SharePoint object model code are passed as 64 bit integers rather than as an enum values.  The function that will create any Permission Level of our liking is the AddRoleDef method of the SPRequest unmanaged object associated with every SPWeb. In order to call this method we will need to use Reflection though. In this case we already have the rootWeb SPWeb object and are going to add this new SPRoleDefinition to the root web, which will make it available for the whole site collection if we don’t break the role definitions. The code below does the job:

 

PropertyInfo propRequest =

      typeof(SPWeb).GetProperty(“Request”, BindingFlags.NonPublic | BindingFlags.Instance);

 

object spRequest = propRequest.GetValue(rootWeb, null);

 

MethodInfo mthAddRoleDef = spRequest.GetType().GetMethod(“AddRoleDef”);

 

mthAddRoleDef.Invoke(spRequest, new object[]

{

    rootWeb.Url,

    “My Custom {SharePoint Internals} User Right”,

    “Can use ‘tricks’ when coding”,

    false,

    1000,

    (ulong)0x2000, /* The mask of our custom right */

    (byte)5, /* Use ‘5’ so editing is not allowed */

    0

});

 

Well we completed most of our plan. Now the new Permission Level will show up in the central administration. Its checkbox will be disabled, which is done on purpose, because the editrole.aspx page will not know how to show the custom right. However it will be there and SharePoint will know about it even that you don’t see it from the UI. Next steps would be to actually grant this new Permission Level to some users. This can be done via the SharePoint administration pages or using the object model. Once we have some users that are allowed use ‘tricks’ we will possible want to check in various ocasions whether the curent user is allowed to use tricks in the current web or not i.e. to check whether our custom user right is granted to the user or not. This is done by the standard DoesUserHavePermissions method you usually use, but the permission mask is passed a little differently:

 

(web as ISecurableObject).DoesUserHavePermissions((SPBasePermissions)0x2000)

 

Well that’s it! You have successfully defined a new user right and have integrated it in the SharePoint security model.

What are the risks

Well Microsoft may decide to add a few more user rights themselves and use some of the flags we’ve taken already. Even that this sounds possible actually it will require a bigger change in the object model and such a change is very unlikely to be done in SharePoint 2007 i.e. without a major version release. Even if this happens remember we have 31 extra flags to choose from so that’s plenty.

Another thing that could go wrong is if you are using any third party tools to replicate permissions across servers or site collections and those third party tools use a conservative approach in the replication. Depending on how exactly the replication is implemented there is a chance the special permission masks to be lost after the replication. However this may only happen to the permission mask of the custom Permission Level we created at the root web. Now if you simply don’t replicate this Permission Level (because it will not change) then you won’t have any other issues.

7 Comments »

  1. […] Implementing Custom Security Rights in SharePoint […]

    Pingback by Links (9/21/2008) « Steve Pietrek - Everything SharePoint — 21 September, 2008 @ 11:46 pm

  2. To remove that we can use

    PropertyInfo propRequest = typeof(SPWeb).GetProperty(“Request”, BindingFlags.NonPublic | BindingFlags.Instance);

    object spRequest = propRequest.GetValue(rootWeb, null);

    MethodInfo mthRemoveRoleDef = spRequest.GetType().GetMethod(“RemoveRoleDef”);
    mthRemoveRoleDef.Invoke(spRequest, new object[] { rootWeb.Url, roleid});


    http://code.grep.in

    Comment by bekz — 21 November, 2008 @ 12:21 pm

  3. This is very clever, Hristo, but I think there is another problem with your use of reflection to call into the unmanaged layer to create your custom permission level. This could potentially be worse than a conflict between the enumerated value you have chosen and another that Microsoft or another developer might use.

    What if a service pack changes the implementation of SPRequest in such a way that your call not only fails, but breaks something in the unmanaged code layer? Worse yet, what if it succeeds and breaks something? I’m thinking along the lines of code that depends on the SPBasePermissions mask internally. Perhaps there is an equally clever internal algorithm that manipulates the mask in some way by relying on certain flags being empty.

    Comment by John Holliday — 6 January, 2009 @ 3:08 pm

  4. Hi John,

    First of all let me say I’m happy to see comments from a very respected MVP in my blog🙂

    Now let me address your concerns. About the use of reflection, this is only done to get to the internal member of type SPRequest that comes with every SPSite. The methods I use to define the permission level with the new user right are actually public methods from the ISPRequest COM interface implemented by the SP.SPRequest object in OWSSVR.DLL (The core of WSS). If Microsoft change this interface they would introduce breaking changes so I think it is highly unlikely that they’ll do that.

    Furhter more looking at the documentation of the usergroup.asmx SharePoint web service (http://msdn.microsoft.com/en-us/library/ms774640.aspx), which also calls the same unmanaged COM object, we find the following description for the permission mask that is passed as 64 bit integer (rather than SPBasePermissions value):

    permissionMask:
    A 64-bit unsigned integer in 0x00000000 format that represents a Microsoft.SharePoint.SPRights value and specifies permissions for the new role definition. Use the pipe symbol (“|”) in Visual C# or Or in Visual Basic to delimit values when creating a custom permission mask that combines permissions.

    All this indicates to me that this field is intended to be used as bitmask permissions. I also don’t see a reason why a direct call to this web method with unused user right bit will not achieve the same I achieve with the reflection approach. I’ll try this later on.

    Additionally the method that is used to check for the user right – DoesUserHavePermissions() – is also implemented in the unmanaged world of OWSSVR.DLL. This means that my approach is not dependent on the managed SPBasePermissions enumeration and how it may be used in various parts of the managed SharePoint world. In the unmanaged world this ulong is intended to be a permission bitmask and is also saved as such in the content database. That can be even considered as a design pattern for storing permission masks. Also because there are public methods where you pass the permission mask as a ulong, I don’t think there are any smarts in the internal implementation of WSS that could rely on certain bits being zero. From my experiense the bits I’ve used worked with no problem. If we can find the documentation of the public ISPRequest COM interface – this could clear any doubt of how the permissionMask is used.

    Still all this doesn’t mean that there is nothing that could go wrong. It is possible Microsoft to add new user rights. I however believe that it is unlikely such a change to be done without releasing a new version of SharePoint (i.e. Office 14) and worying about whether this solution will work in the new version of SharePoint is irrelevant.

    About two people using the same bit – well yes if more than one independent developers (vendors) are trying to define their own rights of course there is a risk that they could pick the same bit for their different user right. Having a controlled environment will mitigate this risk. It should be also clear that my approach only allows 31 new user rights at max as currently this is the number of unused bits.

    So this solution will defenitely not fit in every project but can add a huge value in projects involving more controlled environments. The customer where I implemented this was very happy to have the power to define custom user rights for their needs. Their enterprise environment was controlled which made it impossible to install third party components without investigation and approval.

    To sum up, I believe that risks of using custom rights as described in my post in a controlled environment are absolutely remote. In an uncontrolled environment the risks will be higher, especially if many people start using this approach for custom SharePoint components. However it should be clear from the component purpose whether it may use custom rights bits or not.

    Comment by hristopavlov — 6 January, 2009 @ 11:26 pm

  5. […] Implementing Custom Security Rights in SharePoint SharePoint jogosultságok kezelése – kódból. (tags: sharepoint2007 security Development) […]

    Pingback by ÁghyBlog » links for 2009-01-06 — 15 January, 2010 @ 12:15 am

  6. You have really great taste on catch article titles, even when you are not interested in this topic you push to read it

    Comment by Bruirebraigma — 16 February, 2010 @ 10:51 pm

  7. Hristo, Very intelligent work in MOSS 2007/WSS. I stumbled across your article, to make this work in SharePoint 2010, but this will not work as the SPBase Permissions enum has been ‘fixed’ not to have any ‘unused-value’: This what they have in SP 2010:

    [SubsetCallableTreatAsExternalType]
    [Flags]
    public enum SPBasePermissions : ulong
    {
    AddAndCustomizePages = 262144,
    AddDelPrivateWebParts = 268435456,
    AddListItems = 2,
    ApplyStyleSheets = 1048576,
    ApplyThemeAndBorder = 524288,
    ApproveItems = 16,
    BrowseDirectories = 67108864,
    BrowseUserInfo = 134217728,
    CancelCheckout = 256,
    CreateAlerts = 549755813888,
    CreateGroups = 16777216,
    CreateSSCSite = 4194304,
    DeleteListItems = 8,
    DeleteVersions = 128,
    EditListItems = 4,
    EditMyUserInfo = 1099511627776,
    EmptyMask = 0,
    EnumeratePermissions = 4611686018427387904,
    FullMask = 9223372036854775807,
    ManageAlerts = 274877906944,
    ManageLists = 2048,
    ManagePermissions = 33554432,
    ManagePersonalViews = 512,
    ManageSubwebs = 8388608,
    ManageWeb = 1073741824,
    Open = 65536,
    OpenItems = 32,
    UpdatePersonalWebParts = 536870912,
    UseClientIntegration = 68719476736,
    UseRemoteAPIs = 137438953472,
    ViewFormPages = 4096,
    ViewListItems = 1,
    ViewPages = 131072,
    ViewUsageData = 2097152,
    ViewVersions = 64
    }

    I had to comeup with sort-of a different approach, is to use the default permissions in a different way. eg. viewUsageData is ‘Can Request Refund’. Instead of using these permissions on main site, I then setup a ‘Permissions site’ for the sole purpose of managing permissions. In the code, I then read the permissions of the ‘Permissions site’ to manage application level permissions. Kind of kloogey..but with this approach, I don;t have to write a single line of code or develop any UI for permissions in my custom appication that is deployed in the SharePoint environment.

    Comment by Anil — 18 October, 2011 @ 7:10 am


RSS feed for comments on this post. TrackBack URI

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

Create a free website or blog at WordPress.com.

%d bloggers like this: