SharePoint SSRS reports access denied after UPN suffix change

Symptoms:

Ok Picture this….You have a two-way external trust configured with corp.contoso.com and resource.farbrikam.com.  This trust allows all users to authenticate.  You also have a SharePoint 2010, 2013, or 2016 farm with SSRS and a web application configured to use Kerberos.  For argument sake we’ll state that the Kerberos authentication and Delegation is configured correctly and everything works.  We have preconfigured SSRS reports with the data sources set to use windows integrated authentication.

User, corp\UserA, has been accessing the reports without issues for some time now. This higher ups have decided to change the UPN for Corp\UserA from corp.contoso.com to SomeUPN.com. Ever since this UPN change our user, or any user who had their UPN changed in the Corp.Contoso.com domain, can no longer access the SSRS report.  The user can log into the SharePoint site without issues but can’t access the SSRS reports any more. After the UPN change we now receive the following error:

Error : Could not retrieve a valid windows identity for username ‘Corp\UserA’ with UPN ‘UserA@SomeUPN.com’. UPN is required when Kerberos constrained delegation is used. Exception: System.ServiceModel.Security.SecurityAccessDeniedException: Access is denied.

Why are we all of a sudden getting access denied??

Cause:

Well it is pretty obvious the UPN change is the issue, but why?

SSRS configuration relies upon the Claims to Windows Token Service to take their claims token and create a windows token to pass to SSRS as SSRS doesn’t understand claims.

So now how do we troubleshoot this to find out if the issue with the UPN change is with SharePoint?

Let’s take this Kerberos authentication outside of SharePoint and Claims To Windows Token Service

Rodney Viana has compiled a nice console application that takes this whole process outside of SharePoint

You can download this here:

https://rodneyviana.codeplex.com/downloads/get/816717

We simply run that on a WFE and put in the users UPN and it’ll work or it won’t.

When it works:

When it doesn’t work:

Ok great so we know that the issue is not with SharePoint.

So where is the issue at?

Well we know we are dealing with cross forest Kerberos authentication so the only other piece here would be the trust configuration.

So we take a look at the trusts configured with PowerShell:

 $myLocalDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
 $Trusts = $myLocalDomain.GetAllTrustRelationships()
 ForEach ($trust in $Trusts) 
 {
     Write-Host "SourceName:`t$($trust.SourceName)" -ForegroundColor Green
     Write-Host "TargetName:`t$($trust.TargetName)" -ForegroundColor Green
     Write-Host "TrustType:`t$($trust.TrustType)" -ForegroundColor Green
     Write-Host "TrustDirection:`t$($trust.TrustDirection)" -ForegroundColor Green
     Write-Host "Selective Authentication:`t$($myLocalDomain.GetSelectiveAuthenticationStatus($trust.TargetName))" -ForegroundColor Green
     Write-Host "SidFiltering:`t$($myLocalDomain.GetSidFilteringStatus($trust.TargetName))" -ForegroundColor Green
 }
 
Sample output:
 SourceName:        Corp.Contoso.com
 TargetName:        Resource.Fabrikam.com
 TrustType:        External
 TrustDirection:        Bidirectional
 Selective Authentication:        False
 SidFiltering:        True 

When we add an alternate UPN suffix to our domain this is called an explicit UPN suffix. Let me explain..

We have two kinds of UPN suffix’s:

  1. Implicit
    1. Default UPN created with the domain and contains a mapping with DNS
    2. Example:
      1. Domain: Corp.Contoso.com
      2. Implicit UPN: Corp.Contoso.com
  2. Explicit
    1. An Alternate UPN that will require UPN Suffix Routing for cross forest authentication
    2. Example:
      1. Domain: Corp.Contoso.com
      2. Explicit, or alternate, UPN: SomeOtherUPN.com

So now we know we are dealing with an Explicit UPN, which requires UPN Suffix Routing.

Ok sweet let’s set up that routing and be done with it right???

Wrong!!!

I wish it were that simple 🙂

In order to use UPN Suffix Routing we will need a Forest Trust not an External Trust.

Which means we’ll have to change our trust configuration

https://technet.microsoft.com/en-us/library/dd560679(v=ws.10).aspx

Resolution:

  1. Change the trust to a Forest trust from an External trust
    1. Enable UPN Suffix Routing

When you configure the forest trust you get to select all the alternate UPN Suffix’s you’d like routed

If the Explicit UPN was added after the trust was configured you’ll enable the Suffix routing in Domains and Trusts under the properties for your trust

If the trust can’t\won’t be changed then…..

  1. Change the UPN  back to the Implicit UPN
  2. Change how you are authenticating to the Data Source

IF option 3 is the way we are going we can use stored credentials to access the data source.

So that not everyone has access to all the reports because we’re using stored credentials we can use SharePoint’s security to control access to the reports regardless of how that report connects to its data source.

Or we can prompt for credentials, which most end users will hate.

Scripts:

Domain trusts out-put to file:

 param ( [String]$OutputFile
 )
 $myLocalDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
 $Trusts = $myLocalDomain.GetAllTrustRelationships()
 #Checking for Selective Authentication
 ForEach ($trust in $Trusts) {
 Write-OutPut "SourceName:`t$($trust.SourceName)"
 Write-OutPut "TargetName:`t$($trust.TargetName)"
 Write-OutPut "TrustType:`t$($trust.TrustType)"
 Write-OutPut "TrustDirection:`t$($trust.TrustDirection)"
 Write-OutPut "Selective Authentication:`t$($myLocalDomain.GetSelectiveAuthenticationStatus($trust.TargetName))"
 Write-OutPut "SidFiltering:`t$($myLocalDomain.GetSidFilteringStatus($trust.TargetName))"
 } 

Check Delegation to confirm Kerberos configuration

For a single account:

Get-ADObject -fi {(samAccountName -eq 'svcspapppool')} -prop samAccountName,msDS-AllowedToDelegateTo,servicePrincipalName,userAccountControl
| select DistinguishedName,ObjectClass,samAccountName,servicePrincipalName,
@{name='DelegationStatus';expression={if($_.UserAccountControl -band 0x80000){'AllServices'}else{'SpecificServices'}}}, @{name='AllowedProtocols';expression={if($_.UserAccountControl -band 0x1000000){'Any'}else{'Kerberos'}}}, @{name='DestinationServices';expression={$_.'msDS-AllowedToDelegateTo'}} 

More Information:

Great article that dives into C2WTS (also provides the resolution to the case in reason #1)

SharePoint & Custom Claims when consuming service applications

Wictor Wilen has two great blogs out there that discuss the claim type encoding and a lot of the information I’ve gathered is from those blogs.

These were written for 2010 but a lot of it still applies. In fact, 99% of it still applies to 2013, and 2016. I can imagine the same will be for 2019.

http://www.wictorwilen.se/Post/How-Claims-encoding-works-in-SharePoint-2010.aspx

http://www.wictorwilen.se/introducing-the-sharepoint-2010-get-spclaimtypeencoding-and-new-spclaimtypeencoding-cmdlets

Consider the following configuration:

We have two farms configured. One farm is an Enterprise Services farm publishing service applications like Managed Metadata, User Profile Services, and Search. The other farm is the consuming farm or Content Farm. The consuming farm is authenticated via custom Claims from a third-party Identity provider like SiteMinder or Ping. When we are performing a search, we are getting no results returned when we are expecting to get many search results. When we are logged in with our windows account and not SAML we get results without issue.

  • Customer is logged in as: i:0g.t|siteminder|b0000001
  • Claim added to query: c:0?.t|siteminder|b0000001

For those of you who do not wish to read the whole blog the following two points are the cause of the issue

  • The underlying cause of this issue is due to custom claims and claim type encoding between the two farms.
  • In order to use custom claims between two or more farms their claim type encoding character must be the same between all the farms

But let’s walk through this a little so we know how to troubleshoot this

  • To begin troubleshooting this issue we must know what claim types our farm knows about, and what is the associated Encoding Character.
  • We simply run the following PowerShell command
  • This will give you the following output: (Example from SharePoint 2016)

    This will convert the encoding character to a more manageable value

  • Get-SPClaimTypeEncoding | ft @{Label=”Character”;Expression={[Convert]::ToInt32($_.EncodingCharacter)}}, ClaimType | ft -autosize

    New-SPClaimTypeEncoding

  • Using the New-SPClaimTypeEncoding cmdlet we can add our own claim types and use our own encoding character.
  • The cmdlet takes two arguments the -EncodingCharacter which is the encoded value and the -ClaimType.
    • New-SPClaimTypeEncoding -EncodingCharacter ([Convert]::ToChar(513)) -ClaimType “urn:another-claim-type” -Confirm:$false

  • If you get an Argument Exception, it can be for many different reasons.
    • Make sure that your encoding character is not currently in use.
    • Make sure you’re starting above 500 for your encoding character.
    • We cannot use upper case characters or white space characters.

    Note:

    • The use of the encoding character is the most important part here. We must use an encoding character that is not currently in use in the publishing farm or the consuming farm.
    • This is crucial because you can’t remove a custom claim once added so the encoding character cannot be reused.
    • This encoding character must be the same between farms for our custom claims.
      • If they are not, then when we go to match our custom claim type to our known claim types by the encoding character they will not match up and we will get security trimmed or access denied as the result.

    Clarification:

  • Publishing farm has custom claim “customID” with a claim type of http://somecustomclaim/claims/customID using encoding character 509.

  • Consuming farm has the same custom claim “customID” with a claim type of http://somecustomclaim/claims/customID but it’s using encoding character 505.

  • Between farms this “customID” claim is not the same claim even though they have the same claim name and claim type.
  • Since we map our claim type to our known claims list by the encoding character and not by claim type, or name, these would be treated as two different claims using the same claim name and claim type.

    So, the real question now is how do we fix this??

    Well since you can’t remove a claim once it’s been added

    And you can’t add a claim type if it already exists

    The only tangible way to fix this is to do one of two things

    • Rebuild the farm and add the claim type with the correct encoding character
    • Use a different OOTB claim or custom claim that has the same encoding character between farms

    Rebuilding the farm is straightforward, but what if that’s not an option and you can’t change the incoming claim type sent from the third-party identity provider on the consuming farm?

    • Well I suppose you can use SharePoint to transform the incoming claim type to a different claim type. Perhaps one that exists in the enterprise services farm.
      • I would suggest using an out of the box claim type and not a custom claim type.
    • This process would be done on the consuming farm and it would be done on the trusted identity provider.
    • The easiest way to accomplish this is when create our trusted identity provider when we are creating our claim mappings.
    • I won’t go into detail on how to add a trusted identity token issuer, or how to add a claim mapping to an existing trusted identity token issuer as that is outside the scope of this article. However, the claim mapping would look something like this where we take the incoming claim of “customID” and transform the claim to UPN. Since this is an out of the box claim and if we have permission to resources via the UPN claim type search results will start displaying proper results.

SharePoint and the infamous People Picker

So, I had a pretty interesting support case the other day. Cx stated that people picker wasn’t returning any results for users in a trusted domain. I was thinking ok I mean how hard could this be……. well so, my brain hurts now . So hopefully this will save someone else a huge headache.

Ok so let me paint the scenario for you, but first what is the issue?

Problem:

When you perform a people picker search for a user account that resides in a trusted domain no results are returned and SharePoint reports “User SPPrincipalInfo doesn’t seem to have a user principal name value” for our user we queried for

Scenario:

  • We have two domains with a two-way external trust configured. So, everyone trusts everyone here it’s a happy family.
    • Contoso.com
    • Fabrikam.com
  • SharePoint Servers, SharePoint Farm Service Accounts, and User accounts reside in the Contoso.com domain
  • For purposes of this article only user accounts reside in the Fabrikam.com domain
  • We are using a SAML provider that is using an out of the box AD claim provider. This is created by SharePoint when we create our trusted identity token issuer using the “-UseDefaultConfiguration” parameter like so:

    $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(“C:\temp\ADFSSigningCert.cer”)

    New-SPTrustedRootAuthority -Name “Token Signing Cert” -Certificate $cert

    $ap = New-SPTrustedIdentityTokenIssuer -Name “ADFS SAML Provider” -Description “SAML Authentication for SharePoint” -realm “urn:sharepoint:www.contoso.com” -ImportTrustCertificate $cert -SignInUrl “https://adfs.Contoso.com/adfs/ls-UseDefaultConfiguration -IdentifierClaimIs USER-PRINCIPAL-NAME;

    $ap = Get-SPTrustedIdentityTokenIssuer -Identity “ADFS SAML Provider”;

    $ap.ProviderRealms.Add(“https://www.contoso.com”, “urn:sharepoint:www.contoso.com”);

    $ap.ProviderSignOutUri = “https://adfs.contoso.com/adfs/ls“;

    $ap.ClaimProviderName = “ADFS SAML Provider”;

    $ap.UseWReplyParameter = $true;

    $ap.Update();

Note:

The -UseDefaultConfiguration parameter will create a claim provider for you. Since this is our code (Microsoft) we are able to sink our hooks into AD to perform the user lookup based on the information entered into the people picker. This allows us to return a friendly display name instead of the awful identifying claim, which is some cases could be an employee ID

  • We have not disabled the out of the box AD Claim provider, but we have hidden the provider like so:
    • Do not disabled this claim provider it will break a lot of things that required windows authentication

      $cpm = Get-SPClaimProviderManager

      $ad = get-spclaimprovider -identity “AD”

      $ad.IsVisible = $false

      $cpm.Update()

  • We then must configure the people picker to search both domains like so:
    • If your domain short name differs from the first part of your domains FQDN you will need to disable netbios DC resolve.
    • For example, your FQDN for your domain is “Contoso.com” the Pre-Windows 2000 value for your domain is “Corp” and you log in as “Corp\user”.
      • Since “Corp” does not match “Contoso” we must run this PS and set the proper value for the attribute “ShortDomainName”

        Add-PSSnapin Microsoft.SharePoint.PowerShell -ea silentlycontinue

        $farm = get-spfarm

        $farm.Properties

        $farm.Properties[“disable-netbios-dc-resolve”] = $true

        $farm.Properties

        $farm.Update()

      • Otherwise we only have the need to run this PowerShell

        $wa = Get-SPWebApplication https://www.contoso.com

        $adsearchobj1 = New-Object Microsoft.SharePoint.Administration.SPPeoplePickerSearchActiveDirectoryDomain

        $adsearchobj1.DomainName = “Contoso.com”

        $adsearchobj1.ShortDomainName = “Contoso” #Optional

        $adsearchobj1.IsForest = $true #$true for Forest, $false for Domain

        $wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Add($adsearchobj1)

        $wa = Get-SPWebApplication https://www.contoso.com

        $adsearchobj2 = New-Object Microsoft.SharePoint.Administration.SPPeoplePickerSearchActiveDirectoryDomain

        $adsearchobj2.DomainName = “Fabrikam.com”

        $adsearchobj2.ShortDomainName = “Fabrikam” #Optional

        $adsearchobj2.IsForest = $true #$true for Forest, $false for Domain

        $wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Add($adsearchobj2)

        $wa.Update()

Note:

  • If you’re not certain if your short name differs from your FQDN here is some PowerShell to help you.
    • This will only return the local domain information though.

    Get the FQDN domain name

    (gwmi win32_computersystem).Domain

Get the Short name for the domain

(net config workstation) -match ‘Workstation domain\s+\S+$’ -replace ‘.+?(\S+)$’,’$1′

  • The -UseDefaultConfiguration parameter will create a claim provider for you. Since this is our code we are able to sink our hooks into AD to perform the user lookup based on the information entered into the people picker. This allows us to return a friendly display name instead of the awful identifying claim, which is some cases could be an employee ID.

Search with people picker for the user account in the fabrikam.com domain

  • Attempt to grant permissions to the user in the Fabrikam domain “PeoplePickerTest” using the people picker from here: https://www.contoso.com/_layouts/15/user.aspx
  • Within People Picker search for “peoplepickertest”
  • Search result:
    • No results found is the result

What You’ll see in the ULS logs and Network Monitor trace

Looking at netmon trace we see the first call

24167        12:47:08 PM 11/17/2016        13.6861171        w3wp.exe        10.19.141.20        RHWDC18.contoso.com         LDAPSASLBuffer        LDAPSASLBuffer:BufferLength: 667, AuthMechanism: GSS-SPNEGO        {LDAP:686, TCP:682, IPv4:676}

Here is our query to the Contoso.com domain

Filter: (|(&(objectCategory=person)(|(anr=peoplepickertest*)(SamAccountName=peoplepickertest*)(userPrincipalName=peoplepickertest*)))(&(objectCategory=group)(BIT_AND: (groupType)&2147483648)(|(anr=peoplepickertest*)(SamAccountName=peoplepickertest*))))

Here are the attributes we are requesting

Attributes: ( objectSID )( mail )( mobile )( displayName )( title )( department )( proxyAddresses )( cn )( samAccountName )( groupType )( userAccountControl )( distinguishedName )( objectClass )( userPrincipalName )( msexchmasteraccountsid )

24168        12:47:08 PM 11/17/2016        13.6869698        w3wp.exe        RHWDC18.contoso.com         10.19.141.20        LDAPSASLBuffer        LDAPSASLBuffer:BufferLength: 1027, AuthMechanism: GSS-SPNEGO        {LDAP:686, TCP:682, IPv4:676}

Here is our first search result

SearchResultEntry: CN=peoplepickertest@farbrikam.com,OU=Fabrikam,OU=Contacts from SimpleSync,OU=User,OU=Contoso,DC=contoso,DC=com

Here are the attributes returned

+ PartialAttribute: objectClass=( top )( person )( organizationalPerson )( contact )

+ PartialAttribute: cn=( peoplepickertest@Fabrikam.com )

+ PartialAttribute: title=( Business Application Consultant )

+ PartialAttribute: distinguishedName=( CN=peoplepickertest@Fabrikam.com,OU=Fabrikam,OU=Contacts from SimpleSync,OU=RHDC,OU=Contoso,DC=contoso,DC=com )

+ PartialAttribute: displayName=( Jones, Bob (Fabrikam) )

+ PartialAttribute: department=( AD_Medmanagement )

+ PartialAttribute: proxyAddresses=( x500:/o=Contoso.com/ou=First Administrative Group/cn=Recipients/cn=peoplepickertest.Farbrikam.com1 )( x500:/o=Contoso/ou=First Administrative Group/cn=Recipients/cn=peoplepickertest@Fabrikam.com1 )( X500:/o=Contoso/ou=First Administrative Group/cn=R

+ PartialAttribute: mail=( peoplepickertest@Farbrikam.com )

+ PartialAttribute: msExchMasterAccountSid=( )

In ULS we’ll see these entries for this search

SearchFromGC name = Contoso.com. start

GetAccountNameFromSid “0x0105000000000005150000007B005C46C7C60121369482B9798A0500” start

“0x0105000000000005150000007B005C46C7C60121369482B9798A0500” returned. returnValue=True

GetAccountNameFromSid “0x0105000000000005150000001741550EDE09584B0363450079C20000” start

GetAccountNameFromSid “0x0105000000000005150000001741550EDE09584B0363450079C20000” returned. returnValue=True

SearchFromGC name = Contoso.com. returned. Result count = 2

0x0105000000000005150000001741550EDE09584B0363450079C20000 = Userid:S-1-5-21-240468247-1264060894-4547331-49785

We have returned an entry with the SID S-1-5-21-240468247-1264060894-4547331-49785

This SID translates to fabrikam\peoplepickertest, but it was returned from the Contoso.com domain

We can see in the network monitor trace that this is returned as a contact with the attribute value msExchMasterAccountSid and no UPN

We then call out to the Fabrikam.com domain

39482        12:47:20 PM 11/17/2016        25.4775491        w3wp.exe        10.19.141.20        dc2vm.fabrikcam.com        LDAPSASLBuffer        LDAPSASLBuffer:BufferLength: 418, AuthMechanism: GSS-SPNEGO        {LDAP:1013, TCP:1012, IPv4:1009}

Here we search fabrikcam.com

SearchRequest: BaseDN: DC=Fabrikam,DC=com, SearchScope: WholeSubtree, SearchAlias: neverDerefAliases

We filter on SID

Filter: (&(objectSID=))

And we request these attributes

Attributes: ( objectSID )( mail )( mobile )( displayName )( title )( department )( proxyAddresses )( cn )( samAccountName )( groupType )( userAccountControl )( distinguishedName )( objectClass )( userPrincipalName )( msexchmasteraccountsid )

Here is the search result

39519        12:47:20 PM 11/17/2016        25.4835293        w3wp.exe        dc2vm.fabrikam.com        10.19.141.20        LDAPSASLBuffer        LDAPSASLBuffer:BufferLength: 789, AuthMechanism: GSS-SPNEGO        {LDAP:1013, TCP:1012, IPv4:1009}

SearchResultEntry: CN=PeoplePickerTest,OU=Contract,DC=Fabrikam,DC=Com

We return these attribute values

Frame: Number = 39519, Captured Frame Length = 847, MediaType = ETHERNET

– Attributes: 12 Partial Attributes

+ SequenceHeader:

+ PartialAttribute: objectClass=( top )( person )( organizationalPerson )( user )

+ PartialAttribute: cn=( PeoplePickerTest )

+ PartialAttribute: title=( Business Application Consultant )

+ PartialAttribute: distinguishedName=( CN=PeoplePickerTest,OU=Contract,DC=Fabrikam,DC=Com )

+ PartialAttribute: displayName=( PeoplePickerTest)

+ PartialAttribute: department=( AD_Medmanagement )

+ PartialAttribute: proxyAddresses=( smtp:PeoplePickerTest@fabrikam.com )( SMTP:PeoplePickerTest@fabrikam.com)

+ PartialAttribute: userAccountControl=( 512 )

+ PartialAttribute: objectSid=( )

+ PartialAttribute: sAMAccountName=( PeoplePickerTest )

+ PartialAttribute: userPrincipalName=( PeoplePickerTest@fabrikam.com )

+ PartialAttribute: mail=( PeoplePickerTest@fabrikam.com )

We then see these entries in the ULS for this search

SearchFromGC name = Fabrikam.com. start

GetAccountNameFromSid “0x0105000000000005150000001741550EDE09584B0363450079C20000” start

GetAccountNameFromSid “0x0105000000000005150000001741550EDE09584B0363450079C20000” returned. returnValue=True

SearchFromGC name = Fabrikam.com. returned. Result count = 1

We then see this error

User SPPrincipalInfo doesn’t seem to have a user principal name value. User: ‘fabrikam\PeoplePickerTest’, ID: ‘-1’

Conclusion:

  • We bind to the first result received with the SID
  • The attributes passed in the first search result was for a contact which does not have an object SID but does have a msEXCHMasterSID.
  • This attribute is included in the first search results, so we bind to this master SID and translate to a user account, which is the account we are expecting.
  • However, this search result does not contain a UserPrincipalName attribute, so we do not display the user.

Workarounds (in no particular order):

  • If you change the search order of the domains in the people picker searchadforests property to search the trusted domain Fabrikam.com first, then search Contoso.com. The people picker will bind to the first result it receives which will be the correct user object.

    Origninal configuration

    stsadm -o setproperty -url https://www.contoso.com -pn peoplepicker-searchadforests -pv “forest:contoso.com;forest:fabrikam.com”

    Reversed search order

    stsadm -o setproperty -url https://www.contoso.com -pn peoplepicker-searchadforests -pv “forest:fabrikam.com;forest:contoso.com

  • If you search for the user using domain\username that will return the correct results regardless of the people picker search order
    • This does not query every domain in the list
    • This is a direct LDAP query to the domain specified
  • If you copy the account name and paste it into the people picker search this will also return the correct user
    • This mimics the check name function for the account name
    • When you type in a user account we will actually start performing LDAP queries to return best bet results
      • For example, if I want to search for the account UserA as I type that into the people picker search the results may start displaying all the accounts with User in them and ultimately filter down to the exact result when you finish typing in the user account name
  • If you type in the users account name and then do a CTRL + K (check name function) this is the same as pasting in the users account name
    • This also uses the check name function

More of a permanent solution:

Ultimately the issue is people picker is resolving contact objects.  Since contacts can’t be used to log in with we can filter these out from the people picker results with a custom filter.

The filter is a LDAP search filter: (|(objectCategory=group)(&(objectCategory=person)(!objectClass=contact))).

We can test this filter using the custom search feature in Active Directory Users and Computers.

In SharePoint, we’ll fix this issue by using the below PowerShell:

==============================PowerShell Script==============================

Add-PSSnapin Microsoft.SharePoint.PowerShell -ea silentlycontinue

$wa = Get-SPWebApplication <Put Web App Here>

$wa.PeoplePickerSettings.SearchActiveDirectoryDomains | out-file c:\temp\pp_settings_before.txt

$wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Clear()

$wa.update()

$wa.PeoplePickerSettings.SearchActiveDirectoryDomains

$wa = Get-SPWebApplication <Put Web App Here>

$adsearchobj1 = New-Object Microsoft.SharePoint.Administration.SPPeoplePickerSearchActiveDirectoryDomain

$adsearchobj1.DomainName = “Contoso.com”

$adsearchobj1.ShortDomainName = “Contoso”

$adsearchobj1.IsForest = $false

$newdomain.CustomFilter = “(|(objectCategory=group)(&(objectCategory=person)(!objectClass=contact)))”

$wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Add($adsearchobj1)

$wa = Get-SPWebApplication <Put Web App Here>

$adsearchobj2 = New-Object Microsoft.SharePoint.Administration.SPPeoplePickerSearchActiveDirectoryDomain

$adsearchobj2.DomainName = “Fabrikam.com”

$adsearchobj2.ShortDomainName = “fabrikam”

$adsearchobj2.IsForest = $true

$newdomain2.CustomFilter = “(|(objectCategory=group)(&(objectCategory=person)(!objectClass=contact)))”

$wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Add($adsearchobj2)

$wa.Update()

==============================PowerShell Script==============================

Additional information about People Picker and the MSExchMasterAccountSid property
https://blogs.iis.net/deanc/troubleshooting-people-picker-with-netmon

To see what is currently set in your environment use the following PowerShell
$webService = new-object Microsoft.SharePoint.Administration.SPWebService
$webService.PeoplePickerSearchReplicatedMasterSIDPropertyName

Now this is ignored if you copy paste in a value that exists in the local domain. This will return the local domains user object instead of the referenced object that is in the MSExchMasterAccountSid property

SharePoint 2013 Publishing feature and limited access

Background:

We have upgraded out farm from 2010 to 2013 using the database attach method. We also have created site collections and sub sites after the migration. Our sites are team sites with the publishing feature activated. We have unique permissions for our libraries and sub sites.

 

Issue:

The issue I was tasked with overcoming is that users who have no permissions to the library can still see the library in site contents, and in other sites not only can they see the library in site contents, but they can also access the library. They don’t see any content, but can still load the library. This is not how I would expect this to work at all. I would expect them to get access denied and not see the library in site contents.

 

Scenario A:

  • We have a web application with a root site collection that has been deployed as a team site
  • We have activated the following feature at the site collection
    • SharePoint Server Publishing Infrastructure
    • Limited-access user permission lockdown mode
  • We have created a sub site that inherits permissions from the parent web.
  • We have activated the Site feature “SharePoint Server Publishing”
  • We have created a doc library
    • We have stopped inheriting permissions from the parent web
    • We have removed all permissions to this library via the UI

Result:

  • A user with read only access to the site collection is able to see details about the library in site contents (http://sp2013/_layouts/15/viewlsts.aspx)
    • The user is denied access to the library when attempting to access it via Site Contents and/or through the direct URL

 

 

Scenario B:

  • We have a web application with a root site collection that has been deployed as a team site
  • We have activated the following feature at the site collection
    • SharePoint Server Publishing Infrastructure
  • We have created a sub site that inherits permissions from the parent web.
  • We have activated the Site feature “SharePoint Server Publishing”
  • We have created a doc library
    • We have stopped inheriting permissions from the parent web
    • We have removed all permissions to this library via the UI

Result:

  • A user with read only access to the site collection is able to see details about the library in site contents (http://sp2013/_layouts/15/viewlsts.aspx)
  • If the user tries to access the library they are granted access to view the application page but not the actual contents of the library.       However, they are not presented with the access denied message.

 

Scenario C:

  • We have a web application with a root site collection that has been deployed as a team site
  • We have activated the following feature at the site collection
    • SharePoint Server Publishing Infrastructure
    • Limited-access user permission lockdown mode
  • We have created a sub site that has unique permissions and never inherits from the parent web.
  • We have activated the Site feature “SharePoint Server Publishing”
  • We have created a doc library
    • We have stopped inheriting permissions from the parent web
    • We have removed all permissions to this library via the UI

Result:

  • Users who do not have permissions to this library do not see the library in site contents
  • If they try to access the library directly they get access denied
  • This is the expected result

 

Cause:

  • The underlying cause here is “Limited Access” and the “Style Resource Readers” group.
    • “Use this group to give read permissions to the Master Page gallery and Style Library, which are required to browse this site. Do not delete this group.
  • The Limited Access permission level is designed to be combined with fine-grained permissions to give users access to a specific list, document library, item, or document, without giving them access to the entire site. However, to access a list or library, for example, a user must have permission to open the parent Web site and read shared data such as the theme and navigation bars of the Web site. The Limited Access permission level cannot be customized or deleted.
  • You cannot assign this permission level to users or SharePoint groups. Instead, SharePoint automatically assigns this permission level to users and SharePoint groups when you grant them access to an object on your site that requires that they have access to a higher level object on which they do not have permissions. For example, if you grant users access to an item in a list and they do not have access to the list itself, SharePoint automatically grants them Limited Access on the list, and also the site, if needed.

 

Scenario A:

  • When we have the publishing feature activated on the site collection we are introducing all the publishing features. This is usually done when a need for modifying the master page is present. With this we are introducing the style resource readers group that has default members of “NT Authority\All Authenticated Users” and the “Everyone” group.
  • When we start breaking permissions we are creating limited access to all the parent objects. This is by design.
  • The ‘Limited Access’ permission group has very limited permissions, but by design we have the ‘Open’ permission.
    • Open –       Allows users to open a Web site, list, or folder in order to access items inside that container.
  • Since the Style Resource Readers group has the Limited Access role as well as the default member “c:0(.s|true” this will grant everyone the ability to open the document library that has unique permissions
  • Since all our users have some sort of permission to the document library we will see it in site contents and the quick launch if it’s present there. This is because we have permissions to the object so it’s not being security trimmed.
  • We get access denied though because we don’t actually have any permissions to the library.

 

Scenario B:

  • Same as Scenario B with the following exception:
    • Limited-access user permission lockdown mode
      • If this is not activated the role ‘Limited Access’ will be granted an additional permissions
        • View Application Pages – View forms, views, and application pages. Enumerate lists.
      • This will allow users to view application pages such as the ‘AllItems.aspx’ page
        • The user will not be able to see any items however

Scenario C:

  • This one is different because we created the sub site with unique permissions.
  • Technically this sub site has never inherited from the parent.
    • There are no groups with limited access and the ‘Style Resource Readers’ group has not been inherited
      • Note: Do not inherit permissions from the parent as you will replace all permissions for every child object which will grant the ‘Style Resource Readers’ group limited access to all child objects

 

Resolution:

  • First off there is no resolution to this issue only work arounds. ‘Limited Access’ and the ‘Style Resource Readers’ group is designed to function like this.

Note:

DO NOT MODIFY THE PERMISSIONS FOR THIS GROUP

  • The only potential workaround would be to follow ‘Scenario C’ however, this would only work for sub sites and not the root of the site collection

 

Scripts:

To validate web and list role assignments

$web = get-spweb http://sp2013/

$weburl = $web.Url

$list = $web.Lists[“documents”]

Write-Host -ForegroundColor Cyan “Below are the Role Assignments for $list”

$list.RoleAssignments

Write-Host “”

Write-Host -ForegroundColor Cyan “Below are the Role Assignments for $weburl”

Write-Host “”

$web.RoleAssignments