To assign permissions in SharePoint, you make one or more role assignments, which requires three things:
- Some kind of handle for a securable object. That’s basically a site, list, library, folder, document, or item.
- The principal id for something to which roles can be assigned. That’s either an Active Directory user or security group, or a SharePoint group.
- The id of a role definition. Like ‘Full Control’ or ‘Edit’ or ‘Contribute’. This is basically a named collection of granular permissions that are defined at the site collection root and can be assigned to a securable object in that site collection.
In this post, I’m going to explain the REST service calls required in order to make role assignments to SharePoint securable objects. I will show the calls using jQuery’s ajax (because I’m working through them in the console and the console won’t resolve promises). I’ll follow up with a post with some demo code pulling it all together and probably using fetch.
Role Assignments: Prerequisites
As explained above, there are three things I need to make a role assignment, and while these aren’t directly related to role assignments, this is a series on REST in general so I’m going to explain all of the service calls.
The first thing I will get is collection of lists that are available in the current site, via the endpoint /_api/web/lists. Now this endpoint basically returns the whole list schema, and I don’t need all of that, so to reduce the payload I’ll add $select=Id,Title as a request parameter. I also want to weed out hidden lists, so I’ll add $filter=Hidden eq false. With that, the call looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists?$select=Id,Title&$filter=Hidden eq false"; $.ajax({ url: url, type: 'GET', headers: { 'accept': 'application/json;odata=nometadata' }, success: function(data) { console.log(data); }, error: function(error) { console.log(error); } }); |
And the returned JSON structure looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | data = { value: [ { Id: "6cf00975-3daa-4510-bc93-6c4f8be8da7f", Title: "JobTitles" }, { Id: "ad9fccd3-93ef-4c56-8d97-85a3179397ad", Title: "Pictures" }, { Id: "7127dc6e-739a-4b28-a8f6-2d140838c11a", Title: "Promoted Links" }, { Id: "5286bc76-8239-468b-81a1-befcb4499e67", Title: "SalesDivision" }, { Id: "7c72b15c-8dac-4528-9544-f72e4b6329e3", Title: "SalesRegion" }, { Id: "54bff8cc-6585-4890-9870-3b5b5e64ba6a", Title: "SalesState" }, { Id: "86a21b2e-326c-4ec2-af53-a5004fdb09d8", Title: "Shared Documents" }, { Id: "444442b4-6560-4716-8b7e-b70f16b2915c", Title: "Speaker Evaluations" } ] } |
I’ll use this data to populate some sort of multi-select control for lists and can then get a handle on each list by either id or title.
Site Groups
Next, I’ll need to populate a drop-down list with site collection groups. Again, I’ll add the request parameter $select=Id,Title, and while with lists I could have gotten away with just title, here I need both because add role assignment requires the id, and users are likely going to need the title. Here’s the call:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/sitegroups?$select=Id,Title"; $.ajax({ url: url, type: 'GET', headers: { 'accept': 'application/json;odata=nometadata' }, success: function(data) { console.log(data); }, error: function(error) { console.log(error); } }); |
And the response JSON structure looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | data = { value: [{ Id: 9, Title: "CSRDemos Members" }, { Id: 7, Title: "CSRDemos Owners" }, { Id: 8, Title: "CSRDemos Visitors" }, { Id: 3, Title: "Excel Services Viewers" } ] }; |
I’m not actually going to need this in the demo page, but to be thorough, if you know the name of the group, you can get it’s ID with this call (a lot less chatty):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/sitegroups/getbyname('Excel Services Viewers')/id"; $.ajax({ url: url, type: 'GET', headers: { 'accept': 'application/json;odata=nometadata' }, success: function(data) { console.log(data); }, error: function(error) { console.log(error); } }); |
And the response from this call is quite succinct:
1 2 3 | data = { value: 3 }; |
Role Definitions
And the final preliminary piece of the puzzle is that I need a role definition id. To get a list of role definitions defined in the site collection, I make the following call:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/roledefinitions"; $.ajax({ url: url, type: 'GET', headers: { 'accept': 'application/json;odata=nometadata' }, success: function(data) { console.log(data); }, error: function(error) { console.log(error); } }); |
Now I didn’t actually select anything, so I’m getting back more information than I actually need, but it’s not that big a structure and I wanted to show the whole thing. In particular, note the base permissions structure. I talked about this a bit in my last post Determining the Permissions of the Current User with REST, and how to dissect this structure to get the granular access controls it represents. Anyway, here is the complete JSON structure returned from the role definitions endpoint.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | data = { value: [{ BasePermissions: { High: "2147483647", Low: "4294967295" }, Description: "Has full control.", Hidden: false, Id: 1073741829, Name: "Full Control", Order: 1, RoleTypeKind: 5 }, { BasePermissions: { High: "432", Low: "1012866047" }, Description: "Can view, add, update, delete, approve, and customize.", Hidden: false, Id: 1073741828, Name: "Design", Order: 32, RoleTypeKind: 4 }, { BasePermissions: { High: "432", Low: "1011030767" }, Description: "Can add, edit and delete lists; can view, add, update and delete list items and documents.", Hidden: false, Id: 1073741830, Name: "Edit", Order: 48, RoleTypeKind: 6 } ] } |
The only things I actually need from this are the name and the id.
If you know the name of the role definition you’re interested in, you can get the id with the following REST call:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/roledefinitions/getbyname('Edit')/id"; $.ajax({ url: url, type: 'GET', headers: { 'accept': 'application/json;odata=nometadata' }, success: function(data) { console.log(data); }, error: function(error) { console.log(error); } }); |
And the very simple returned JSON structure from this call looks like:
1 2 3 | data = { value: 1073741830 }; |
Manipulating Role Assignments
Whew! We finally have enough information to make a role assignment. It’s a bit tedious, but not that hard (which kind of describes programming in general). In the following code, I’m going to work on the permissions of the list titled “Speaker Evaluations”. And prior to doing anything, the permissions for that list look like this:
The first thing I need to do is check if the list is currently inheriting permissions. To do that, just like object model code, I need to call has unique role assignments like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // has unique role assignments url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Speaker Evaluations')/hasuniqueroleassignments"; $.ajax({ url: url, type: 'GET', headers: { 'accept': 'application/json;odata=nometadata' }, success: function(data) { console.log(data); }, error: function(error) { console.log(error); } }); |
Keep in mind that the part of the URL before /hasuniqueroleassignments is what I called earlier “a handle to a securable object”, in this case, a list. So I could just as easily use /_api/web/hasuniqueroleassignements, and the returned value would be in exactly the same format but would have told me if the web had broken inheritance. And I could do …/items(2)/hasuniqueroleassignments to determine if the item with id 2 has broken role inheritance. The same is true of all of the endpoints to follow in this post, they can all be tacked onto any URL that represents a securable object to perform securable operations on those objects.
Anyway, here is the returned value, which would be true if role inheritance had already been broken:
1 2 3 | data = { value: false }; |
If the list is currently inheriting permissions, I now need to break role inheritance. I do that with the following call, passing in false. The input is true if I want to copy all of the role assignments from the parent and false if I want to start with a blank slate.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // break role inheritance url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Speaker Evaluations')/breakroleinheritance(false)"; $.ajax({ url: url, type: 'POST', headers: { 'accept': 'application/json;odata=nometadata', 'X-RequestDigest': $('#__REQUESTDIGEST').val() }, success: function(data) { console.log(data); }, error: function(error) { console.log(error); } }); |
Which returns the terribly useful JSON structure shown below. Basically, if success gets called back, that’s enough to shout WOO HOO!
1 2 3 | data = { "odata.null": true } |
And if I re-check the permissions for my list it now looks like this:
Yours will look a little different of course. I wouldn’t expect you to see permissions assigned to Joe McShea for instance ;). Breaking role inheritance with false just assigns full control to the current user to prevent orphaned objects.
But calling break role inheritance on an object that already doesn’t inherit does nothing. Even if you pass in false, it certainly doesn’t delete any previously copied role assignments from the parent. That’s why I had to check has unique role assignments, because if not then I call the above service, and if so then I call the following service.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // delete all role assignements for CSRDemos Members, 404 if none url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Speaker Evaluations')/roleassignments/getbyprincipalid('9')"; $.ajax({ url: url, type: 'POST', headers: { 'accept': 'application/json;odata=nometadata', 'X-RequestDigest': $('#__REQUESTDIGEST').val(), 'X-HTTP-Method': 'DELETE' }, success: function(data) { console.log("'" + data + "'"); }, error: function(error) { console.log(error); } }); |
This call just deletes all role assignments for the user I’m about to add role assignments for. That way, at least with respect to this one user, I always start with a clean slate. Curiously, this call returns nothing on success, just a blank string. not even a lousy { “object.null”: true }! One thing you need to know, however, is if the user has no role assignments, the result is a “404 Not Found”. This isn’t an error. You asked for a resource and it wasn’t found. So you should handle 404 errors as appropriate.
1 | data = '' |
And finally, we’ve arrived at the point of this post, which is making a role assignment. The following service call adds a role assignment to the “Speaker Evaluation” list, which assigns Edit (i.e. roledefid=’1073741830′) to the SharePoint group “CSRDemos Members” (i.e. principalid=’9′).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // assign the Edit role to CSRDemos Members url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Speaker Evaluations')/roleassignments/addroleassignment(principalid='9',roledefid='1073741830')"; $.ajax({ url: url, type: 'POST', headers: { 'accept': 'application/json;odata=nometadata', 'X-RequestDigest': $('#__REQUESTDIGEST').val() }, success: function(data) { console.log(data); }, error: function(error) { console.log(error); } }); |
And again we see this very useful structure. But again, the fact that the success callback was called is more than enough.
1 2 3 | data = { "odata.null": true } |
And now if I check the permissions for the list, I see:
Sum Up
In this post I showed the various pieces you need to navigate in order to assign roles to SharePoint groups using the REST API. In my next post, I’ll pull it all together with a demo page.
Reference
Set custom permissions on a list by using the REST interface – Microsoft Docs
Hi,
I am facing issue with ‘X-RequestDigest’: $(‘#__REQUESTDIGEST’).val().
what is mean by ‘X-RequestDigest’: $(‘#__REQUESTDIGEST’).val() in header
Hi Ganesh,
So SharePoint Pages generally have a hidden control with an id of __REQUESTDIGEST. If you view source on a SharePoint page and search for that ID, you should see the control. It’s just some kind of hash, that you have to send back to the server in the X-RequestDigest header in order to do any write operations (i.e. POST, PUT, MERGE, or DELETE), if you’re not using OAuth.
JavaScript that runs on a SharePoint page doesn’t need to do OAuth since the user has already authenticated in the browser, so it can just use the __REQUESTDIGEST value of the current page like I am.
$("#__REQESTDIGEST").val()
is using jQuery to get that digest value. Without jQuery, you can use pure JavaScript something likedocument.getElementById("__REQUESTDIGEST").value
. Ifdocument.getElementById("__REQUESTDIGEST")
returns undefined then you’re either not on a SharePoint page, or the page doesn’t use a request digest.And if you’re on a SharePoint page, meaning the user has already authenticated to SharePoint, but the page doesn’t have a control with an ID of __REQUESTDIGEST, then there is another way to get the request digest, which is to call the context info REST service to retrieve the digest, with a url like:
http://[site url]/_api/contextinfo
The digest serves a couple of purposes, including making sure your version of the item isn’t stale, so it expires from time to time (I believe 30 minutes by default). So if you try to use a stale digest, you’ll get an error like “An error has occurred with XXXX. Please refresh the page and try again”. In which case, you can use the context info service to refresh the digest.
Now if you’re not on a SharePoint page, then you probably have to use OAuth and worry about CORS (Cross Origin Resource Sharing), which is a whole different can of worms, and not what my post is about.
You can find a lot more info about this here:
Complete basic operations using SharePoint REST endpoints
That page has some information on the different contexts you might be in, like on a SharePoint page vs. using OAuth etc.
Hope that helps some.
Joe
Hello Joe,
First of all, thank you for this post, it’s very helpful.
I’d like to ask you if you know how to remove an external user from a specific folder/file.
I granted access to a folder with the “SP.Web.ShareObject” rest call method which accepts a “peoplePickerInput” body parameter (where you can define all the email addresses to invite), but the method “SP.Web.UnshareObject” only accepts the url parameter of the folder, so every external user will lose the access, which it’s not what I need.
Do you have any solution for this ?
Thanks in advance.
Liev
Hi Liev,
I do not have any particular insight on that, but I’ll take a look when I get a chance, and let you know if I figure it out.
Joe