Conditionally Set Permissions on New Documents in SharePoint 2013 Workflow 

Tags: O365, Dev, IT Pro, How To, Admin, SharePoint 2013, SharePoint 2010, SharePoint Designer

Overview

Goal

A business requirement specified setting permissions on newly uploaded documents based on a managed metadata field in an Office 365 SharePoint Document Library. This is significantly easier to do with an OOTB action with a SP 2010 workflow, but we decided to use a SharePoint 2013 workflow for forward compatibility.

Obstacles

There were many, and no one site or post had all the answers, thus this post.

  1. Consuming and executing REST from a SharePoint 2013 workflow.
  2. Waiting for and comparing managed metadata fields on a new document.
  3. Looping through a collection of role assignments and deleting them through workflow REST calls.

First Steps

Allow Site to Make Elevated REST Calls from Workflow

Some of the REST calls require full control. You need administrative rights to enable the feature: Settings > Site Settings > Site Actions - Site Features > Workflows can use app permissions – Activate.

 

Generate and Determine ID(s) of Target Principal(s)

  1. Use the group or person on a test or real item somewhere so that SharePoint generates an ID.
  2. There may be a better way, but I used this REST command in a browser:
    https://mySubDomain.sharepoint.com/sites/contoso/_api/web/siteusers
    From there, you can use a JSON viewer, use the raw returned XML or copy it into a Visual Studio XML file which parses and syntax highlights it nicely.
  3. We need the Id for the target group. In this case my group is spDocumentLevel1 (see the <d:Title> tag) and my Id is 50. Here is a snippet of the returned XML.


<content type="application/xml">

<m:properties>

<d:Id m:type="Edm.Int32">50</d:Id>

<d:IsHiddenInUI m:type="Edm.Boolean">false</d:IsHiddenInUI>

<d:LoginName>c:0-.f|rolemanager|s-1-5-21-1072394957-3547065719-591965004-302694133</d:LoginName>

<d:Title>spDocumentLevel1</d:Title>

<d:PrincipalType m:type="Edm.Int32">4</d:PrincipalType>

 

Workflow Steps

  1. Open the site in SharePoint Designer.
  2. Create the a new SharePoint 2013 workflow against the desired list.
  3. Check "Start the workflow automatically when an item is created".

Wait for Metadata by Waiting for Check in

We set the workflow to start for all new documents, but the workflow starts as soon as the document has been uploaded, before the user has a chance to save the metadata. Since our document library is set to require check out, we can wait for user to check the document in.

Tip: for any SP 2013 workflow against a list or library requiring check out, be sure to clear the "Automatically update the workflow status to the current stage name". Updating the status means updating the metadata of the list item which we want to avoid:

Then we add a Stage to wait for the Checked Out To field to be empty:

 

Checking the Managed Metadata

Creating a Managed Metadata field actually creates a second field, usually with a "_0" appended. This field stores a copy of the lookup data value from the hidden site collection taxonomy list along with the GUID so that it looks something like this: Manual|1304b2eb-d9fa-4885-961f-2763af001bfd

We just want to check that the value is "Manual". The field we want is DocumentType_0

The whole stage looks like this:

The value of 50 comes from the "First Steps" above.

Changing Permissions Set up

So now we need to make some REST calls to the site from our workflow to change the permissions of the new document. The steps are

  1. Break inheritance.
  2. Get all the existing roles for the current item.
  3. Loop through all the roles and delete them one by one.
  4. Set the role(s) that we want.

Before we make the REST calls, we need two different request headers.

  1. Build a dictionary called JSONRequestHeader with two items, Accept and content-type, both with a value of application/json;odata=verbose:

  1. Build a dictionary called JSONDeleteHeader with a single item of X-HTTP-Method with a value of DELETE

 

Make REST Calls

REST calls from the workflow require elevated privileges, so we will use an "App Step" just after the two Build actions.

If the App Step ribbon item is disabled, set the site feature as detailed above.

Break Inheritance

Our first REST call allows custom permissions on the current item using the breakroleinheritance call of the current item. The URI template is

<sitepath>/_api/lists/getbytitle('<list name>')/items(<item ID>)/breakroleinheritance(true)

 

Here are the steps to get to the place in the image below. The String Builder is for setting the reqURL value.

  1. Set the string reqURL variable to the appropriate value using the general template above. See String Builder three images below.
  2. Click Ribbon - Action > Call HTTP Call HTTP Web Service.
  3. Set the URL to reqURL
  4. Change the RequestType to POST and set the RequestHeaders to JSONRequestHeader
    1. Hover over the Call … action
    2. Click the down arrow > properties

    3. Set the values:

       

  5. I recommend logging the responseCode for debug purposes. You may also want to log the actual reqURL before the HTTP call for debugging.

 

Remove the Roles

  1. Create a new step within the app step under the Break Inheritance step named "Remove Roles".
  2. Get all roles in the JSONResults dictionary variable.
    1. Create another step within "Remove Roles" called "Get All Roles".
    2. Copy the all three actions from the "Break Inheritance" step and paste them into the "Get All Roles" step.
    3. The URI template is
      <sitepath>/_api/lists/getbytitle('<list name>')/items(<item ID>)/roleassignments

    This means that we just need to replace breakroleinheritance(true) with roleassignments within the reqURL String Builder.

    1. We need the responseContent
      1. Hover over the Call … action.
      2. Click the down arrow > properties.
      3. Click the ResponseContent dropdown and Create a new variable… called JSONResults.
    2. After the Call … action, add this workflow action: Get "d/results" from JSONResults output to new dictionary variable called allRoles.
    3. Another workflow action: Count items in allRoles output to new variable countRoles.
  3. Set the cursor outside of the Get All Roles step, but within the "Remove Roles" step.
  4. Set a new workflow integer variable called index to 0.
  5. Loop through each Role with an assigned permission
    1. Ribbon – Loop > Loop with condition > Set header to "Remove Each Role"
    2. Loop while index is less than countRoles.
    3. Workflow action: Get d/results(index) from JSONResults output to a new dictionary variable called roleItem.
    4. Workflow action: Get PrincipalId from roleItem output to new integer variable called principalId.
    5. Copy the three steps from Break Inheritance into the Loop and paste them to the bottom of the Loop.
    6. The URI template is
      <sitepath>/_api/lists/getbytitle('<list name>')/items(<item ID>)/roleassignments(<principal Id>)
      Here is the String Builder:
    7. In the Call ... action, change the RequestHeaders property value to JSONDeleteHeader
    8. Workflow action: Calculate index plus 1 output to calc
    9. Workflow action: set index to calc

Note that after loop executes properly, only administrators and processes with elevated privileges such as this workflow (see Trust below), have access to the item.

Set Role

We are granting our group full control permissions which always has a roleDefId of 1073741829 in SharePoint.

  1. Create a new step within the App Step called Set Role.
  2. Paste the three actions from the Break Inheritance step.
  3. The URI template is
    <sitepath>/_api/lists/getbytitle('<list name>')/items(<item ID>)/roleassignments/addroleassignment(principalid=<target principal Id>,roleDefId=1073741829)
    Here is the String Builder:

Finalize and Publish

  1. In the transition stage, Go to End of Workflow
  2. Publish > Accept that App Steps run with different credentials.
  3. Since we are not updating the workflow stages, you may want to remove the workflow column from default document library view.

Grant Full Permission to Workflow

The Break Inheritance REST call works, but Get All Roles is generates a response code of Unauthorized without elevating the permissions for the call. You must be an admin or have full control to perform these steps.

  1. Settings > Site Settings > Users and Permissions – Site app permissions
  2. Copy the client value the App Identifier for the Workflow entry. This is the value between the second pipe symbol | and the @ sign:

    In this case, we want 8be90517-bb48-42c1-87dc-a1d7070b6bae.
  3. Navigate to the Grant permission to an app page, something like https://mySubDomain.sharepoint.com/sites/contoso/_layouts/15/appinv.aspx
  4. Paste the client id in the App Id field > Lookup. The next four text boxes populate with the Title of Workflow, etc.
  5. In the Permission Request XML text box, enter

<AppPermissionRequests>

<AppPermissionRequest

Scope="http://sharepoint/content/sitecollection"

Right="FullControl" />

</AppPermissionRequests>

  1. Click Create > Trust It

Resources

 

Users, groups, and roles REST API reference
How to: Set custom permissions on a list by using the REST interface
Create a SharePoint site using REST in workflow with SharePoint Designer
Looping on List Items in SharePoint Designer Workflow 2013

 
Posted by AndyGett on 31-Aug-14
20 Comments  |  Trackback Url | Bookmark this post with:        
 

Comments


Anna commented on Friday, 20-Feb-2015
I am getting unauthorized response code for break inheritance, any ideas what could be the reason. I am doing this in workflow in Office 365.


Maaz commented on Monday, 16-Mar-2015
I am getting Bad request as response code while trying to beakInheritance,Any Idea what could be the reson??


Ady Gettings commented on Wednesday, 17-Jun-2015
Anna, did you follow the steps for granting full permissions to the workflow? This workflow example was written against Office 365.


Andy Gettings commented on Wednesday, 17-Jun-2015
Maaz, to debug, as noted in step 5 above, log the reqURL. You can use the Fiddler utility to create POST actions to the web site outside of the workflow. Tweak it until the request works in Fiddler and then make the same changes in the workflow.


Morten commented on Wednesday, 1-Jul-2015
Hi Andy First off great post. I having an odd issue, from a functional point of view everything is working fine. I can break inheritance and assign new permission. But the workflow stops just after the "Set permission Http" with an error stating it got the wonrg response format. When I look at the item the change has been made, so i guess it's just the response that's messing with me. I have tried setting a responseheader to the same as the request header but that does not Work :-( Any advise? //M


Andy Gettings commented on Thursday, 2-Jul-2015
Morten,thanks for the feedback. It's great that it works, but frustrating that it does not finish cleanly. You could try logging the reqURL and then investigating in the free Fiddler utility which shows a lot more information about the response. Please let me know if and how you resolve it.


Ram commented on Wednesday, 26-Aug-2015
I'd follow the steps still getting Unauthorized in the Break Inheritance ? Can you please advise ?


Andy Gettings commented on Wednesday, 26-Aug-2015
Ram,I used the Fiddler utility to debug. You may see more information after the breakRoleInheritance(true) call. A likely culprit is that the workflow does not have full trust. Double check the "Grant Full Permission to Workflow " steps above.


Morten commented on Thursday, 12-May-2016
Hi Andy. Sorry for the late reply. I managed to get it to Work by removing the "Content-Type" element in the header. So basically only having the "Accept" did the job :-) //M


Federica commented on Wednesday, 13-Jul-2016
if first call rest set /_api/lists/getbytitle('')/items()/breakroleinheritance(false) you can skip to "Set Role" because the remove roles is automatic.


khaled commented on Monday, 18-Jul-2016
hi Andy response code for the first call logs not found !!!


Andy Gettings commented on Thursday, 21-Jul-2016
Khaled, not sure what you mean by this. If you provide more detail, perhaps we can figure out what is going wrong.


khaled commented on Sunday, 24-Jul-2016
hi andy, can you help to get manager from user profile using call HTTP web service in designer 2013.


khaled commented on Tuesday, 26-Jul-2016
hi andy, can you export your solution to visio and send me the visio file.


Andy Gettings commented on Thursday, 29-Dec-2016
Khaled, what errors are you getting?


Ravi commented on Tuesday, 3-Jan-2017
First time when i edit the document it is showing 220 role count in "GEt All roles" step and the same when again edited showing 7 roles count. Groups are same only first time they have full control and then i am assigning them read.


Andy Gettings commented on Tuesday, 3-Jan-2017
So is it a success for you Ravi?


Ravi commented on Tuesday, 3-Jan-2017
First time when i edit the document it is showing 220 role count in "GEt All roles" step and the same when again edited showing 7 roles count. Groups are same only first time they have full control and then i am assigning them read.


Ravi commented on Wednesday, 4-Jan-2017
Yes Andy, but my concern is why i am getting count 220 when workflow is ran first time to remove the default groups and later it is showing count as 7 when workflow is ran 2nd time on the same item. Basically both the time the groups are the same.So why count is different? First time as the count is 220, it is taking time to loop through 220 and remove them? Please let me know if my question is not clear


Andy Gettings commented on Wednesday, 4-Jan-2017
Ravi, you might try logging the details of each role as it is removed see what the extra roles are. I suspect that there are extra roles that SharePoint creates by default.

Name:
URL:
Email:
Comments: