Search This Blog

Tuesday, December 18, 2012

Create a truly personal field with Field Level Security

Since Microsoft Dynamics CRM 2011 was released, I never had the chance to really use Field Level Security feature. Last week, I was asked to implement a secured field that should be accessed only by the owner of the record, even if the record itself can be accessed by anyone.
 
This can be achieved with one field level security profile and one plugin.
 
Field Level Security Profile
The field level security profile basically restrict Read and Update of the field to all users (or all teams) but allows Create. This way, anyone can create a record with access to the secure field. When created, as all users can’t read or update, the field is not accessible anymore.
 
Plugin
The plugin will handle this problem. On PostCreate event, it will share the secured field with the owner. For this, we use the entity PrincipalObjectAttributeAccess to give Read and Update access to the secured field to the owner of the record.

public void ShareSecureFieldWithOwner(Entity record)
{
    // Any method that helps you find the AttributeMetadata Id
    var attributeId = FindSecuredAttribute();

    if (attributeId != Guid.Empty)
    {
        var userAccess = new PrincipalObjectAttributeAccess
                             {
                                 AttributeId = attributeId,
                                 ObjectId = record.ToEntityReference(),
                                 PrincipalId = record.OwnerId,
                                 UpdateAccess = true,
                                 ReadAccess = true
                             };

        context.AddObject(userAccess);
        context.SaveChanges();
    }
}

Voilà! You have now a secure field accessible only for the owner of the record!

15 comments:

Charles Prat-Filloz said...

Thank you for this tutorial.

Can you tell me how FindSecuredAttribute() gets the attributeId?

Thank you :)

Tanguy said...

Hi, it just performs a RetrieveAttributeMetadataRequest which uses the entity logical name and the attribute logical name, then read the AttributeId on the resulting AttributeMetadata

Charles Prat-Filloz said...

Thank you for your fast answer!

Charles Prat-Filloz said...

Ok I almost reached what I wanted but I did it differently.

I am using CRM online and I saw that I needed to use entities like that :

Entity poaa = new Entity("principalobjectattributeaccess");
poaa["attributeid"] = attributeId;
poaa["objectid"] = objectIdReference;
poaa["readaccess"] = true;
poaa["updateaccess"] = true;
poaa["principalid"] = principalIdReference;
service.Create(poaa);

I have a problem. I can't create a new principalobjectattributeaccess because the service I use is connected to the user creating the new entity. Or this user does not have the rights to modify PrincipalObjectAttributeAccess. Therefore I have a privilege problem. Do you have any ideas about that?

Tanguy said...

Execute the plugin as an administrator,then no privileges problem

Charles Prat-Filloz said...

OK thank you for everything I managed to do a working plugin thanks to your help!

I have one last question. When I create a Field Security Profile, if I do not connect it with any users or teams, it does not work. I have to explicitly select all users or all teams to make it work.
The thing is that in my solution, if I want to add a new team or user, I also have to add it to the security profile which is a huge constrain, do you have any idea to make it work? Or maybe it should work without connecting the Field Security Profile but my solution is not working properly...

Tanguy said...

No, that's a requirement... But you can also write a plugin that will add the new user or team (post operation create) to this field level security

Charles Prat-Filloz said...

Yes I thought about that solution too but I wanted to be sure I could not do it differently...

Thank you anyway :)

Philip Clark said...

@Charles Prat-Filloz, did you have any luck implementing this in CRM online? I've got the exact same requirement and would appreciate some help if you're willing to share your plugin.

Tanguy said...

This is only supporter SDK code so it works with CRM Online too.

Everything is explained in the post and the comments

Philip Clark said...

Not having any luck implementing it. PrincipalObjectAttributeAccess isn't a recognised type - is there an assembly reference I need in the code?

Tanguy said...
This comment has been removed by the author.
Tanguy said...

Hi Philippe, you have to generate EarlyBound classes with crmsvcutil (included in the SDK) or use class "Entity" instead:

Entity poa = new Entity("principalobjectattributeaccess");

mfairouz said...

Hi,
can you explain how to create the plugins step by step.
Message
Pipeline
Class

appreciate your support

thanks

Skill Quotient said...

Iam really satisfy by your information. It's well-written, to the point, and relative to what I do. I like it very much for giving information on
Sharepoint Online Training | Microsoft Dynamics CRM Online Training