1. Using the SharePoint 2010 Client Object Model - Part 5
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 = "[email protected]";
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).Delete Object()
//************************************************** ***********
//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.