Thursday, May 17, 2012

debug claim provider



Claims Administration, Audiences and More

E-mail Print PDF

The Need for Claims Administration

Have you ever wondered how to get a list of claims for a specific user and if it's even possible?
There shall be no suspense, it is possible. If you have ever tried to implement user impersonation
(emulating the actual user logging in) you would expect some complexity with having to recreate the
HTTP Context or using unmanaged code, but none of this will be required.

This article is a second part of the series about a Custom Claims Provider using SharePoint 2010
Global Audiences. The first part described how to make sure Global Audiences can be used to
provide full security mechanism and not just targeting. At the authentication instant
(your experience may vary) we have added a claim for each audience a user is a member of,
and made sure the audience claim is discoverable in the People Picker.

At that point the claim could be added to a SharePoint Site access control list (ACL),
and the users properly authorized to the site. What is left to develop is a way for site owners and
administrators to find out which claims are available for a particular user. If you've had a chance
to look at Ted Pattison's proof of concept Audience Claim Provider, you've noticed he's included
a Claims Viewer to enumerate claims for an authenticated user. There's quite a few of them,
depending on the authentication method used to log in.

Let me digress and talk about claims in general: I try to think of claims as having two distinct
purposes - authentication and authorization. From this perspective, there are two classes of
claims - an Identity claim (a unique claim used for authentication and authorization) and other claims
(custom claims or authorization-only claims). That's oversimplification as there are other claims
(besides the Identity claim) that users have assigned depending on the authentication method.

It is the second class of claims (authZ claims) that the Audience claim belongs to. It is this class
of claims that enables the most interesting scenarios in Claims mode of operation in SharePoint 2010.
And it's the set of the authorization claims for a particular user that site owners or admins would
like to see to make informed decisions on security.

The Solution in a Nutshell

Get an identity claim for a user with the People Picker control and supply it to SPClaimProviderOperations.ClaimsForEntity(), which calls FillClaimsForEntity() method of the Custom Claims Provider(s) and returns an SPClaim[] array, containing the authorization claims (for all or specified Claims Providers) for the user. Here it is, a  Simply Working Solution™ in one sentence!
"Check Permissions" at /_layouts/user.aspx also performs a call to SPClaimProviderOperations.ClaimsForEntity().
Check Permissions

Displaying Audience Names

Since we are using Global Audience GUID as a claim value, the Claims Viewer will not show the audience names by default.
To add the names, let's populate a dictionary using AudienceManager.GetUserAudienceIDs(identityClaim, true, ca.RootWeb) in one call, and then add the Audience names to the Claims Viewer Grid.

Identity Claim Format

The last, but not the least is making sure that we pass the user identity to the Audience Manager properly in FillClaimsForEntity() in our Custom Claims Provider.
The entity.OriginalIssuer property will contain "SecurityTokenService" for the actual user that is authenticating, or "TrustedProvider" / "Windows" / "Forms" (see SPOriginalIssuerType) if SPClaimProviderOperations.ClaimsForEntity() call is performed.

Conclusion

And with the site owners and site collection administrators happy it's time to celebrate. Champagne, anyone?
Please contact me with questions and suggestions or leave a comment.
Last Updated on Saturday, 31 December 2011 16:38
 

Custom Claims Provider to the Rescue

E-mail Print PDF

SAML Authentication and the People Picker

Out of the box SharePoint claims provider in SAML mode (SPTrustedClaimProvider) resolves any string a user enters in the People Picker. And this is a good thing, since there's no "standard" user store for this authentication method.
In a live system (that end-users should learn to love) this presents several usability problems, but fortunately they are relatively easy to overcome. You guessed it - a Custom Claims Provider should be used.
There are several sample implementations of Custom Claims Providers already available, so you are not alone.
Let's look at a real-life implementation of a localized Custom Claims Provider with a User Profile service application as a back-end user / claims store.
Before we start, there's a few prerequisites and good-to-haves:

  • SharePoint Server 2010 license: any SKU that includes the User Profile service is suitable. You can still benefit from the Localization section if your SKU is SharePoint Foundation.
  • Multilingual users: if you don't have any multilingual requirements, skip to the User Profile App as a User Store section
  • SharePoint Web application in Claims mode: you'll get the most benefits if SAML authentication is used, and some benefits in Forms/Claims or Windows/Claims mode
  • I will assume you have the User Profile Application running smoothly and your global audiences are compiled and contain members, based on a chosen criteria
  • Please see the References and Credits section and familiarize yourself with the basics of Claims-based identity and access control, as well as the User Profile Application considerations

Localization

Thanks to good folks at Microsoft with the SharePoint Service Pack 1 it is now possible to fully localize a Custom Claims Provider.
To localize the DisplayName of a Custom Claims Provider, override FillDefaultLocalizedDisplayName, and for everything else there's your good old friend SPUtility.GetLocalizedString()

protected override void FillDefaultLocalizedDisplayName(CultureInfo culture, out string localizedName) {
localizedName = SPUtility.GetLocalizedString ("$Resources:DisplayNameText",
"ClaimsProviderResource", (uint)CultureInfo.CurrentUICulture.LCID);
}

Place the referenced resource files into a mapped SharePoint Layouts/Resources folder, for example:
ClaimsProviderResource.en-US.resx, ClaimsProviderResource.fr-FR.resx and ClaimsProviderResource.resx for an invariant culture
Now getting SPClaimProvider.DisplayName will result in a GetLocalizedDisplayName() call and the code in FillDefaultLocalizedDisplayName will run if you choose to override this method.

User Profile App as a User Store

The User Profile app can be synchronized with your company's User Directory of choice and augmented with other information from multiple sources. There's API to help retrieving information from this source, and almost no configuration required to make this work.
Besides the Custom Claims Provider, there's no other code to write, isn't it great?
Make sure the User Profile service app contains the objects you need and then use the UserProfileManager.Search() and UserProfileManager.ResolveProfile() methods to ensure scalability of the solution. These methods allow searching in several built-in User Profile attributes at once. The searchable profile properties are FirstName, LastName, PreferredName, UserName, Office, Title, Department, WorkEmail, SPS-SipAddress, and AccountName.
The search algorithm is quite robust and the beginning of every word is searched. This is very useful in a scenario when a profile property contains concatenated data: "John English | Jean Français".
private ProfileBase[] GetUserProfiles (string searchPattern, SPServiceContext ctxt, Boolean resolveMode)  {
ProfileBase[] Users;
UserProfileManager profileManager = new UserProfileManager(ctxt);
if (resolveMode) { Users = profileManager.ResolveProfile(searchPattern); }
else { Users = profileManager.Search(searchPattern); }
returnUsers;
}
Please note that there are scenarios when keeping the ability to resolve any claim is still useful:
- The User Profile service app may not be available
- The user might be too new and the User Profile synchronization has not been yet completed
- The user may not even be in the user store that is accessible from a SharePoing application (yes, you may need to access more than one user store in the Claims Provider)
- The Custom Claims Provider is a core solution and there are many places where the resolution should work, including non claims-based apps like the Central Administration.
So consider the following logic - if there's no exact match of the identity, you may still offer users a choice to resolve any string that they enter.

Identity Claim Value Type

As specified in the SharePoint Security Token Service Web Service Protocol Specification, the unique user identifier (the Identity Claim) is in the format similar to i:05.t|trusted-sts|user_identifier
Character at position 4 in the above example is "5", which corresponds to the "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" URI.
Most of the examples that you will encounter use this custom claim type, and for a good reason - there are only a handful URIs that are not reserved and result in a ASCII character:
  • Email address: "5", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" - use this claim type if you can
  • UPN: "e", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn" - also possible to use in some scenarios (Office 365)
  • Role: "-", "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" - may already be used, and can cause conflicts potentially
The majority of the other URIs or the URIs that are not listed in the spec will produce a UNICODE character at position 4, and this will cause problems with mapping User Profiles to records in the SharePoint Access Control Lists.
So once again, try to use an email address as an identifier if possible.

Audiences as a Flexible Claims Mechanism

Audiences are above the scope of a site collection and can be used for targeting in OOTB SharePoint. With SharePoint 2010 in claims mode, the audiences can also be used to make authorization decisions, just like user identities. This is a very flexible mechanism for creating compound or simple claims, and the possibilities on how to use the Audiences are almost endless.
SharePoint already includes most of the plumbing for using Audiences, so it's another Simply Working Solution™!
AudienceManager.Search() and AudienceManager.Resolve() methods should be used to ensure scalability of the solution, similar to the UserProfileManager methods.
Using the above methods requires elevating privileges and adding the Application pool identity to the Administrators list in the User Profile Service application.
Assign "Manage Audiences" permission to the account.
Please note with the SAML trusted provider (or Forms) SharePoint may still not allow to execute AudienceManager.Search() and AudienceManager.Resolve().  The workaround is to save the HttpContext, clear it and elevate privileges, then restore the HttpContext after the code finishes running.
Without the workaround using the methods results in an exception: System.UnauthorizedAccessException {"Access Denied")} ... Microsoft.SharePoint.SPSecurity.<>c__DisplayClass4.<RunWithElevatedPrivileges>b__2() at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode)
//IB: null http context is required in SAML (and Forms) mode so the AudienceManager.Search could return results
HttpContext httpCtxt = HttpContext.Current; HttpContext.Current = null;
//IB: Make sure the application pool accounts for the claims applications are in the User Profile service application administrators for the ProfileManager and AudienceManager methods to work.  
Permissions required: Manage Profiles, Manage Audiences
SPSecurity.RunWithElevatedPrivileges (delegate() {
using (SPSite ca = new SPSite (SPAdministrationWebApplication.Local.AlternateUrls[0].IncomingUrl)) {
SPServiceContext ctxt = SPServiceContext.GetContext(ca);
//IB: Users
profileMatches = GetUserProfiles(searchPattern, ctxt, false);
//IB: Audiences
AudienceManager mgr = new AudienceManager(ctxt);
audiences = mgr.Search(searchPattern); }
});
//IB: restoring the HTTP context
HttpContext.Current = httpCtxt;
//... Now profileMatches and audiences collections contain the items we need.

Claims Augmentation

You don't need to add the identity claim, as it is already present when a user authenticates. However, in order for the audiences claim or another custom claim to be useful, a set of claims need to be added after a user authenticates to SharePoint, and this is called "claims augmentation".
We will override FillClaimsForEntity() method and add Global Unique Identifiers for each audience the user belongs to by calling AudienceManager.GetUserAudienceIDs().
Please note the code will run in a context of a Security Token Service Application when an actual user authenticates, and in the context of the tested Web application when a call to SPClaimProviderOperations.ClaimsForEntity() is made (for example, a site owner uses "Check Permissions" function).
To debug the FillClaimsForEntity() code after deployment: reset IIS and attach your Visual Studio debugger to the SecurityTokenServiceApplicationPool w3wp process and the Web application w3wp process if you are debugging administration functionality.
To find out which w3wp process to attach to, run the commands below from the command prompt (on a Windows Server 2008 R2):
cd %systemroot%\system32\inetsrv
appcmd list wp

In the code sample below remember to provide the identityClaim in a suitable format (see the Identity Claim section). Please also check my post about the administration functionality to see a code sample: Windows/Claims and Trusted Provider/Claims configurations are considered.
using (SPSite ca = new SPSite (SPAdministrationWebApplication.Local.AlternateUrls[0].IncomingUrl)) {
SPServiceContext ctxt = SPServiceContext.GetContext(ca);
AudienceManager mgr = new AudienceManager(ctxt);
ArrayList userAudiences = mgr.GetUserAudienceIDs (identityClaim, false, ca.RootWeb);
foreach (AudienceNameID audienceNameId in userAudiences) { claims.Add (CreateClaim (AudienceClaimType, audienceNameId.AudienceID.ToString(), StringClaimType)); }
}

References and Credits

Implementing Claims-Based Authentication with SharePoint Server 2010 (whitepaper)
Claims-Based Identity and Access Control with a foreword by Steve Peschka: see SharePoint-related sections
Creating Custom Claims Providers in SharePoint 2010 by Ted Pattison and Scot Hillier on a sample Custom Claims Provider with Audience claim augmentation
Custom claims providers for People Picker (SharePoint Server 2010) on TechNet
Claims Based Identity & Access Control Guide with a sample Claims Provider implementation for SharePoint
If you must read only one* article on the User Profile setup and synchronization, read "Stuck on Starting": Common Issues with SharePoint Server 2010 User Profile Synchronization by Spencer Harbar! *Well, you ought to follow the links.
Special thanks to John Holliday and Spencer Harbar for validating the scalability of the solution.

Conclusion

Hopefully by this time you've got enough information to implement your own production-quality Custom Claims Provider.
Now move on to Claims Administration article to get an end-to-end working solution.

Please contact me with questions and suggestions on how to improve the articles (it's work in progress) or leave a comment.
Last Updated on Saturday, 31 December 2011 16:36
 






No comments:

Post a Comment