Sunday, October 7, 2012

Event Receiver and column level security in SharePoint 2010

SharePoint doesn't support field/Column level security out of the box. So when we want to allow some fields to be filled up by only a particular group then we need to go for a custom development. As I found out there are some ways to do it,

1) Using InfoPath (I guess the best way)
2) Using Event Receiver (If you don’t have InfoPath, then the best way :)

I will be developing this with Event Receiver.

Understanding Event Receiver


An event receiver in Microsoft SharePoint Foundation 2010 is simply a method that is called when a triggering action occurs on a specified SharePoint object. Triggering events include actions such as adding, updating, deleting, moving, checking in, and checking out. SharePoint objects that listen for events—that is, event receiver hosts—include objects such as site collections, sites, lists, and workflows.—Microsoft Site Definition.

There can be various events like ItemAdding, ItemAdded, ItemUpdating, ItemUpdated,ItemDeleting, ItemDeleted. These events are broadly categorized into (before) synchronous and (after) asynchronous events. The method suffix such as “ing” (e.g. ItemAdding) and “ed” (e.g. ItemAdded) will tell you whether it gets invoked before or after the actual change is made.

Now there are three holders (beforeProperty, afterproperty, listitem) which carry the data during these events, hence it is very important to understand which event receiver will carry data in these holders and which not. The below table will clears this mist.
 
 


Creating Event Receiver through Visual Studio

Problem Statement: We have an "Expense Report" list in which there are two columns a) Amount  b) DateCheck . These two columns are required to be filled by only “My Test Owners” group users.
Let’s Start :
  1. Fire up Visual Studio 2010 and from the templates select “Event Receiver” for the new project under SharePoint 2010 and name the project as “HideFieldEventReceiver”

  2. The configuration wizard would ask you for the URL of the site against which you would like to create the Event Receiver, also choose deploy as farm solution. Then choose the Event Receiver settings as below.

  3. Visual Studio will create an EventReceiver.cs and Elements.XML, the EventReceiver.cs will have methods for both ItemAdding and Item Updating events, and Elements.XML will help in connecting to the List and the events.
    As in Elements.Xml the Event is connected to all Custom List, hence we need to modify it as below to make it connect to our specific List called “Expense Report”. Remove ListTemplateId to ListUrl.

  4. Now open the EventReceive.cs file and in the ItemAdding Event add the below code. This will run whenever a user is adding a new Item in List, which will check the permissions of the user and also identify if he is updating those restricted fields. We will be using the AfterProperties of the column to check if the fields are being updated.

  5.                 public override void ItemAdding(SPItemEventProperties properties)
            {
                base.ItemAdding(properties);
                if (properties.AfterProperties["Amount"].ToString() != string.Empty || 
                    properties.AfterProperties["DateCheck"].ToString() != string.Empty)
                {
                    using (SPWeb ob = properties.OpenWeb())
                    {
    
                        SPGroup group = ob.Groups["My Test Owners"];
                        if (!ob.IsCurrentUserMemberOfGroup(group.ID))
                        {
                            properties.Cancel = true;
                            properties.ErrorMessage = "You are not allowed to fill this";
                        }
                    }
                }
            }
            
  6. Now in the Item Updting Event add the below code. This is little complicated as we need to check if there is a change in the field value by comparing the ListItem property(before Value) and AfterProperty (afterValue) of the fields.

  7. public override void ItemUpdating(SPItemEventProperties properties)
            {
                base.ItemUpdating(properties);
                if ((properties.ListItem["Amount"]!=null && properties.ListItem["Amount"].ToString() != properties.AfterProperties["Amount"].ToString())
                    || (properties.ListItem["DateCheck"]!=null && 
                    DateTime.Parse(properties.ListItem["DateCheck"].ToString(), null, DateTimeStyles.AdjustToUniversal) != 
                    DateTime.Parse(properties.AfterProperties["DateCheck"].ToString(), null, DateTimeStyles.AdjustToUniversal)))
                {
                    using (SPWeb ob = properties.OpenWeb())
                    {
                        SPGroup group = ob.Groups["My Test Owners"];
                        if (!ob.IsCurrentUserMemberOfGroup(group.ID))
                        {
                            properties.Cancel = true;
                            properties.ErrorMessage = "You are not allowed to Edit the restricted Fields";
                        }
                    }
    
                }
                else if ((properties.ListItem["Amount"] == null && properties.AfterProperties["Amount"].ToString() != string.Empty)
                    || (properties.ListItem["DateCheck"]==null && properties.AfterProperties["DateCheck"] !=null && 
                    properties.AfterProperties["DateCheck"].ToString() != string.Empty))
                {
                    using (SPWeb ob = properties.OpenWeb())
                    {
                        SPGroup group = ob.Groups["My Test Owners"];
                        if (!ob.IsCurrentUserMemberOfGroup(group.ID))
                        {
                            properties.ErrorMessage = "You are not allowed to Edit the restricted Fields";
                            properties.Status = SPEventReceiverStatus.CancelWithError;
                            properties.Cancel = true;
                            
                        }
                    }
                }
            }
    
  8. Deploy the solution and test the code. I believe the code is very much self-explanatory where we test on the events if the fields have changed or not. If they are changed we check whether the current user is a member of the Owners group which is allowed for access. If they are not then we show the Error with Properties.ErrorMessage and we cancel the event



 

5 comments:

  1. This is NOT column level security, how are you preventing the display of data in that column for certain groups? Just by an event receiver you cannot control the views or alerts where the column is used...there is a much BROADER impact of using column level protection, namely Search results and DataSheet Views. You may be better of considering these before any implementation. There are only a handful of companies that have such products, and even these have some limitations.

    ReplyDelete
    Replies
    1. The display of fields for restricted groups are done by views.Also the Insert and Edit forms are having scripts on the page to show and hide those fields based on the user permission. This code above does a second level security whereby if a user manages to display those fields and try to push down the information then he will be unable to do so.

      We have tested this and this methodology is full proof. I know that these customization are hard to move from stage to production but in some environments like ours this is the best and free solution.

      Delete
  2. I don't get it, the blog post does not describe how it can aaply column level protection on an existing list conatining data. Not sure if you were aware, there are other areas like alerts that would need to be addressed; and I can't forsee a solution with InfoPath or Event Receivers by themselves. If you have addressed Add/Edit/display forms, why not share those solutions as well?

    ReplyDelete
    Replies
    1. I will definetly be shareing the Add/Edit forms javscript code in my subsequent post.The security which we are trying to accomplish here is that the only some set of users group can push information to those secure columns. The view part of those column is still open to be accissible to others, hence other feature like alerts and search wont create a difference, because they are still open for others.
      I completely agree that there can be scenarios where in search also u dont want those columns to be cralled but this is not the case here.

      Delete
  3. properties.ErrorMessage = "You are not allowed to Edit the restricted Fields";
    properties.Status = SPEventReceiverStatus.CancelWithError;
    properties.Cancel = true;
    I have used same apporch but i am not able to get error message

    ReplyDelete