Using Event Handlers to Assign Item Level Permissions to a Document Repository
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:
- Depending on the type of events you are hooking into the properties event argument may not always be populated.
- 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.