Using Event Handlers to Assign Item Level Permissions to a Document Repository

 
I was speaking with a developer following my session last weekend at the Richmond code camp and he had some questions related to coming up with a solution that among other things automated different aspects of assigning item level security to a document repository.  In his scenario managers are required to restrict access to certain documents on the manager's site to only employees under his/her direct supervision although many different employees have access to the site. Now this could be managed through some creative uses of groups (and someone managing those group memberships) and simply break security inheritance between the site and document repository but if you required more granularity that comes with item level permissions there is an option to automate the process of item level permissions assignments  Item Level Security allows you to set permissions for a specific user or group on a single record in a list. This is a very powerful feature but can often be cumbersome to apply by hand to every document or list item as they are created.

My suggestion to him was to wire an event handler onto the ItemAdded event of the document repository and then modify the permission on the fly based on some external logic that helps define manager\employee relationships. Brian Wilson did a good series of  posts  a few months back that describe event handlers in more detail. Also checkout this months (November 2007) copy of MSDN Magazine which has a good article by Ted Pattison on Event Handlers.

 

Step #1 - Define Manager\Employee Relationships

Define the relationships between your employees and managers. In my example I created a simple list with 2 columns. Employee and Manager. These columns provide to the permission assignment logic the relationships between manager and employee to find all the employees for a given manager.  Other possibilities include the use of user profile information in MOSS which is gleamed from AD, MySite Colleagues Membership, or finally an external system such as  using the BDC to interact with an external HR system .

NOTE: I will still suggest the use of a mix of event handlers and SharePoint group membership instead of direct user assignment. First it reduces the general overhead on the list (which is likely negligible in most implementations) but more importantly if a relationships changes (employee moves to a new department for example) then those changes can cascade down to existing item level assignments (if using a group) as opposed to a user retaining their "old" permissions to the documents because individual user assignments on the document and not a group.  For the purpose of this example I'm keeping things simple with individual permissions but this could easily be modified to use groups instead of users.

Step #2 - Develop the Event Handler

SharePoint Event Handlers allow developers to write code the executes based on some activity that takes place inside a list or content type (no longer just document repositories but any list in WSS 3.0). Responses to events on lists, events on sites, events on list definitions, and events on contenttypes to name a few can all be wired with custom logic .

 

The following is a snippet of a class that is registered with the ItemAdded event of a list and assigns item level permissions to a list item (in this scenario a document).

   1: public class DocumentPermissionHandler : SPItemEventReceiver 
   2:    {
   3:  
   4:        public override void ItemAdded(SPItemEventProperties properties)
   5:        {
   6:  
   7:            SPSecurity.RunWithElevatedPrivileges(delegate()
   8:            {
   9:                //We need to open a new web because we want one that 
  10:                //was created under the Elevated Privileges context
  11:                SPWeb web = new SPSite(properties.WebUrl).OpenWeb();
  12:  
  13:                //Remember to Dispose
  14:                using (web)
  15:                {
  16:                    List<SPRoleAssignment> documentRoleAssignments = new List<SPRoleAssignment>();
  17:  
  18:                    //add the manager who is the current user uploading the document in our scenerios
  19:                    documentRoleAssignments.Add(GetManagerRoleAssignment(properties.UserLoginName, web));
  20:  
  21:                    //Get all the employees based on the current manager and then add the employees as readers to the document
  22:                    documentRoleAssignments.AddRange(GetEmployeeRoleAssignments(properties.UserLoginName, web));
  23:  
  24:                    //Break permission inheritance on the document
  25:                    if (!properties.ListItem.HasUniqueRoleAssignments)
  26:                    {
  27:                        properties.ListItem.BreakRoleInheritance(true);
  28:                    }
  29:  
  30:                    //add new role assignments to the document
  31:                    foreach (SPRoleAssignment assignment in documentRoleAssignments)
  32:                    {
  33:                        properties.ListItem.RoleAssignments.Add(assignment);
  34:                    }
  35:  
  36:                    //Save trhe Changes
  37:                    properties.ListItem.Update();
  38:                }
  39:            }
  40:            );
  41:  
  42:            base.ItemAdded(properties);
  43:  
  44:        }
  45:  
  46:        private SPRoleAssignment GetManagerRoleAssignment(string userName, SPWeb web)
  47:        {
  48:            SPUserCollection users = web.SiteUsers;
  49:  
  50:            //Load up the manager (current user) account
  51:            SPUser manager = users[userName];
  52:  
  53:            //obtain a role definition for administrator
  54:            SPRoleDefinition adminRoleDefinition = web.RoleDefinitions.GetByType(SPRoleType.Administrator);
  55:  
  56:            //Create a role assignment for the user and add the role definition to the assignment
  57:            SPRoleAssignment managerRoleAssignment = new SPRoleAssignment(manager as SPPrincipal);
  58:            managerRoleAssignment.RoleDefinitionBindings.Add(adminRoleDefinition);
  59:            
  60:            return managerRoleAssignment;
  61:  
  62:        }
  63:  
  64:        private List<SPRoleAssignment> GetEmployeeRoleAssignments(string userName, SPWeb web)
  65:        {
  66:            SPUserCollection users = web.SiteUsers;
  67:            List<SPRoleAssignment> employeeAssignments = new List<SPRoleAssignment>();
  68:  
  69:            //Get a reference to the employees list. In our scenerio its located in the main site (web) and manager sites are child webs
  70:            SPList employeeList = web.ParentWeb.Lists["Employees"];
  71:  
  72:            //Generate a query to get all the employees that have the current user as their manager
  73:            //NOTE: If you may have to add the LookupId="true" to the query depending on what the display options for your user fields are
  74:            SPQuery query = new SPQuery();
  75:            query.Query = String.Format("<Where><Eq><FieldRef Name=\"Manager\" /><Value Type=\"User\">{0}</Value></Eq></Where>", userName);
  76:  
  77:            SPListItemCollection employees = employeeList.GetItems(query);
  78:            SPUser spEmployee;
  79:            SPRoleDefinition readerRoleDefinition;
  80:            SPRoleAssignment employeeRoleAssignment;
  81:  
  82:            //Loop through each result and create a role assignment and reldefinition binding to that assignment
  83:            foreach (SPListItem employee in employees)
  84:            {
  85:                string[] userNameField = employee["Employee"].ToString().Split(new char[] { '#' });
  86:  
  87:                spEmployee = users[userNameField[1]];
  88:  
  89:                readerRoleDefinition = web.RoleDefinitions.GetByType(SPRoleType.Reader);
  90:  
  91:                employeeRoleAssignment = new SPRoleAssignment(spEmployee as SPPrincipal);
  92:  
  93:                employeeRoleAssignment.RoleDefinitionBindings.Add(readerRoleDefinition);
  94:  
  95:                employeeAssignments.Add(employeeRoleAssignment);
  96:  
  97:            }
  98:  
  99:            return employeeAssignments;
 100:  
 101:        }
 102:  
 103:  
 104:    }

 

 A few gotchas when working with event handlers:

  1. Depending on the type of events you are hooking into the properties event argument may not always be populated.
  2. You may have to use impersonation or the new RunWithElevatedPriveledges functionality depending on the rights that the current user has. Along with that you may not be able to use some of the existing property objectives (such as SPWeb) because they were created in a different context then the elevated permissions.

 

Step #3 - Deployment

Deployment of event handlers can be a bit of a pain. The default deployment of event handlers via a feature can be applied to broad at times (depending on your goal) as it wires in the event handler for a specific list type and every instance. For a more targeted deployment you have to use the object model. Luckily there are a couple nice community written utilities out there including  the Event Handler Explorer

posted on 2009-11-26 22:14  黑木 Kang  阅读(383)  评论(0编辑  收藏  举报

导航