Online collaboration and sharing of documents are key in today’s business. It comes with it, however, the question of confidentiality and security of the documents. In many cases, business needs to instill fine-grained control over such sharing. There are various controls that may need to be enforced with such sharing e.g. share only internally, only with specified users or groups, limited sharing with external users, or even no sharing at all. Add to that an ability to monitor and control each such sharing in detail.
Microsoft provides an excellent framework for online collaboration as well as a deep stack of Azure Information Protection and Management tools to achieve such goals. But it may need a lot of sewing in between before one can play with the canvas.
Talking about sharing, one needs to get into the technicalities of how exactly a sharing event happens and how to trap that to be able to integrate it with document-level security requirements. A detailed use case and implementation could be the subject matter of a future discussion, but today, I wish to discuss the inside working of a sharing event and how to intercept the event allowing you to write your own event handler.
Let me wear the practitioner’s hat now. In one of our recent assignments, we had some requirement and we needed to tap into the sharing event of a document so that we can perform a few additional actions upon sharing. Our natural choice was the out of the box events like "RoleAssignmentAdded/ RoleAssignmentDeleted/ResetInheritance" etc, but unfortunately, these events alone could not do the job. We needed some logic to be built around "Sharing Links" hidden list.
Let’s look at the technical intricacies when a document or folder is shared from SharePoint online or One drive and why we had to look into that detail level of implementation from Microsoft.
Just a recap first of what you can do with the permissions while sharing a document. There can be different types of permission levels that can be attached to the user/group:
With Read permission, User gets read access and he would be able to download the document.
User is entitled with this type of permission when we Select a document>Share>Provide email id and comment(optional):
With “Restricted View” permission, User gets read access and he would NOT be able to download the document.
User is entitled with this type of permission when we Select a document>Share>Provide email id and comment(optional)>click on people you specify can view>Enable block download
This type of access is only available for files, not for folders.
With “Contribute” permission, User gets the edit access to the document, in the case disabling download option is not viable option.
User is entitled with this type of permission when we Select a document>Share>Provide email id and comment(optional)> click on people you specify can view>check the “Allow Editing” option
So far, this is still what the user gets to see how it is organized. Dig a little deeper now. The item that I am going to discuss here is how the portal maintains this sharing information and how to capture the event of sharing effectively.
If we try to capture the event like “RoleAssignmentAdded” or “RoleAssignmentDeleted”, we might get into issues when we try to share the same document with other users with the similar type of access. The above event handler will not be triggered. To understand why we need to understand the underlying design behind the sharing.
Naming convention of Sharing Group
When we share a document with any user, it creates a special type of SharePoint group with following naming convention:
"SharingLinks.<document unique id>.Flexible.<access type id>"
When the first user gets added to the group, "RoleAssignmentAdded" event is triggered and we can capture the same. But if the same type of access is provided to the second user for the same document, this time, it is the same group where user gets added and "RoleAssignmentAdded" event will not be triggered.
Similar is the issue when we have multiple users with same access type. Only when permission is being revoked from the last user, "RoleAssignmentDeleted" event is triggered.
Hence, we ran into issues of not being able to capture event for all instances of sharing of the same document.
To overcome this issue, “Sharing Links” hidden SP list became the savior for us. Let us understand the design and behavior of this list.
Hidden List – “Sharing Links” – The Solution
Assuming the reader has enough idea about “document unique id”, I’m taking the liberty to explain this “access type id” of the dynamic group name mentioned above. To understand that, we need to know about this hidden list (“Sharing Links”) because sharing information of any document in the site is captured in this very hidden list. Initially when we create a site, this list is not created by default. As soon as any document is shared from the site, the list gets created and first entry is made to the list.
Here are the important fields in the hidden list:
||Unique id of the document which is shared
|Available sharing links
||Sharing details with invitee and inviter details along with the permission details
Available Links Payload
AvailableLinks field stores the Share Id, Expiration Details, Role definition, inviter and invitee details and other required information in the form of json payload
Here is a sample payload:
All information about sharing of any document is segregated based on the above 3 types of shares (Read/Contribute/Restricted View) and gets stored in the "Sharing Links" hidden list.
Let me explain the important properties which are highlighted above.
||This is the unique identifier for the type of access which gets appended as part of the dynamic group explained above
||This is the unique string appended as part of the shared link sent to the user
||Defines the type of access (Read/Contribute/Restricted View)
||This is again a nested json object representing the people to whom the document is shared providing the given type of access. These are properties of the object to be specific which would be to our interest:
Type: Defined the type of user: Internal User (1)/ Internal Group (2)/ External User (3)
PId: User id or group id in case it is shared with internal user or group
Email: Email id in case user is external user
SharedEmailGuest: is set to true when user is B2B external user and already added to the tenant as guest user.
Invited By: User id of the user who has shared the document
Invited On: Date Time when document is shared to the invitee
Sharing with Internal and External User
Sharing with an internal user and the external user has got some different behaviors in terms of actual access modification of any document which makes it a challenge to work with events like RoleAssignementAdded/RoleAssignmentDeleted etc. For example, when a link is shared with an internal user, the user gets added to the document permission set immediately and if we try to query RoleAssignment of the document programmatically, we would be able to fetch the user details. But the same is not true for external users. Unless and until the external user is clicking on the shared link received in his mailbox, the user is not added to the permission set of the document. These kinds of differences make it difficult to build a generic solution around document sharing. Whereas this “Sharing Links” hidden list itemadded/itemupdated event behavior is quite consistent for both internal and external users. We would be able to fetch the external user information even though the user is yet to access the link.
So, really this is all about understanding the “Sharing Links” hidden list. Once we know how the system maintains the list, we can capture the events on this list.
Voilà! you could write the desired piece of code in the event handler and be done with it. The applicability is enormous, you can control any attribute of the document and implement your own security policy.