in

SharePoint Blogs

The Best Place for SharePoint-related Blogs

Mirjam's blog

Blogging about SharePoint related stuff

Setting item level security in an eventhandler

Last week I was building a solution for a customer that involved setting item level security on a document in a document library the moment it is added to the document library.
I'm not a big fan of item level security, because it can create chaos from a maintenance perspective, but sometimes it's simply the best, or even the only solution.

I started out be creating the feature that will contain the eventhandler.

The Feature.xml is very straightforward:

<Feature Scope="Web"
    Title="Set Security Eventhandler"
    Id="7B2CB0DC-8F27-4252-A4F2-89729DF9331B"
    xmlns="http://schemas.microsoft.com/sharepoint/">
    <ElementManifests>
        <ElementManifest Location="Elements.xml"/>
    </ElementManifests>
</Feature>

The Elements.xml looks like this:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <Receivers ListTemplateId="101">
        <Receiver>
            <Name>AddedEventHandler</Name>
            <Type>ItemAdded</Type>
            <SequenceNumber>10000</SequenceNumber>
            <Assembly>Macaw.Custom.Moss2007.Portal.Business.Components, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6bdc41c2016ac3e3</Assembly>
            <Class>Macaw.Custom.Moss2007.Portal.Business.Components.SetSecurityEventHandler</Class>
            <Data></Data>
            <Filter></Filter>
        </Receiver>
    </Receivers>
</Elements>

So you can see that I created an eventhandler that will fire when an item is added to the library. By setting the ListTemplateId I'm linking the eventhandler to document libraries. This means that the eventhandler will be linked to every document library on the web where this feature is activated. It would be better to create a feature with your own custom type of document library and link to that, so you can make sure that the eventhandler will only fire when it's supposed to.

Now that the feature is created it's time to move on to the actual code for the eventhandler.

    // First create a new class that inherits from the SPItemEvenReceiver class
    public class SetSecurityEventhandler : SPItemEventReceiver
    {

        // Override the ItemAdded event and add your own code
        public override void ItemAdded(SPItemEventProperties properties)
        {
            SetSecurityForNewItem(properties.ListItem.File, properties.ListItem.ParentList);
        }

        // The function in which I actually set the item level security on the newly added document
        private void SetSecurityForNewItem(SPFile newFile, SPList docLib)
        {
            SPListItem newItem = newFile.Item;
            newItem.BreakRoleInheritance(false);

            SPRoleDefinitionCollection roleDefinitions = docLib.ParentWeb.RoleDefinitions;
            SPRoleAssignmentCollection roleAssignments = newItem.RoleAssignments;

            SPUserCollection users = docLib.ParentWeb.AllUsers;

            try
            {
                SPUser userToAdd = users.GetByEmail(newFile.Properties["MailTo"].ToString());
                SPRoleAssignment roleAssignment = new SPRoleAssignment(userToAdd);
                SPRoleDefinitionBindingCollection roleDefBindings = roleAssignment.RoleDefinitionBindings;
                roleDefBindings.Add(roleDefinitions["Contribute"]);
                roleAssignments.Add(roleAssignment);
            }
            catch (Exception ex)
            {
                ExceptionPublisher.PublishInternalException(ex);
            }
        }
    }

The interesting bit of code can be found in the SetSecurityForNewItem function.

The function starts of by resolving the SPListItem from the SPFile and by breaking the role inheritance on the item. A necessary step to enable item level security on this item. The false means that the security as it is set on the parent document library is not being copied into the item before breaking the inheritance.

Next step is to get the roledefinitions from the parentweb and the roleassignments from the item. The roledefinitions are only assigned to webs, and not to document libraries, lists or items.

Now I need to get the collection of users from the web, these are all the users that are members of the site, or that have browsed to the site. I need this collection of users in order to resolve the user belonging to the email address that is added in one of the properties of the document. The document is a postal item, and the user belonging to the email address is the addressee. To get the address from the document property I use newFile.Properties["MailTo"].ToString(). Using newItem["MailTo"].ToString() would have produced the same result.

Next I add the user to a new roleassignment and get the collection of roledefinitionbindings from the roleassignment, The roledefinitionbindings are used to bind roledefiitions to a roleassignment. Now add a roledefinition (in this case I add the Contribute definition) to the roledefinitionbindings. The last step is to add the new roleassignment to the collection of roleassignments of the document.

Setting security in WSS 3.0 from code is quite complicated. There are roleassignments, roledefinitions and roledefinitionbindings. Roleassignments are used to add users and to link them to sites, webs, libraries or items. Roledefinitions are used to determine the level of security, it can for instance be Read, or Contribute, or you can create your own definition, I'll write another post about that later on. Roledefinitionbindings are used to bind roledefinitions to roleassignments. I must say that I do not completely understand why we can't just bind roledefinitions directly to roleassignments, but we can't, and there is probably an explanation, I just don't know what it is.
I can however very easy change the above code to add the same user with conbribute roledefinitions to the document library instead of the item. The only line that needs to change in order to achieve this is:

            SPRoleAssignmentCollection roleAssignments = docLib.RoleAssignments;

In order to give the user read instead of contribute rights I would use:

                roleDefBindings.Add(roleDefinitions["Read"]);

Basically all you have to do to use the object model to set security in WSS 3.0 is remember the piece of code. You don't need to understand the "why" to build great solution with it..

Comments

 

Blog del CIIN said:

De nuevo os presentamos la tradicional entrega de enlaces interesantes que sobre WSS 3.0 &amp; MOSS han

November 14, 2007 1:21 AM
 

Dan Russell said:

Mirjam, thanks for the code.  However, in the second statement of SetSecurityForNewItem, you are using the variable copiedItem to break role inheritance.  Shouldn't it actually be newItem?:

     newItem.BreakRoleInheritance(false);

Also, have you noticed any performance hits when using item-level security?  Thanks!

December 30, 2007 6:37 AM
 

Satheesh P said:

BreakRoleInheritance causes performance problem when there are too many users in the site or at the document library. If the users count croses more than 400 then it takes more than 2 mins to complete that function.

If someone has any solution on that, please post it.

January 25, 2008 1:21 AM
 

Mirjam said:

Hi Dan,

You are right, I missed one replacement when adjusting my code for the post. I will change it.

Thanks!

Mirjam

January 25, 2008 9:30 AM
 

Mirjam said:

Dan, Satheesh,

I have never used item level security on a scale that caused problems. After hitting the WSS v3 bounderies once I try to stay far away from them.

You can have 2000 security principles (= users and groups) in a site collection. Also check this link for more MOSS 2007 boundaries.

Apparently when using item level security there are other forces at work that aren't documented as well yet.

Generally speaking these things can't be solved, except be breaking the solution down over multiple site collections and content databases.

Regards,

Mirjam

January 25, 2008 9:35 AM
 

Wdrozenia Sharepoint said:

I think the code from your sample is run in user context, so for all visitors and contributors it should be wrapped in RunWithElevatedPriveleges method.

March 24, 2008 4:36 PM
 

Satheesh said:

i facing a problem with break role inheritance, as of now a custom list is having 4000+ users and i want to provide item level security. I trying to remove permissions through a custom code

           while(oListItem.RoleAssignments.Count !=0)

           {

               oListItem.RoleAssignments.Remove(0);

           }

i have to do this on three items on different lists/libraries. And all this happens on ItemAdded method. But very frequently it fails. Is there any other alternative methods for providing item level security in the above scenario.

Satheesh

April 10, 2008 5:50 AM

Leave a Comment

(required )  
(optional )
(required )  
Add

About Mirjam

Mirjam van Olst works as a lead SharePoint developer at Macaw in the Netherlands. She started working for Macaw in April 2004. Before that she had her own company (Van Olst Websolutions) with which she build web applications. Mirjam has specialized in SharePoint development since June 2004. She started working with the 2007 Office system in may 2006. She has implemented several world-wide intranet environments based on SharePoint. She also gives presentations about MOSS 2007 on a regular basis and occasionally writes articles about MOSS related subjects.

Need SharePoint Training? Attend a SharePoint Bootcamp!

Posts (c) their respective authors. Everything else (c) 2007 SharePoint Experts