Freigeben über


SharePoint 2013: "Check Permissions" and "DoesUserHavePermissions" Internals

Overview

If you're a SharePoint administrator, you've probably wondered how the "Check Permissions" tool in SharePoint works, and more importantly, why does it happens sometimes that the users have been granted permissions via an Active Directory security group and can access SharePoint without a problem yet "Check Permissions" reports that those users do not have permissions to the site. This post should hopefully help clarify some of those confusions and will also help you troubleshoot and address issues related to "Check Permissions". This post primarily applies to SharePoint 2013, but the concepts are applicable to all versions of SharePoint starting SharePoint 2007.

First off, it's important to understand that the "Check Permission" tool should provide an accurate reflection of a particular user's access to the site, but in some cases, especially when the users have been granted permissions via Active Directory security groups, it does not function correctly and incorrectly reports permissions. In most cases, that does not prevent users from accessing the site as long as they have been granted the correct permissions even though "Check Permissions" reports otherwise. Most parts of SharePoint do not rely on "Check Permissions" to determine whether or not the user has access to a particular resource.  However, there is a function in the SharePoint object model SPWeb.DoesUserHavePermissions that a developer may be using in a custom WebPart/control and this API uses the same mechanism as "Check Permissions" and could therefore be affected if "Check Permissions" tool is not reporting permissions correctly. Now you could argue why doesn't "Check Permissions" use the same underlying mechanism to verify the user's permissions as SharePoint's core permission management process, and that's a valid argument. User authentication in SharePoint has evolved over the years (classic to claims), however, "Check Permissions" tool and the DoesUserHavePermissions API have worked the same way since the MOSS 2007 days. Sure there is room for improvement, and we will get there one day :).

How does it work?

So how does "Check Permissions" work? In a nutshell, every single user that ever accesses a SharePoint site gets a record created for themselves in the UserInfo table of the content database for that site. The user's record gets created regardless of whether they have been granted permissions directly in SharePoint or whether they have been granted permissions via an Active Directory security group. The only difference is that if a user has been granted permissions via an Active Directory security group, the record in UserInfo table only gets created when the user accesses the site for the first time or when SPWeb.EnsureUser function is called for that particular user. For users that have been granted permissions via AD Security Groups, SharePoint stores a token in the tp_ExternalToken column of the UserInfo table that contains information about all the Active Directory security groups that the user is a member of. "Check Permissions" reads this token to determine whether or not the user is a member of a group that has permissions to the site. "Check Permissions" reports incorrect permissions when the information in tp_ExternalToken is either outdated or incorrect. So how does SharePoint populate information in tp_ExternalToken?

tp_ExternalToken Explained

By default, SharePoint updates the tp_ExternalToken for users once every 24 hours. You can easily tell exactly when the user's token was last updated by examining the tp_ExternalTokenLastUpdated column which contains the last update time in GMT time zone. The token may get refreshed after 8 (24/3) hours if the user logs into SharePoint themselves, and SharePoint takes the opportunity to update the User's token to avoid permission problems that the farm/application pool account may run into when updating the user's token (permission problems are discussed later). This default 24 hour token timeout is configurable, but I would strongly recommend against changing this default timeout as that can lead to other problems (too much traffic going to your domain controllers and users intermittently getting the "the context has expired and can no longer be used" error message). Here is how you can change the timeout for tp_ExternalToken using PowerShell. Notice that this is a farm wide setting and applies to all sites. In this example, we are just setting the timeout to it's default value of 1440 minutes (24 hours):

 $cs = [Microsoft.Sharepoint.Administration.SPWebService]::ContentService 
$cs.TokenTimeout = (New-TimeSpan -minutes 1440) 
$cs.Update() 

The same effect can also be achieved by adjusting the token-timeout property using stsadm.

Note: If Active Directory security groups have been given permissions on the site and newly added users to the groups are receiving access denied while accessing the site, you can adjust the WindowsTokenLifetime and LogonTokenCacheExpirationWindow properties of STSConfig object for the farm as needed. This is assuming your site is using Windows Claims Authentication. This is outside the scope of this post as we are discussing "Check Permissions" and "DoesUserHavePermissions".

Each time SharePoint needs to read the user's external token (e.g. "Check Permissions" is being used), the following sequence of events occurs:

1) The freshness of the token is evaluated based on the currently configured token timeout. If the token is valid, it is simply returned from the database and SharePoint does not try to connect to the Active Directory to get a new token.

2) If the token has timed out, SharePoint attempts to refresh the token by reading the user's tokenGroupsGlobalAndUniversal attribute from Active Directory.

3) If SharePoint is able to read the tokenGroupsGlobalAndUniversal, SharePoint essentially has a list of SIDs (Security Ids) with each SID representing a group.

4) SharePoint then tries to resolve each SID to a group name and eventually updates the token in the database.

Common Problems with tp_ExternalToken Refresh

Here are some of the common problems that can occur with tp_ExternalToken fresh and possible resolutions. All of these problems do not result in any error message to the user. We just get a bad token that gets cached in the database for the duration of the timeout. As a result, "Check Permissions" and "DoesUserHavePermisions" simply returns incorrect information. This makes troubleshooting particularly challenging.

Problem #1: SharePoint cannot read the user's tokenGroupsGlobalAndUniversal attribute because the user is in a domain that does not trust SharePoint domain (one-way trust).

Resolution: Adjust the people picker settings to specify a username and password for each domain from which the users can be added to SharePoint. Be sure to run the setapppassword command on each server in the farm before you configure people picker. You have to list every domain from which the users can be added or that domain will not be searched. Here is a sample PowerShell command:

stsadm -o setproperty -pn peoplepicker-searchadforests -pv "domain:domain1.fqdn.com,account,Password;domain:domain2.fqdn.com,account,Password" -url https://webapp

Problem #2: SharePoint timer service account or application pool account does not have permission to read the user's tokenGroupsGlobalAndUniversal attribute.

Resolution: Add the SharePoint accounts to the Windows Authorization Access group by using the Active Directory Users and Computers snap-in.

Problem #3: SharePoint cannot resolve the SID to group name because the group does not exist in the same domain as SharePoint. You may see the following error in ULS logs:

03/29/2017 23:17:01.98 w3wp.exe (0x9DAC) 0x9578 SharePoint Foundation Claims Authentication ame5l Medium SPClaimsAuthRoleProvider.GetTokenGroupsForUser() unable to resolve group SID S-1-5-21-682003330-1957994488-839522115-553146

This problem occurs because SharePoint does not use the Global Catalog to resolve the SID to a group name.

Resolution: A new property of the SPPeoplePickerSettings object named ClaimsAuthRoleProviderUsesGlobalCatalog was introduced with the December 2015 CU for SharePoint. By setting this property, you essentially tell SharePoint to use the Global Catalog when resolving group SIDs. Here is how you can set the property:

 $w = Get-SPWebApplication $url 
$w.PeoplePickerSettings.ClaimsAuthRoleProviderUsesGlobalCatalog = $true 
$w.Update()

Problem #4: SharePoint tries to enumerate user groups using the Windows API GetAuthorizationGroups. This API has known issues including one that results in this API call to fail if one of the groups that the user is a member of has SID history attached to it. You may see the following errors in the ULS logs:

System.InvalidOperationException: The server is unwilling to process the request.

System.DirectoryServices.DirectoryServicesCOMException: There is no such object on the server

Resolution: We can force SharePoint to use the "soft" SID resolution method which does not involve a call to GetAuthorizationGroups. This can be done by identifying an account in each domain that is a member of Windows Authorization Access group in that domain. Once the accounts have been identified, we need to set ClaimsAuthRoleProviderUsesGlobalCatalog property to $true as mentioned in Resolution #3 and then configure the people picker settings with a username and password for each domain. Every single domain from which users can be added to SharePoint must be specified or that domain will not be searched.

stsadm -o setproperty -pn peoplepicker-searchadforests -pv "domain:domain1.fqdn.com,account,Password;domain:domain2.fqdn.com,account,Password" -url https://webapp

Be sure to run setapppassword on each server in the farm so that password can be encrypted.

Happy SharePointing!