SharePoint Event Handler to set Item-Level Security based on Names in a List Column

First, I would like to credit a lady by the name of Mirjam for her piece at http://www.sharepointblogs.com/mirjam/archive/2007/11/11/setting-item-level-security-in-an-eventhandler.aspx; it was the starting point that I so desperately needed to build this – thanks Mirjam!

What I have here is piece of code that takes the information of two columns, ‘WriteSecurity’ and ‘ReadSecurity’ and sets Contribute and Read permissions respectively on a document library item. This allows the user to choose security for the document that they create/upload at the time of their create/upload.

It is a feature that overrides the ItemUpdating and ItemUpdated events in a site collection. The feature first looks for the columns, WriteSecurity and ReadSecurity; if it does not find both of them, it ignores the rest of the process and continues as normal. If it does find them both, it will check for data within those columns; if no data is present, the method will have the item inherit permissions from its containing library. If it finds that only the ReadSecurity column is filled, it will throw an error saying that Read without Write permissions are not allowed. Otherwise the process will cycle through the users/groups specified and assign the appropriate permissions. All of this is done with elevated permissions, but the ‘Modified By’ column will be that of the calling user.

I’ve spent a lot of time on this code, but I was not a C# programmer when I started and I knew nothing of SharePoint event receivers and code-based modification. That said, this has worked in two environments. I would imagine, however, that an experienced programmer/SharePoint Guru, would identify my messy bits and suggest better approaches (please do!) especially with my method of extracting user IDs from the security columns, and the way I repeat the code for the Write and Read bits.

This has been designed to work with Document Libraries. As said before, the feature looks for the columns ‘WriteSecurity’ and ‘ReadSecurity’ with no spaces – at least, the columns in the library should be created that way; you can rename after. And I believe that this may not work if your user’s domain starts with a number…

For those who are like I was when I first created this, he’s the summarised approach to take:

  1. Using Visual Studio, create a C# Class Library Project
  2. Add a reference to Windows SharePoint Services
  3. Rename the class to SetPermissionsEventhandler
  4. Copy the C# code into the project
  5. Sign the project with a strong key
  6. Build it
  7. Drag the resulting DLL from the project’s bin folder into the c:\windows\assembly folder on the SharePoint server.
  8. Copy the Public Key Token from the DLL now in the assembly
  9. Go to C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\ on the SharePoint server
  10. Create a folder called SetPermissionsEventhandler
  11. Within that, create two files, Feature.xml and Elements.xml
  12. Copy the respective XML codes (specified below) into the files
  13. Open a command prompt
  14. Go to C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN
  15. Run this: stsadm -o installfeature -filename SetPermissionsEventhandler\Feature.xml
  16. Run this: stsadm -o activatefeature -filename SetPermissionsEventhandler\Feature.xml -url http://YOURSITECOLLECTION
  17. Run this: iisreset
  18. If you haven’t already, create a Document Library with ‘People or Group’ columns called ‘WriteSecurity’ and ‘ReadSecurity’
  19. Enjoy.

Class Code:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Security;
using System.Security.Principal;
using System.Security.Permissions;
using System.Text.RegularExpressions;

namespace SetPermissionsEventhandler
{
    public class SetPermissions : SPItemEventReceiver
    {
        public override void ItemUpdating(SPItemEventProperties properties)
        {
            base.ItemUpdating(properties);
            //make sure security fields exist
            if (properties.ListItem.Fields.ContainsField("WriteSecurity") == true &&
                properties.ListItem.Fields.ContainsField("ReadSecurity") == true)
            {
                // check to see if the user has set Read Security without setting
                // Write Security. If so, show error page and cancel the process.
                if (properties.AfterProperties["WriteSecurity"].ToString() == ""
                    && properties.AfterProperties["ReadSecurity"].ToString() != "")
                {
                    properties.Cancel = true;
                    properties.ErrorMessage = "You cannot specify Read Security without also specifying Write Security. " +
                        "\r\n\r\nPlease click your browser's BACK button to try again.";
                }
            }
        }
       
       
        //this will fire if the ItemUpdating event was not cancelled
        public override void ItemUpdated(SPItemEventProperties properties)
        {
            //make sure security fields exist again
            if (properties.ListItem.Fields.ContainsField("WriteSecurity") == true &&
                properties.ListItem.Fields.ContainsField("ReadSecurity") == true)
            {
                //make sure that file is not checked-out before setting any permissions
                if (properties.ListItem.File.CheckOutStatus == SPFile.SPCheckOutStatus.None)
                {
                    //if no security data has been specified, inherit the permission of the
                    //containing library otherwise run the method to set the permissions
                    if (properties.AfterProperties["WriteSecurity"].ToString() == ""
                        && properties.AfterProperties["ReadSecurity"].ToString() == "")
                    {
                        InheritLibraryPermission(properties);
                    }
                    else
                    {
                        SetNewPermissions(properties);
                    }
                }
            }
        }
 
        private void InheritLibraryPermission(SPItemEventProperties properties)
        {
            Guid siteID,webID,listID;
            int itemID;
            listID = properties.ListId;
            itemID = properties.ListItem.ID;
            using (SPWeb web = properties.OpenWeb())
            {
                siteID = web.Site.ID;
                webID = web.ID;
            }
            //get the calling user id for later use
            String callingUserID = properties.CurrentUserId.ToString();
            //run this block as System Account
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite site = new SPSite(siteID))
                {
                    using (SPWeb web = site.OpenWeb(webID))
                    {
                        SPList list = web.Lists[listID];
                        SPRoleAssignmentCollection roles = list.RoleAssignments;
                        SPListItem updatedItem = list.GetItemById(itemID);
                        updatedItem.ResetRoleInheritance();
                        this.DisableEventFiring();
                        //sets the 'Modified By' column as the calling user, not the System Account
                        updatedItem["Editor"] = callingUserID;
                        updatedItem.Update();
                        this.EnableEventFiring();
                    }
                }
            });
        }
 

        private void SetNewPermissions(SPItemEventProperties properties)
        {
            Guid siteID,webID,listID;
            int itemID;
            listID = properties.ListId;
            itemID = properties.ListItem.ID;
            //get the calling userid for later use
            String callingUserID = properties.CurrentUserId.ToString();
            using (SPWeb web = properties.OpenWeb())
            {
                siteID = web.Site.ID;
                webID = web.ID;
            }
            //run this block as System Account
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite site = new SPSite(siteID))
                {
                    using (SPWeb web = site.OpenWeb(webID))
                    {
                        SPList list = web.Lists[listID];
                        SPRoleAssignmentCollection roles = list.RoleAssignments;
                        SPListItem updatedItem = list.GetItemById(itemID);
                        updatedItem.BreakRoleInheritance(false);
                        SPRoleDefinitionCollection roleDefinitions = web.RoleDefinitions;
                        SPRoleAssignmentCollection roleAssignments = updatedItem.RoleAssignments;
                        SPUserCollection users = web.AllUsers;
                        //SPGroupCollection groups = web.Groups;
                        SPGroupCollection groups = web.SiteGroups;
                        //Remove all Permissions before applying chosen permissions
                        for (int x = updatedItem.RoleAssignments.Count - 1; x >= 0; --x)
                        {
                            updatedItem.RoleAssignments.Remove(x);
                        }
                        //set write permissions
                        foreach (String eachUserID in getUserIDsFromPeopleColumn(updatedItem.File.Properties["WriteSecurity"].ToString()))
                        {
                            int intID = Int32.Parse(eachUserID);
                            //determine if ID is user or group
                            Boolean isGroup = false;
                            foreach (SPGroup testGroup in web.SiteGroups)
                            {
                                if (testGroup.ID == intID)
                                {
                                    isGroup = true;
                                }
                            }
                            if (isGroup == false) //user
                            {
                                SPUser userToAdd = users.GetByID(intID);
                                SPRoleAssignment roleAssignment = new SPRoleAssignment(userToAdd);
                                SPRoleDefinitionBindingCollection roleDefBindings = roleAssignment.RoleDefinitionBindings;
                                roleDefBindings.Add(roleDefinitions["Contribute"]);
                                roleAssignments.Add(roleAssignment);
                            }
                            else //group
                            {
                                SPGroup groupToAdd = groups.GetByID(intID);
                                SPRoleAssignment groupRoleAssignment = new SPRoleAssignment(groupToAdd);
                                SPRoleDefinitionBindingCollection groupRoleDefBindings = groupRoleAssignment.RoleDefinitionBindings;
                                groupRoleDefBindings.Add(roleDefinitions["Contribute"]);
                                roleAssignments.Add(groupRoleAssignment);
                            }
                        }

                        //repeat the above for Read permissions, if specifed
                        if (updatedItem.File.Properties["ReadSecurity"].ToString() != "")
                        {
                            //set permissions
                            foreach (String eachUserID in getUserIDsFromPeopleColumn(updatedItem.File.Properties["ReadSecurity"].ToString()))
                            {
                                int intID = Int32.Parse(eachUserID);
                                //determine if ID is user or group
                                Boolean isGroup = false;
                                foreach (SPGroup testGroup in web.SiteGroups)
                                {
                                    if (testGroup.ID == intID)
                                    {
                                        isGroup = true;
                                    }
                                }
                                if (isGroup == false) //user
                                {
                                    SPUser userToAdd = users.GetByID(intID);
                                    SPRoleAssignment roleAssignment = new SPRoleAssignment(userToAdd);
                                    SPRoleDefinitionBindingCollection roleDefBindings = roleAssignment.RoleDefinitionBindings;
                                    roleDefBindings.Add(roleDefinitions["Read"]);
                                    roleAssignments.Add(roleAssignment);
                                }
                                else //group
                                {
                                    SPGroup groupToAdd = groups.GetByID(intID);
                                    SPRoleAssignment groupRoleAssignment = new SPRoleAssignment(groupToAdd);
                                    SPRoleDefinitionBindingCollection groupRoleDefBindings = groupRoleAssignment.RoleDefinitionBindings;
                                    groupRoleDefBindings.Add(roleDefinitions["Read"]);
                                    roleAssignments.Add(groupRoleAssignment);
                                }
                            }
                        }
                        //update the item preventing further events
                        this.DisableEventFiring();
                        updatedItem["Editor"] = callingUserID; //sets the 'Modified By' column as the calling user, not the System Account
                        updatedItem.Update();
                        this.EnableEventFiring();
                    }
                }
            });
        }
 
        public string[] getUserIDsFromPeopleColumn(String rawUsers)
        {
            rawUsers = "#" + rawUsers;
            string[] rawUsersArray = rawUsers.Split(';');
            string Users = "";
            foreach (string eachUser in rawUsersArray)
            {
                if (System.Text.RegularExpressions.Regex.IsMatch(eachUser.Substring(1, 1), "^[A-Za-z]$") == false)
                {
                    Users = Users + eachUser.Substring(1) + ";";
                }
            }
            Users = Users.Substring(0, Users.Length - 1);
            string[] UsersArray = Users.Split(';');
            return UsersArray;
        }
    }
}

 

 

 

Feature.xml

<Feature Scope="Web"
   Title="Set Permissions Event Handler"
   Description="This feature takes the People and Groups identified in the 'Write Security' and 'Read Security' columns in Document Libraries and applies the relevant permissions to each item when it is edited. If the item is checked-out, the permissions will not be applied until the item is checked back in. To enable this feature's functionality in a Document Library, 'Person or Group' columns must be created with the names, WriteSecurity and ReadSecurity (with no spaces); they can be renamed thereafter."
   Id="CREATEANEWGUIDANDPUTITHERE"
   xmlns="http://schemas.microsoft.com/sharepoint/">
   <ElementManifests>
      <ElementManifest Location="Elements.xml"/>
   </ElementManifests>
</Feature>

 

 

Elements.xml

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   <Receivers ListTemplateId="101">
      <Receiver>
         <Name>SetPermissionsEventhandler</Name>
         <Type>ItemUpdating</Type>
         <SequenceNumber>10000</SequenceNumber>
         <Assembly>SetPermissionsEventhandler, Version=1.0.0.0, Culture=neutral, PublicKeyToken=PUBLICKEYTOKENFROMYOURDLL</Assembly>
         <Class>SetPermissionsEventhandler.SetPermissions</Class>
         <Data></Data>
         <Filter></Filter>
      </Receiver>
      <Receiver>
         <Name>SetPermissionsEventhandler</Name>
         <Type>ItemUpdated</Type>
         <SequenceNumber>10000</SequenceNumber>
         <Assembly>SetPermissionsEventhandler, Version=1.0.0.0, Culture=neutral, PublicKeyToken=PUBLICKEYTOKENFROMYOURDLL</Assembly>
         <Class>SetPermissionsEventhandler.SetPermissions</Class>
         <Data></Data>
         <Filter></Filter>
      </Receiver>
   </Receivers>
</Elements>
 
 
Advertisements

17 thoughts on “SharePoint Event Handler to set Item-Level Security based on Names in a List Column

  1. Hi

    Would like to say it is a Wow! approach

    Let me know you tried removing all permissions for the Document Added for the person uploading? using this event Handler Approach?

    Thank you,

  2. Pingback: SharePoint Event Handler to set Item-Level Security « Witek Blog

  3. great article! just trying to get it working now.

    In Feature.xml there is a line Id=”CREATEANEWGUIDANDPUTITHERE”

    Is this the guid of the document library?

    Thanks,
    Brian

    • Hi Brian,

      That GUID is a brand new GUID for the feature itself i.e. don’t use an existing one from a doc library or anywhere else.

      I’d be very keen to know how you go with your implementation – feel free to keep us updated.

      Cheers,
      Warren

  4. Thats worked for me. Also Changed the template type to 115 and got it working for a form library

    Didn’t know about GUIDs but found this info:
    You can generate GUIDs in Visual Studio (Tools – Create GUID – Registry Format – Copy). Paste the new GUID for the ID and remove the curly brackets.

    Thanks for help!!

    Brian

    • Excellent! That’s really good hear that:
      1. it works for you; and
      2. you’ve got it working in a form lib – I only use it for 101’s

      Cheers!

      Warren

  5. Pretty close to what I need. The code works great on my document library! And thank you for the step by step directions on implementing!

    Using the same code, I’m trying to get it to work with a generic list ListTemplateId=”100″.

    The handler will break the inheritance but won’t add the user’s permissions. The item security is left blank.

    The handler will reattach the inheritance when you clear out both fields.

    Any ideas what else needs to be changed to use this for a generic list instead of a document library?

    Thanks again!

    Ken

    • Hi Ken,

      I’m no expert and I am certainly not a C# programmer. I also have not done this for a list, but looking at the code, I noticed that the lines that use “updatedItem.File.Properties” probably suggest that they are updating security on a “file” object, perhaps? – I really don’t know…

      Please let us know if you find a solution!

  6. Warren,

    I now have this working for generic lists. Like you said the problem was in the:

    updatedItem.File.Properties[“WriteSecurity”].ToString()

    I switched this to:

    properties.AfterProperties[“WriteSecurity”].ToString()

    I also switched the statement for read and now it works! Thanks again for your code.

    Ken

  7. Hi Warren,
    Thanks for your work.
    I have successfully installed the feature, created a document library with two columns as you suggested WriteSecurity and ReadSecurity. I uploaded the document and provided the users for both writesecuirty and readsecurity it does not do anything. Can you please let me know if I am missing something. Even I entered only ReadSecurity I don’t get any error message.

    I appreciate your immediate response Please.

    Thanks in Advance
    Josh

  8. I have successfully installed the feature but it is not working. I am I missing something.

    I appreciate your quick response.

    Thanks
    Josh

  9. Hi there, just became aware of your blog through Google, and found that it is truly informative. I�m going to watch out for brussels. I will be grateful if you continue this in future. Lots of people will be benefited from your writing. Cheers!

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