Managing Role Assignments/Permissions with SharePoint REST Part2

In my last post I described many of the REST endpoints available in SharePoint to manage role assignments. In this post, I will provide a concrete example of using these endpoints in a provisioning-like scenario. I say provision-like because real provisioning scenarios tend to be very specific and one-offs (i.e. I need 7 sites, each with 5 lists and 3 groups, based on a naming convention by organization, and these permissions, and blah and blah and blah). Such specific requirements can’t be written into a one size fits all solution, so I’m just going to mimic them by creating a whole bunch of role assignments, and then deal with some of the issues of initiating a bunch of ajax calls in a short period of time.


What We’re Building

I want to build a page that demonstrates what a provisioning scenario might look like. Namely, this is a whole lot of individual REST service calls, which presents some unique challenges when implementing in JavaScript in the browser.

In the interface below, you can choose a SharePoint group, one or more SharePoint lists in the current site, and one or more role definitions, and with one click you can assign each of those role definitions to that SharePoint group for each of those SharePoint lists. As filled out below, there are 6 lists and 4 role definitions to be applied to the group “CSRDemos Members”.

So the first thing I need to do is check each list to see if role inheritance isn’t broken (that’s 6 lists, so 6 REST calls and 6 network connections). Then if role inheritance isn’t broken, I need to break it (6 more REST service calls), and if it is already broken I need to delete each list’s role assignments for the SharePoint group (also 6 REST service calls, so stage 2 is 6 calls either way). And finally, I need to make 4 role assignments for each of 6 lists (i.e. 4×6 = 24 more REST service calls). Hmmm…that seems like a lot:

(6 hasuniquepermissions) + (6 breakroleinheritance/roleassignments) + (6 lists * 4 role definitions) = 36 REST calls

Piece of cake…right?


Manage Role Assignments Interface

And while you’re waiting, I pop up this nifty CSS-only spinner! What more could you ask for? I’ve said in previous REST posts that I could probably use some sort of busy indicator here or there, but this time I really need it.


Waiting for 36 REST Calls

Actually, it’s just not as bad as you might imagine. Below I show the console output from a run with these inputs. I log a bunch of console messages with things like how many connections I currently have open, the results from each service call, and finally the elapsed time. In the screenshot below, the elapsed time is 5.6 seconds. 36 REST calls in 5.6 seconds, I can live with that. And in fact, it could be much faster. I implement some throttling in my code, which is fancy tech talk for I slow it down some. Without that throttling, it takes roughly .7 to 1.5 seconds.


Finished in 5.6 seconds

Don’t believe me? It’s pretty clearly shown in the screenshot below. This is IE’s network tab in developer tools. It is currently showing the output from one button click with the above parameters, in other words the 36 REST service calls I described above. Notice how the network connections are bunched together in groups of 5. And as time progresses, a gap is clearly noticeable between those bunches.

That’s because I only open 5 network connections at a time. When I need more, I pause in one-second intervals and wait for old connections to be released before initiating new requests. This actually slows down the client quite a bit, but also reduces the load on the server quite a bit. It’s a trade-off, and you need to be aware of it if you’re going to implement anything that does bulk operations with SharePoint (REST or not). You can’t always pound the server full-throttle without eventually getting nasty calls from your farm administrators (or if you are a farm admin, you’re customers).

Now one of the knocks on REST vs CSOM is that CSOM has support for batch operations. In truth, so does SharePoint REST, but it’s very cumbersome and not even available until 2016. And anyway, realize that batching is a trade-off too. By opening multiple connections you can perform many operations in parallel, but at a cost of more resources used on both the client and the server, and you can tweak that with throttling. By doing batch operations I use a lot fewer resources on both the client and the server, but quite possibly the result appears slower to the end user because now all operations are executed on the server in serial, and the only way to tweak that is with more and better hardware. Or at least changesets are executed in serial (see the second Andrew Connell post referenced below). Batch operations within a changeset may be serial or may be parallel, but either way it’s not up to you.


Network shows connections bunched once per second

Now let’s say I had a more realistic scenario like say I had the same 6 lists in 10 sub-sites, and I needed to add role assignments to 3 different groups. But as long as we’re shooting for realism, let’s say I only need one role definition for each group, because there is really no reason to ever need more than one. If I need the union of permissions of 2 permission levels I can create a custom level with those permissions. And most permission levels have a privilege/subordinate relationship anyway, meaning Designer is Contributor+. So there’s no reason to assign a group Contributor and Designer, just Designer will do. I just did it above to generate a lot of connections and see how it performed. So anyway, here’s what this scenario looks like:

10 sites * 6 lists * 2 service calls (hasuniquerollassignments and breakinheritance/removeperms) = 120 service calls

And:

10 sites * 6 lists * 3 groups * 1 role assignment = 180 service calls

So if I can do 36 REST calls in 5.6 seconds with throttling, I can extrapolate that 300 similar REST calls would take approximately 46 seconds with the same throttling. And I can adjust the throttling to reduce the server load or increase the speed as needed (to a degree). 46 seconds might seems like a lot of time, but at least you get to look at my beautiful spinner while you’re waiting, and that would be roughly on the order of magnitude of 10 seconds without any throttling. And keep in mind that this is provisioning code. It probably only needs to be run once, or maybe once each time a new sub-site is added, can probably be run on off-peak hours, and therefore maybe neither performance nor server load is that much of a consideration. And if you want to do a lot of work, it’s going to take a bit of time. Even if you do it all manually, the server’s still going to end up doing the same work, albeit not all at once.

Either way, this is starting to look doable, even from JavaScript in the browser. There does come a point where it starts to look more like a job for PowerShell, but since many SCAs have neither the skills nor the option to run PowerShell against their site, anything in the range of 30-60 seconds is reasonably doable in the browser. Even longer really, but ideally you should provide a bit more than a pretty spinner to show that something is happening if it’s going to go much longer. Like maybe show the most recent console log message above the spinner. Some quick changing text like that can be very reassuring to the user that they’re not just locked up, even if it’s too fast to read.

Provisioning Role Assignments with Code

The sample code for this post is pretty big, so I’ll make no effort to show it all here. Much of it is just the same stuff I showed in my last post with the $.ajax calls converted to fetch. I’m just going to show some of the more interesting bits. Most of the code is encapsulated in an object literal called roleDefinitionManager (or rdm for short). The action starts with the rmd.init method:

First it initializes the options for the 3 select elements for group, lists, and role definitions. Then it sets up a click handler for the terribly named “Do it now” button. When that button is clicked, it summarizes what is to be done in a member property called todo, and then calls setRoleAssignments shown below. It also initializes some member properties used for connection management, but that will be easier to explain in a later code block.

setRoleAssignments just loops through the lists and calls setRollAssignmentsOnList. It just mostly does a call to the REST endpoint hasuniquerollassignments on the list. It does this to see if it should then break role inheritance, or just delete any role assignments from the list for the principal id. The code for these methods is omitted as uninteresting, but either of them ultimately calls a method that creates the new role assignments on the list. The more interesting thing in the above code is that the web service call is encapsulated in the nested method doListInternal, and the setRollAssignmentsOnList method ends with a call to executeOrDelayUntilConnectionAvailable.

If you’ve been doing any SharePoint JavaScript code, you must have heard about ExecuteOrDelayUntilScriptLoaded. executeOrDelayUntilConnectionAvailable (shown below) does basically the same thing but with different criteria for delaying. reserveConnection just increments a counter of connections and returns the current counter if a connection is available (i.e. max connections would not be exceeded). If no connection is available, it returns 0. releaseConnection just decrements that counter. So executeOrDelayUntilConnectionAvailable tries to reserve a connection and call the callback. If it cannot, it calls setInterval with the interval passed in and upon waking up tries again, over and over every interval until it successfully reserves a connection and calls the callback. Obviously, this is where the throttling can be tweaked, mostly by adjusting the maximum connections and/or the interval.

Note that each browser has a maximum number of concurrent connections and a lower maximum number of concurrent connections per host. When I started this, a fundamental misunderstanding led me to believe I had to do something like this reserve connection scheme because of these limits. But if you ask for more connections than the browser allows it doesn’t fail (at least not until a very high number in modern browsers). The browser just queues up your request and makes the connection when one is available much as I am doing. Still, doing it myself does allow me to throttle my requests which is quite useful. So if I set the maximum to 100, I’ll always reserve a connection as soon as I ask so I’ll plow ahead as soon as the browser will let me. If you look at the network tab of the developer tools, you’ll see that it still doesn’t open up 36 connections at once, there are just no appreciable gaps between close and open. I wouldn’t be surprised if there is still some high limit beyond which the browser will barf, but I haven’t hit it, and it’s probably browser specific.

And that’s really all the code I’m going to dump on you here. The rest of it is more of the same, I described the more interesting REST-related bits in my last post, and as usually I’ll include a download to a complete working page at the end of the post.

Sum Up Role Assignments Sample

The code for this sample was really quite the PITA, which is why I split this post into two parts. At some point it would be interesting to redo this using batch operations à la the Andrew Connell articles referenced below. But I wouldn’t do that without writing some kind of wrapper around the painfully tedious multi-part mime parsing described in those articles, and I’ll make no promises about when I might get around to that.

ManageRoleAssignments.aspx

Reference

Leave a Comment