Augmenting Claims and Registering Your Provider
The first thing that we will do is claims augmentation. As described previously,
after a person is authenticated, our claim provider fires. We use the rules that
I defined previously to add a claim (which basketball team is their favorite)
to the user token.
Use the following steps to write a claim provider. The example uses Microsoft Visual
Studio 2010 to create a class library application.
Step 1: Add References
First, add references to Microsoft.SharePoint and Microsoft.IdentityModel. I am assuming
that you can find Microsoft.SharePoint.dll in your SharePoint installation. The
Microsoft.IdentityModel.dll is installed as part of the Windows Identity Foundation.
The following is the full path of Microsoft.IdentityModel.dll in my installation.
\Program Files\Reference Assemblies\Microsoft\Windows Identity Foundation\v3.5\Microsoft.IdentityModel.dll
Step 2: Add using Statements and Class Inheritance
Now that we have our references, we have to add our using statements and define the
base class that our class inherits from. For this example, we use the following
using statements.
using Microsoft.SharePoint.Diagnostics;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Claims;
using Microsoft.SharePoint.WebControls;
Our class must inherit from the SPClaimProvider base class. The following code shows
how our class appears at the start; do not be confused by the name SqlClaimsProvider;
I was originally going to write it to work only with the SQL membership and role
provider, but later decided to make it work with Windows also.
namespace SqlClaimsProvider
{
public class SqlClaims : SPClaimProvider
{
public SqlClaims(string displayName)
: base(displayName)
{
}
}
}
Step 3: Adding the Required Implementation
Instead of stepping through all the interfaces that you must implement, you can pause
over the SPClaimProvider name in the previous code, click the drop-down arrow
that appears under S, and then select the Implement abstract class 'SPClaimProvider'
command.
Step 4: Implementing the Functionality Included with the Provider
After you implement the abstract class, you must implement five properties in the
SPClaimProvider class, regardless of what you will do: Name, SupportsEntityInformation,
SupportsHierarchy, SupportsResolve, and SupportsSearch.
Start with the Supports* properties. If you are doing only claims augmentation, the
only one that you must support is entity information. The property list in my
class resembles the following.
public override bool SupportsEntityInformation
{
get
{
return true;
}
}
public override bool SupportsHierarchy
{
get
{
return false;
}
}
public override bool SupportsResolve
{
get
{
return false;
}
}
public override bool SupportsSearch
{
get
{
return false;
}
}
Next, implement support for the Name property. You generally want to support both
a display name and an "internal" name by which your provider can be
referenced.
In this case, I use internal static properties so that I can share them with another
class that I will add later in the project. My implementation for the Name property
resembles the following.
internal static string ProviderDisplayName
{
get
{
return "Basketball Teams";
}
}
internal static string ProviderInternalName
{
get
{
return "BasketballTeamProvider";
}
}
public override string Name
{
get
{
return ProviderInternalName;
}
}
We have now finished all the basic implementation. On to more interesting things.
First, I set up an array to use in our claims provider for the teams. It is added
at the top of the class, as follows.
// Teams that we are using.
private string[] ourTeams = new string[] { "Consolidated Messenger", "Wingtip Toys", "Tailspin Toys " };
Next, we must implement the core of our claims provider. To do claims augmentation,
we add implementation code for the FillClaimsForEntity method, the FillClaimTypes
method, and the FillClaimValueTypes method in the SPClaimProvider class.
To start, we create some simple helper functions because we will call them throughout
the creation of our claims provider. These helper functions define the ClaimType,
which is basically our claim namespace or identifier, and the ClaimValueType,
such as string or int.
The following are the two helper functions that do that for us.
private static string SqlClaimType
{
get
{
return "http://schema.steve.local/teams";
}
}
private static string SqlClaimValueType
{
get
{
return Microsoft.IdentityModel.Claims.ClaimValueTypes.String;
}
}
At this point, what you can determine from this is that if our claim provider is
working, we will be adding a claim with a name of http://schema.steve.local/teams
and the value in that claim is a string.
From the previous code and information, we know that the value will be Consolidated
Messenger, Wingtip Toys, or Tailspin Toys. For the claim name itself, there are
no required rules for what the name must look like. We usually follow the format
schemas.SomeCompanyName.com/ClaimName. In this case, my domain is steve.local.
Therefore, I used schemas.steve.local, and teams is the property that I want
this claim to track.
The part where we actually add that claim comes next, in the FillClaimsForEntity
method of the SPClaimProvider class, as shown in the following code.
protected override void FillClaimsForEntity(Uri context, SPClaim entity,
List<SPClaim> claims)
{
if (entity == null)
throw new ArgumentNullException("entity");
if (claims == null)
throw new ArgumentNullException("claims");
// Determine who the user is, so that we know what team to add to their claim
// entity. The value from the input parameter contains the name of the
// authenticated user. For a SQL forms-based authentication user, it looks similar
to
// 0#.f|sqlmembership|user1; for a Windows claims user it looks similar
// to 0#.w|steve\\wilmaf.
// I will skip some uninteresting code here, to look at that name and determine
// whether it is a forms-based authentication user or a Windows user, and if it is
a forms-based authentication user,
// determine what the number part of the name is that follows "user".
string team = string.Empty;
int userID = 0;
// After the uninteresting code, "userID" will equal -1 if it is a Windows user.
// If it is a forms-based authentication user, it will contain the number that follows
"user".
// Determine what the user's favorite team is.
if (userID > 0)
{
// Plug in the appropriate team.
if (userID > 30)
team = ourTeams[2];
else if (userID > 15)
team = ourTeams[1];
else
team = ourTeams[0];
}
else
team = ourTeams[1];
// If they are not one of our forms-based authentication users,
// make their favorite team Wingtip Toys.
// Add the claim.
claims.Add(CreateClaim(SqlClaimType, team, SqlClaimValueType));
}
The main thing worth pointing out from that method is the very last line of code.
We take the input claims parameter, which is a list of SPClaim objects. We create
a new claim by using the CreateClaim helper method.
Finally, when we create the claim, we use the two helper methods that I described
previously: the SqlClaimType method and the SqlClaimValueType method.
Now that we have completed the most complex part of this code, there are two additional
methods that we must provide an implementation for: FillClaimTypes and FillClaimValueTypes.
These methods are easy to do because we are merely going to use our helper methods
for them. Their implementation resembles the following.
protected override void FillClaimTypes(List<string> claimTypes)
{
if (claimTypes == null)
throw new ArgumentNullException("claimTypes");
// Add our claim type.
claimTypes.Add(SqlClaimType);
}
protected override void FillClaimValueTypes(List<string> claimValueTypes)
{
if (claimValueTypes == null)
throw new ArgumentNullException("claimValueTypes");
// Add our claim value type.
claimValueTypes.Add(SqlClaimValueType);
}
I think those should be straightforward. Therefore, I will not explain them any more.
Now we have completed our basic implementation of a claims provider, which should
do claims augmentation for us. So how do I use it? The preferred method is to
use a claims feature receiver.
Implemented custom Sharepoint Custom Claims Provider
Step1:
Download the code from the link. It is the code written by Ted Pattison, a well renowned sharepoint security expert.
Step 2:
The custom provider class should be Implementing abstract class "SPClaimProvider". Understand the declarations part where we declare name of claim provider, its type,
Value and the name of the custom claim that we gona induce into claims repository
provided by STS.
internal const string ClaimProviderDisplayName = "Audience Claim Provider";
internal const string ClaimProviderDescription = "SharePoint 2010 Demoware from Critical Path Training";
protected const string AudienceClaimType = "http://www.wingtip.com/identity/claims/audience";
protected const string StringTypeClaim = Microsoft.IdentityModel.Claims.ClaimValueTypes.String;
public override string Name {
get { return ClaimProviderDisplayName; }
}
Step 3:
Now observe below 4 properties and 2 methods
public override bool SupportsEntityInformation {
get { return true; }
}
public override bool SupportsHierarchy {
get { return true; }
}
public override bool SupportsResolve {
get { return true; }
}
public override bool SupportsSearch {
get { return true; }
}
protected override void FillClaimTypes(List claimTypes) {
if (claimTypes == null) throw new ArgumentException("claimTypes");
claimTypes.Add(AudienceClaimType);
}
protected override void FillClaimValueTypes(List claimValueTypes) {
if (claimValueTypes == null) throw new ArgumentException("claimValueTypes");
claimValueTypes.Add(StringTypeClaim);
}
Though they look simple, these properties control the execution of the remaining
code. For example, SupportSearch property returns true. This means, when a search
happens in my sitecollection, the code in my claim provider will be executed
instead of default search code in sharepoint.
Step 4: We have 4 more abstract methods to override.
FillClaimsForEntity() - Method controls additional claims loaded/created for logged
in user
FillHierarchy() - Method controls the Claim provider hierarchy display in search
window.
FillSearch() - Method controls the search functionality in all people pickers in
site collection.
FillResolve() - Method controls functionality executed when you type in a name and
click resolve button (one with green tick mark).
Here is where my code sample is simplified for better understanding. Instead of involving
audience manager into scope, i simple created a array of strings representing
users on my machine.
internal string[] AvailableClaims = {"administrator","spuser","pratap" };
I will add extra claim with claim-type as "http://www.wingtip.com/identity/claims/audience"
and Claim-value as their name from this array.
Step 5: Look at the updated methods with simplest possible code.
protected override void FillClaimsForEntity(System.Uri context, SPClaim entity, List claims) {
if (entity == null) throw new ArgumentException("entity");
if (claims == null) throw new ArgumentException("claims");
string userLogin = (entity.Value.Split('\\'))[1];
foreach(string claim in AvailableClaims)
{
if (userLogin.ToLower()==claim.ToLower()) {
claims.Add(CreateClaim(AudienceClaimType, claim, StringTypeClaim));
}
}
}
protected override void FillHierarchy(System.Uri context, string[] entityTypes, string hierarchyNodeID,
int numberOfLevels, SPProviderHierarchyTree hierarchy) {
// No additional nodes need to be added to the People Picker
}
protected override void FillSearch(System.Uri context, string[] entityTypes, string searchPattern, string
hierarchyNodeID, int maxCount, SPProviderHierarchyTree searchTree) {
if (EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole)) {
List audiences = new List();
foreach (string claim in AvailableClaims) {
if (claim.StartsWith(searchPattern, StringComparison.CurrentCultureIgnoreCase))
audiences.Add(claim);
}
foreach (string audienceName in audiences)
searchTree.AddEntity(CreatePickerEntityForAudience(audienceName));
}
}
protected override void FillResolve(System.Uri context, string[] entityTypes, string resolveInput, List resolved)
{
List audiences = new List();
foreach (string claim in AvailableClaims)
{
if (claim.StartsWith(resolveInput, StringComparison.CurrentCultureIgnoreCase))
audiences.Add(claim);
}
foreach (string audienceName in audiences)
{
resolved.Add(CreatePickerEntityForAudience(audienceName));
}
}
Output:
Now when i log into application, below are the claims created because of my code

Look at the people picker search window changed from what to what.

If you observe we haven't implemented any thing in FillHierarchy() Method as
we didn't have any child nodes to display in our claim provider. But you
can implement this method if you want to display some thing like below.

Look how the resolving of names got changed.

If you observe the first one is resolved based on my windows credentials. But the
second one is resolved based on my claim value.
Last obvious question, what we achieved from this?
Well let me show you. i have a document which has to be displayed for people whose
age>18.
we need to do 2 simple things.
1- Break default inheritance of permissions on that document.
2- Assign the permissions to "Youth" and "MiddleAge" claims.
Now anyone who login with age less than 18 will not be able to see it. Now you don't
need to bother about no of people and their ids or their AD Associations. Just
follow our custom claim value.
Demo: I have a document library with 3 documents. I want to show only 2 documents
to user Pratap.

Go to those two documents and manage their permissions.

Remove all users from access list and remove permission inheritance.

Add access to specific claim type, in this case to "pratap".
Now, when pratap login, this is how the document library will be displayed.

Task accomplished. We have learned about Custom Claims Provider, significance of
each method in it and Importantly how granular level security can be implemented
using custom claims.