Custom Workflow Activity for Granting Permissions on a SharePoint Site

In this post I'll take a look at how to build a custom workflow activity in Visual Studio 2012 that grants permissions on a SharePoint 2013 site to a user or group.

Note: this is part of a series of posts on building workflow activities to manage sites, groups, users and permissions. For a complete list of posts, together with a more detailed walkthrough of how to build a custom workflow activity and make it available in SharePoint Designer, start at the beginning.


Fundamentals

You can use the REST API to grant permissions to a SharePoint principal (i.e. a user or group). There are two stages to the process.

Stage 1: Get the Role Definition ID
Before you can grant permissions to a principal, you need to know the ID of the role definition (i.e. the permission level) you want to assign to the principal. Built-in role definitions have a ten-digit ID value. For example:
  • The ID of the Full Control role definition is 1073741829.
  • The ID of the Contribute role definition is 1073741827.
  • The ID of the Read role definition is 1073741826.
However, maintaining a list of these IDs is pretty inconvenient. Also, if you want to use a non-standard role definition, you may not know the ID. When you're building a workflow, it's much easier if you can specify the name of the role definition you want rather than the integer ID. Fortunately, getting a role definition by name is straightforward with the REST API - you need to send a web request that resembles the following:

Endpoint:
{site collection URL}/_api/web/roledefinitions/getbyname('{name of role definition}')

HTTP method:
GET

Headers:
Accept: application/json; odata=verbose
Content-Type: application/json; odata=verbose

The REST service will send a response that resembles the following:
{"d":{
   "__metadata":{
      "id":"http://team.jason.net/_api/Web/RoleDefinitions(1073741829)",
      "uri":"http://team.jason.net/_api/Web/RoleDefinitions(1073741829)",
      "type":"SP.RoleDefinition"
   },
   "BasePermissions":{
      "__metadata":{
         "type":"SP.BasePermissions"
      },
      "High":"2147483647",
      "Low":"4294967295"
   },
   "Description":"Has full control.",
   "Hidden":false,
   "Id":1073741829,
   "Name":"Full Control",
   "Order":1,
   "RoleTypeKind":5
}}

As you can see, you can get the ID value you want by retrieving the "d/Id" property from this response.

Stage 2: Add a New Role Assignment
The REST API includes an endpoint on every securable object that enables you to add a role assignment by specifying the integer principal ID and the integer role definition ID. Unlike server-side or client-side programming, you don't need to worry about creating role definition bindings - the service will take care of this for you behind the scenes. To add a new role assignment on an SPWeb, you need to send a web request that resembles the following:

Endpoint:
{site collection URL}/_api/web/roleassignments/addroleassignment(principalid={principal ID}, roledefid={role definition ID})

HTTP method:
POST

Headers:
Not required

Body:
Empty

To create a workflow activity that grants permissions, I first built a utility activity (Get Role Definition ID) that retrieves the ID value of a role definition with a specified name. I then consume this utility activity within a primary activity (Grant Web Permissions) that adds a new role assignment to the specified site. I'll run through both of these activities in the rest of this post.

The "Get Role Definition ID" Activity

I'm now on post number five of this series of posts on creating workflow activities, so I'll keep this as concise as possible - please refer back to earlier posts if you want a bit more detail on the process. I'll start by defining the arguments for the Get Role Definition ID activity:










In this case, I want the consumer of the workflow activity to provide the name of the role definition. The workflow activity will return the corresponding ID of the role definition, together with the status code returned by the REST service.

Next, I'll define the variables I want to use within my activity:













Here, I've got a variable to store the URL of the current site collection, a variable to store the REST endpoint we want to use, and variables to store the headers, content and status code returned by the service call.

The complete activity looks like this:








































Let's take a brief look at each of these child activities - I've covered these activity types in detail in previous posts, so I'll try to keep this concise.

Get Current Site URL
The first thing we need to do is to grab the URL of the current site and store it in our siteUrl variable. I've used a LookupWorkflowContextProperty activity to do this:















Build the REST URL
The next task is to build the URL for our REST service call, using our site URL, the site-relative REST endpoint, and the role definition name provided by the activity consumer:














HttpSend
Next, we use an HttpSend activity to make the REST API call:

























Assign responseStatusCodeOut
In this activity, we convert the response status code returned by the REST service into a SharePoint Designer-friendly string format:














Get ID from Response
In this final task, we use a GetDynamicValue<T> activity to extract the ID of the role definition from the JSON response content returned by the REST service:














Build the Actions File

To finish off the Get Role Definition ID activity, we need to edit the .actions4 file. In this case, the file should resemble the following:
<Action Name="Get Role Definition ID" ClassName="SiteManagementActivities.GetRoleDefinitionId" Category="Site Management" AppliesTo="all">
  <RuleDesigner Sentence="Get the integer ID of the role definition %1 (Output: %2 %3)">
    <FieldBind  Field="roleDefinitionName" Text="Role Definition Name" Id="1" />
    <FieldBind  Field="roleDefIdOut" Text="Role Definition ID" Id="2" />
    <FieldBind  Field="responseStatusCodeOut" Text="Response Status Code" Id="3" />
  </RuleDesigner>
  <Parameters>
    <Parameter Type="System.String, mscorlib" Direction="In" Name="roleDefinitionName" />
    <Parameter Type="System.Int32, mscorlib" Direction="Out" Name="roleDefIdOut" />
    <Parameter Type="System.String, mscorlib" Direction="Out" Name="responseStatusCodeOut" />
  </Parameters>
</Action>

Now the Get Role Definition ID helper activity is complete, we can build our primary activity that makes use of it.

To use this helper activity within another activity, we need it to show up in the Visual Studio toolbox. The toolbox gets its activities from the SharePoint site you're using for debugging. As such, you'll need to deploy this activity (Start Without Debugging) and then close and reopen Visual Studio. You should then see the activity in the toolbox when you're building your next activity.

The "Grant Web Permissions" Activity

In this activity, we're going to add a role assignment to the web specified by the activity consumer. The role assignment will specify the integer ID of the principal to whom we want to grant permissions (could be a user or a SharePoint group), together with the integer ID of the permission level we want to assign. As before, let's start by defining the arguments:












In this case, we need the activity consumer to provide:
  • The URL of the web on which they want to grant permissions.
  • The login name (title or email will also work) of the principal to whom they want to grant permissions.
  • The name (e.g. "Full Control", "Contribute", "Read") of the permission level (aka role definition) they want to assign.
The workflow activity will return the response status code returned by the REST API call.

Next, let's define our activity variables:














Here we've got a variable to store our REST endpoint, variables to store the integer IDs for the principal and the role definition, and variables to store responses from the REST API call.

The complete activity looks like this:








































Let's take a brief look at each of these child activities.

Get Principal ID
The first thing we need to do is to get the integer ID of the principal to whom we want to grant permissions, using the login name provided by the activity consumer. I've used a LookupSPPrincipalId activity to do this:
















Get Role Definition ID
Next, we need to get the integer ID of the role definition we want to assign to the principal, using the permission level name provided by the activity consumer. Here, I've used the GetRoleDefinitionId helper activity that we defined in the first half of this post:
















Assign (build restUrl)
We've now got all the information we need to build our REST URL. I've used an Assign activity to do this:














The full value for the restUrl variable is as follows:
String.Format("{0}/_api/web/roleassignments/addroleassignment(principalid={1}, roledefid={2})", webUrl, principalId, roleDefinitionId)


HttpSend
Next, we send our REST request to the server:






















Here, we're sending an HTTP POST request with an empty body to the REST URL we defined in the previous step. We're also assigning the headers, content and status code returned by the service to local variables. At this stage you'll also need to configure your request headers by clicking the ellipsis button in the RequestHeaders row:

















Assign responseStatusCodeOut
In this final activity, we convert the response status code returned by the REST service into a SharePoint Designer-friendly string format as before:














Build the Actions File

To finish off the Grant Web Permissions activity, we need to edit the .actions4 file. In this case, the file should resemble the following:
<Action Name="Grant Web Permissions" ClassName="SiteManagementActivities.GrantWebPermissions" Category="Site Management" AppliesTo="all">
  <RuleDesigner Sentence="Grant the permission level %1 to user or group %2 on the SPWeb %3 (Output: %4)">
    <FieldBind  Field="permissionLevel" Text="Permission Level Name" Id="1" />
    <FieldBind  Field="principalLoginName" Text="Principal Login Name" Id="2" />
    <FieldBind  Field="webUrl" Text="SPWeb URL" Id="3" />
    <FieldBind  Field="responseStatusCodeOut" Text="Response Status Code" Id="4" />
  </RuleDesigner>
  <Parameters>
    <Parameter Type="System.String, mscorlib" Direction="In" Name="permissionLevel" />
    <Parameter Type="System.String, mscorlib" Direction="In" Name="principalLoginName" />
    <Parameter Type="System.String, mscorlib" Direction="In" Name="webUrl" />
    <Parameter Type="System.String, mscorlib" Direction="Out" Name="responseStatusCodeOut" />
  </Parameters>
</Action>

Once you've deployed the solution, activated the feature and cleared the SharePoint Designer cache, the Grant Web Permissions activity should be available for use in SharePoint Designer workflows. Behind the scenes, the Grant Web Permissions activity will make use of the Get Role Definition ID helper activity to get the integer ID for a specified role definition name.

Update 17/11/14: a sample Visual Studio solution containing these workflow activities is now available for download.

Comments

  1. How do you get a custom activity to show in the Visual Studio toolbox? After deploying, the custom activity shows in Designer but not in Visual Studio.

    ReplyDelete
  2. Hi Srallen - you need to deploy the solution and activate the feature on the SharePoint site that Visual Studio is using for debugging, and then restart Visual Studio.

    ReplyDelete
  3. Followed your instructions step by step. I deployed and activated the feature. It works great in Designer. Restarted Visual Studio, started a new custom activity for the same site and the deployed activity does not show in the toolbox.

    ReplyDelete

Post a Comment

Popular posts from this blog

Server-side activities have been updated

The target principal name is incorrect. Cannot generate SSPI context.

Custom Workflow Activity for Creating a SharePoint Site