Share-n-dipity

SharePoint serendipity is the effect by which one accidentally discovers something fortunate, especially while looking for something else entirely. In this case, it is the occassional musings, observations, and Ouija board readings about the phabulously

Using the SharePoint 2010 Client Object Model - Part 5

Using the SharePoint 2010 Client Object Model - Part 5

  • Comments 5
  • Likes

The previous posts in this series have been pretty list-centric.  In this posting we’re going to delve down a different path in exploring the client object model and talk about security.  The client OM actually provides great support for working with the security model in SharePoint.  You can do everything from getting a list of groups and members to enumerating roles (permissions levels), the rights associated with them, creating new users and groups, breaking inheritance, and more.  Let’s go ahead and start with the most straightforward scenarios and start working our way down the list.

We’ll start out by getting a list of groups in the site:

//get the connection

ClientContext ctx = new ClientContext("http://foo");

               

//get the groups

GroupCollection grps = ctx.Web.SiteGroups;

 

//load up the group info

ctx.Load(grps);

//execute the query

ctx.ExecuteQuery();

 

// enumerate

foreach (Group grp in grps)

{

// do something with each group

}

So we’re following the pattern we’ve seen throughout in these posts – getting our connection, creating our query and executing the query.  The SiteGroups property is a simple enumeration that’s easy to work with.    If we wanted to get the members of that group, we could just add this additional code while we enumerate each group:

UserCollection usrs = ctx.Web.SiteGroups.GetById(grp.Id).Users;

ctx.Load(usrs);

ctx.ExecuteQuery();

Side note:  I’ll cover a more efficient way in which to deal with object properties like this below.  Getting the list of permissions levels, also known as RoleDefinitions is equally easy:

RoleDefinitionCollection roleDefs = null;

roleDefs = ctx.Web.RoleDefinitions;

ctx.Load(roleDefs);

And then we call ExecuteQuery and it works just like enumerating the SiteGroups as shown above.   Now, figuring out the rights associated with an individual RoleDefinition is a little different.  Assume that we’re enumerating through the list of RoleDefinitions.  For each one we need to also enumerate through all the possible BasePermissions and call the Has method on the BasePermissions class to see if that BasePermission is part of that RoleDefinition.  The code for doing that would look something like this:

foreach (RoleDefinition rd in roleDefs)

{

      //enumerate the enum and check each permission

//type to see if the perm is included

      string[] keys = Enum.GetNames(typeof(PermissionKind));

 

      //load the base permissions

//more efficient pattern demonstrated

//later in this post

      ctx.Load(rd.BasePermissions);

      ctx.ExecuteQuery();

 

      //get a reference to the base permissions

      //in this RoleDefinition

      BasePermissions bp = rd.BasePermissions;

 

      //enumerate the enum

      foreach (string key in keys)

      {

if (bp.Has((PermissionKind)Enum.Parse(typeof(PermissionKind),

      key)))

{

      //do something

}

      }

}

Okay, so what if we want to see the rights associated with a list or item?  In that case it’s slightly different – instead of working with a collection of RoleDefinitions we work with a collection of RoleAssignments.  Here’s an example of just such a thing for a List:

//get the list

List curList = ctx.Web.Lists.GetByTitle("My List Title");

 

//load the RoleAssignments in the list

ctx.Load(curList.RoleAssignments)

 

//execute query

ctx.ExecuteQuery();

 

//enumerate results

foreach(RoleAssignment ra in curList.RoleAssignments)

{

      //do something

}

 

Retrieving the permissions on an item is essentially exactly the same:

 

ListItem curItem =

ctx.Web.Lists.GetByTitle("My List Title").GetItemById(3);                       

 

//load the RoleAssignments in the item

ctx.Load(curItem.RoleAssignments)

 

//execute the query and enumerate the RoleAssignments collection

//not shown here, see the list example

 

There are a couple of interesting twists here to consider.  The first is, what if I want to reuse as much of this code as possible and not write all this stuff twice – once for a List and once for a ListItem?  Also, one of the more interesting properties of the RoleAssignment is the Member.  The Member property is an object of type Principal and includes things like the Id, PrincipalType and Title.  As you’ve probably noticed by now, having the Id for items is pretty valuable because many collections have a GetById or GetItemById method to retrieve an individual member of the collection.

 

Let’s talk about the second problem first – getting the values for a property that is an object property (rather than a value property, like a string, int, etc.).  The simplistic way to deal with the problem would be to say okay, let’s load up our collection of RoleAssignments.  As we enumerate each one we’ll retrieve the Member property.  This is definitely doable and would look something like this (borrowing from our example above):

 

//enumerate results

foreach(RoleAssignment ra in curList.RoleAssignments)

{

      Principal p = ra.Member;

      ctx.Load(p);

      ctx.ExecuteQuery();

      //do something with p

}

 

Okay, yeah, it works, but look at the amount of network traffic and server requests we’re generating – one call across the network and one hit on the server for every single RoleAssignment we have in the collection.  Using some of the crazy LINQ syntax that I’m sure we’ll all become increasingly familiar with though, you can retrieve both the RoleAssignment and the Member, as well as the RoleDefinitionBindingsCollection all at once.  Here’s what that LINQ syntax looks like:

 

IEnumerable<RoleAssignment> roles = null;

 

//our query code

roles = ctx.LoadQuery(

curList.RoleAssignments.Include(

      roleAsg => roleAsg.Member, 

      roleAsg => roleAsg.RoleDefinitionBindings.Include(

roleDef => roleDef.Name, // for each role def, include roleDef's Name

      roleDef => roleDef.Description)));

Now when we enumerate the RoleAssignments collection we have all the details on the RoleAssignment, as well as the values we care about for our object property.  This is pretty slick.  Now to address the first part of the problem posed above – I want to get better code reuse.  While the LINQ statement above is interesting, the last thing I want to do is to write it twice and try to keep it in sync to retrieve the RoleAssignment info for both a List and ListItem.  Well the good news is that both of these classes inherit from SecurableObject.  So, we can modify our code above slightly to use it like this:

 

SecurableObject curObj = null;

 

if (someBooleanThatSaysUseAListIsTrue)

{

      //get the list

curList = ctx.Web.Lists.GetByTitle("My List Title");

 

      //do whatever I'm gonna do with the list

 

      //plug it into our query object

      curObj = curList as SecurableObject;

}

else

{

      //get the list item

ListItem curItem =

ctx.Web.Lists.GetByTitle("My List Title").GetItemById(3);                       

 

      //do whatever I'm gonna do with the item

 

      //plug it into our query object

      curObj = curItem as SecurableObject;

}

 

//our query code

roles = ctx.LoadQuery(

curObj.RoleAssignments.Include(

      roleAsg => roleAsg.Member, 

      roleAsg => roleAsg.RoleDefinitionBindings.Include(

roleDef => roleDef.Name, // for each role def, include roleDef's Name

      roleDef => roleDef.Description)));

//execute the query

ctx.ExecuteQuery();

And there we go, we just solved two problems with one fairly nice chunk of code.  Now a few quick hit tasks.  Want to create a user and add them to a SiteGroup:

//get the connection

ClientContext ctx = new ClientContext("http://foo");

 

//get the group

Group grp = ctx.Web.SiteGroups.GetById(MyGroupId);

 

//create the user object

UserCreationInformation usr = new UserCreationInformation();

usr.LoginName = "foo\steve";

usr.Email = "steve@foo.com";

usr.Title = "Microsoft Human";

 

//add it to the group

grp.Users.Add(usr);

 

//execute the query to add the user

ctx.ExecuteQuery();

Okay, how about creating a new Role (i.e. Permission Level):

//get the connection

ClientContext ctx = new ClientContext("http://foo");

 

//create a new base permission

BasePermissions perms = new BasePermissions();

 

//left as an exercise for the reader to decide what

//perms there are and which ones you want to add to

//your custom role.  For each perm you want to add

//you would do something like this (assuming that "p" is a string

//that corresponds to a BasePermission enum name                 

perms.Set((PermissionKind)Enum.Parse(typeof(PermissionKind), p));

 

//create the construct for a new role definition

RoleDefinitionCreationInformation rdInfo = new

      RoleDefinitionCreationInformation();

 

//set the perms

rdInfo.BasePermissions = perms;

 

//set a description

rdInfo.Description =

"A role definition created with the client object model";

 

//set the name

rdInfo.Name = "Your Custom Role";

 

//add the definition to the web collection

RoleDefinition rd = ctx.Web.RoleDefinitions.Add(rdInfo);

 

//execute to create

ctx.ExecuteQuery();

Cool.  Moving right along, add a user to a Role:

//get the connection

ClientContext ctx = new ClientContext("http://foo");

 

//get the group

RoleDefinition rd =

      ctx.Web.RoleDefinitions.GetByName("Your Custom Role ");

 

//get the user object

Principal usr =

ctx.Web.EnsureUser("foo\steve");

 

//create the role definition binding collection

RoleDefinitionBindingCollection rdb = new

RoleDefinitionBindingCollection(ctx);

 

//add the role definition to the collection

rdb.Add(rd);

 

//create a RoleAssigment with the user and role definition

ctx.Web.RoleAssignments.Add(usr, rdb);

 

//execute the query to add everything

ctx.ExecuteQuery();

We’re on a “role” now.  Ha ha, couldn’t resist (it’s late Friday night for those of you wondering).  How about creating a group and adding it to a role?

//get the connection

ClientContext ctx = new ClientContext("http://foo");

 

//get the group

RoleDefinition rd =

      ctx.Web.RoleDefinitions.GetByName("Your Custom Role");

 

//create the group

GroupCreationInformation grpInfo = new GroupCreationInformation();

grpInfo.Title = "Your New Group";

grpInfo.Description = "A custom group created with the client object model";

 

//add it to the list of site groups

Group grp = ctx.Web.SiteGroups.Add(grpInfo);

 

//create the role definition binding collection

RoleDefinitionBindingCollection rdb = new

      RoleDefinitionBindingCollection(ctx);

 

//add the role definition to the collection

rdb.Add(rd);

 

//create a RoleAssigment with the group and role definition

ctx.Web.RoleAssignments.Add(grp, rdb);

 

//execute the query to add everything

ctx.ExecuteQuery();

And last but not least, let’s break inheritance on a list and add a user to a role on that item:

//get the connection

ClientContext ctx = new ClientContext("http://foo");

 

//get the group

RoleDefinition rd = ctx.Web.RoleDefinitions.GetByName("Your Custom Role");

 

//get the list

List theList = ctx.Web.Lists.GetByTitle("My List Title");

 

//break the inheritance

theList.BreakRoleInheritance(true);

 

//create the role definition binding collection

RoleDefinitionBindingCollection rdb = new

      RoleDefinitionBindingCollection(ctx);

 

//add the role definition to the collection

rdb.Add(rd);

 

//get the user object

Principal usr =

ctx.Web.EnsureUser("foo\steve");

 

//*************************************************************

//NOTE:  IF WE WANTED TO REMOVE A USERS'S PERMISSIONS TO A LIST

//OR ITEM OUR CODE WOULD BE LIKE THIS:

//theList.RoleAssignments.GetByPrincipal(usr).DeleteObject()

               

//*************************************************************

 

//create a RoleAssigment with the group and role definition

theList.RoleAssignments.Add(usr, rdb);

 

//execute the query to add everything

ctx.ExecuteQuery();

Whew!  That’s it.  It was a whole lot of code but a ton of fun in the process.  Hopefully you again got a feel for the patterns and common classes involved when managing security through the client object model.  You’ll find it quite powerful for managing your site. 

 

Coming Next…

We’ve covered a lot of ground so far in this series, but we’ve got one last stop – managing web parts using the client object model.  That will be the focus of the next and final posting in this series.

Comments
  • Really good artical.

    Although reading BasePermissions for given roledefinition

    ctx.Load(rd.BasePermissions);

    Fails with error

    The type 'Microsoft.SharePoint.Client.BasePermissions' cannot be used as type parameter 'T' in the generic type or method 'Microsoft.SharePoint.Client.ClientRuntimeContext.Load<T>(T, params System.Linq.Expressions.Expression<System.Func<T,object>>[])'. There is no implicit reference conversion from 'Microsoft.SharePoint.Client.BasePermissions' to 'Microsoft.SharePoint.Client.ClientObject'

    Could you shade some light on this.

    Thanks in Advance,

    Rupali Kothawade

  • Hi all,

    We have figured out the problem.

    Above Code has a small bug:

    Reading base permissions for the gievn RoleDefintion using ctx.Load(rd.BasePermissions); failes.

    Although changing it to ctx.Load(rd); has solved the problem.

    Hope it helps others.

    -Rupali Kothawade

  • good article. i am trying to check for a list item permission for a specific user.

    Principal member = roleAssignment.Member;

                               if ((member != null) && (member is User))

                               {

                                further action here

                               }

    but member is User is always false. is my statement correct?

  • Hi Sir,

    i am Adding a User to the currently existing Site Group, which is adding the user to the respective site group. but the role definitions are not getting copied to the the user,

    i mean if there is a Group Called SiteOwner and User1(myself) added to the Group

    my record is Existed in the Group but i m not able to Access that Group

    Here Is My Code:

                  string strUserId = userModel.UserID;

                   int grpId = userModel.groupId;

                   ClientContext clientContext = GetPortalContext(portalName);

                   Web web = clientContext.Web;

                   //User user = web.EnsureUser(strUserId);

                   User user = web.EnsureUser(strUserId);

                   Microsoft.SharePoint.Client.Group testingOwnersGroup = web.SiteGroups.GetById(grpId);

                   UserCollection userCollection = testingOwnersGroup.Users;

                   userCollection.AddUser(user);

                   clientContext.Load(userCollection);

                   clientContext.ExecuteQuery();

    Regards

    Jay

  • Thank you

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment