共用方式為


Claims Walkthrough: Writing Claims Providers for SharePoint 2010

Summary:  Discover how to write a claims provider to augment claims and provide name resolution. By using claims authentication, you can assign rights based on claims without knowing who a user is, or how they are authenticated. You have to know only the attributes of the user.

Applies to: Business Connectivity Services | Open XML | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio

Provided by:  Steve Peschka, Microsoft Corporation

Contents

  • Overview of Claims Providers

  • Part 1: Augmenting Claims and Registering Your Provider

  • Part 2: Adding Support for Hierarchy Nodes

  • Part 3: Searching Claims

  • Part 4: Supporting Name Resolution

  • Conclusion

  • Additional Resources

Click to get code  Download code: SqlClaimsProvider_MSDNExample.zip

Overview of Claims Providers

A claims provider in Microsoft SharePoint 2010 issues claims and packages claims into security tokens, that is, into the user's token. When a user signs in to Microsoft SharePoint Foundation 2010 or Microsoft SharePoint Server 2010, the user's token is validated and then used to sign in to SharePoint.

A claims provider in SharePoint 2010 is used primarily for two reasons:

  • To augment claims

  • To provide name resolution

Claims augmentation is part of a process that occurs after you log on to a claims authentication site. It is important to remember that the following can run with claims authentication in SharePoint 2010:

  • Windows claims (when you log on with NTLM or the Kerberos protocol)

  • Forms-based authentication claims (when you use an ASP.NET membership and role provider)

  • Security Assertions Markup Language (SAML) claims (when you log on by using a security token service (STS), such as Active Directory Federation Services (AD FS) 2.0)

After you log on, all the registered claims providers for that web application are triggered. When the claims providers are triggered, they can add claims that were not provided at logon time. For example, maybe you logged on by using Windows authentication. But we want to grant an additional claim that can be used for accessing an SAP system. This is where claims augmentation comes in.

Name resolution comes in two parts: the People Picker control and the type-in control. When you want to enable users to search for claims and find your claims, a claims provider enables you to do this by using the People Picker. The type-in control enables you to type in a user name, or a claim, and then click the resolve button. Your claims provider can examine that input and determine whether it is valid. If it is valid, the claims provider will "do the best thing" to resolve it.

So now that we have talked about what a claims provider can do in abstract terms, let's lay out the scenario that we will use to build a custom claims provider. For our particular scenario, we want to assign rights in our site collection based on a person's favorite basketball team. This is one of the great features about claims authentication that you will need to familiarize yourself with—we do not really care who a user is, or how a user is authenticated to the site; all we care about is an attribute of the user—what is the user's favorite team.

For our web application, I have enabled both Windows claims and forms-based authentication. I use the standard SQL membership and role provider for the forms-based authentication users, and I prepopulated my list of forms-based authentication accounts with user1 through user50. To simplify the scenario, a person's favorite team is determined in the following way:

  • The favorite team of all Windows users is Consolidated Messenger.

  • For forms-based authentication users:

    • The favorite team of user1 through user15 is Consolidated Messenger.

    • The favorite team of user16 through user30 is Wingtip Toys.

    • The favorite team of user31 through user50 is Tailspin Toys.

Part 1: 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.

<drive>:\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.

Tip

I strongly encourage you to use the helper methods when possible. They generally do more than merely using the default constructors, and you will generally find that things work more completely when you use them.

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.

Step 5: Creating the Claims Provider Feature Receiver

For this step, we start by adding a class to the project. This class will inherit from the SPClaimProviderFeatureReceiver base class. The implementation of it is straightforward. Therefore, I have copied all the code for this part from the sample code file in the following example.

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Claims;
using Microsoft.SharePoint.Diagnostics;


namespace SqlClaimsProvider
{
    public class SqlClaimsReceiver : SPClaimProviderFeatureReceiver
    {

        private void 
              ExecBaseFeatureActivated(
              Microsoft.SharePoint.SPFeatureReceiverProperties properties)
        {
            // Wrapper function for base FeatureActivated. Used because base
            // keyword can lead to unverifiable code inside lambda expression.
            base.FeatureActivated(properties);
        }

        public override string ClaimProviderAssembly
        {
            get 
            {
                return typeof(SqlClaims).Assembly.FullName;
            }
        }

        public override string ClaimProviderDescription
        {
            get 
            { 
                return "A sample provider written by Steve Peschka"; 
            }
        }

        public override string ClaimProviderDisplayName
        {
            get 
            {
                  // This is where we reuse that internal static property.
                return SqlClaims.ProviderDisplayName; 
            }
        }

        public override string ClaimProviderType
        {
            get 
            {
                return typeof(SqlClaims).FullName; 
            }
        }

        public override void FeatureActivated(
              SPFeatureReceiverProperties properties)
        {
            ExecBaseFeatureActivated(properties);
        }
    }
}

I think the following things are worth pointing out:

  • The ClaimProviderAssembly property and the ClaimProviderType property of the SPClaimProviderFeatureReceiver class are merely using type properties of the custom claims provider class that we wrote.

  • ClaimProviderDisplayName uses the internal static property that we created on our claims provider class. We created the property so that we could reuse it in our claims feature receiver.

  • The FeatureActivated event calls our special internal method for activating properties.

Step 6: Compiling the Assembly and Adding It to the Global Assembly Cache

I am making this step a manual step. But you can use a solution package if you prefer. In this scenario, I am doing everything on one server while I am in development mode. Therefore, I am doing it a bit differently than if I were ready to distribute it to my farm.

For now, ensure that your assembly is strongly named and then compile it. To move things along, use whatever your method of choice is to add the assembly to the global assembly cache (I use a post-build event and the Global Assembly Cache tool, gacutil.exe). Now we are ready to create and deploy our feature.

Step 7: Creating and Deploying the Claims Provider Feature

For this step, there is nothing specific to claims providers. We create a standard SharePoint feature and deploy it. The basics of how to create and deploy a claims provider feature is outside the scope of this article. The following XML is copied from my feature.xml file so that you can see what it resembles.

Note

For more information about how to install and uninstall features, see Installing or Uninstalling Features.

<Feature   Id="3E864B5C-D855-43e4-B41A-6E054C6C0352" 
           Title="Sql Claims Provider Feature" 
           Scope="Farm" 
           ReceiverAssembly="SqlClaimsProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=eaf1ba21464322ae" 
           ReceiverClass="SqlClaimsProvider.SqlClaimsReceiver" 
           xmlns="https://schemas.microsoft.com/sharepoint/" />

With my feature.xml file for reference, I created a directory named SqlClaimsProvider in my Features folder and installed my feature. Because it is a farm-scoped feature, it is automatically activated. The following is the command to do that in Windows PowerShell.

install-spfeature -path SqlClaimsProvider

Now all of the pieces are in place and we can try out the claims provider.

Step 8: Logging In to the Site and Trying It Out

For this final step, I use my web application that I described at the beginning of this article. It supports Windows claims and forms-based authentication claims.

Note

For more information about how to sign into SharePoint, see Incoming Claims: Signing into SharePoint.

To validate whether my claims are being appropriately augmented, I have created a Web Part that displays all my claims. I will show you screen shots of that, to validate that the claims augmentation is actually working.

First, I log on to the site by using Windows credentials. Remember from our code earlier that a Windows user should always be assigned Wingtip Toys as his or her favorite team. Figure 1 shows how this appears for the Windows user.

Figure 1. Claims augmentation for Windows user

Claims augmentation for Windows user

Figure 2 shows what it resembles for a forms-based authenticated user.

Figure 2. Claims augmentation for forms-based authenticated user

Claims augmentation for forms-based authenticated

Okay, this is cool stuff—we have our claims augmentation working. In part 2, I show how to start to work with the People Picker and also describe supporting and adding a hierarchy to the People Picker.

Part 2: Adding Support for Hierarchy Nodes

In Part 1: Augmenting Claims and Registering Your Provider, I showed how to create a custom claims provider, and how to do claims augmentation. In part 2, I show a simple way to add a hierarchy to the People Picker control. In and of itself, this is not necessarily interesting—but in part 3 you will see how we can use it.

Next, we go back to the class that we created for part 1, and we touch one property and one method of the SPClaimProvider class: the SupportsHierarchy property and the FillHierarchy method.

We want the SupportsHierarchy property to return true so that SharePoint knows that we can fill up a hierarchy, as shown in the following code snippet.

public override bool SupportsHierarchy
{
      get 
      { 
            return true; 
      }
}

After we set the SupportsHierarchy property to true, our FillHierarchy method will be called by SharePoint when the People Picker is displayed.

When we add a node to the hierarchy, we have to provide a display name and node identifier (ID). To help with that and to correspond to the favorite basketball teams that we have been using, I created some simple helper arrays to track label names and IDs. The following is what the code resembles. l also show the array of team names that we use for our favorite teams.

// Teams that we are using.
private string[] ourTeams = new string[] { "Consolidated Messenger", "Wingtip Toys", "Tailspin Toys " };

// Keys for our People Picker hierarchy.
private string[] teamKeys = new string[] { "empUS", "empEMEA", "empASIA" };

// Labels for our nodes in the People Picker.
private string[] teamLabels = new string[] { "US Teams", "European Teams", "Asian Teams" };

Now that we know what our keys and labels are, I will show you the FillHierarchy method that I use to add the nodes when the picker is displayed. This is for demonstration—I do not have a great reason to do it from a functional perspective for this particular scenario. 

One thing that is important to note here—if you are not creating identity claims, then a claim can be used to provision almost any security item in a SharePoint site except for a site collection administrator. Fortunately, we do get a clue when a search is being performed for a site collection administrator. When that happens, the entityTypes array will have only one item, and it will be Users. In all other circumstances you will normally see at least six items in the entityTypes array. If you are the default claims provider for a SPTrustedIdentityTokenIssuer, then you do issue identity claims, and therefore you can assign one of your identity claims to the site collection administrator. In this case, because this provider is not an identity provider, we will add a special check when we fill the hierarchy because we do not want to add a hierarchy we know we will never use.

protected override void FillHierarchy(Uri context, string[] entityTypes,
      string hierarchyNodeID, int numberOfLevels,
      Microsoft.SharePoint.WebControls.SPProviderHierarchyTree hierarchy)
{                        

// Ensure that People Picker is asking for the type of entity that we 
// return; site collection administrator will not return, for example.
if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole))
      return;

// Check to see whether the hierarchyNodeID is null; it is when the control 
// is first loaded, but if a user clicks on one of the nodes, it will return
// the key of the node that was clicked. This lets you build out a 
// hierarchy as a user clicks something, instead of all at once. So I 
// wrote the code for that scenario, but I am not using it in that way.  
// Which means that I could have just as easily used an 
// if (hierarchyNodeID == null) in this particular implementation, instead
// of a switch statement.
switch (hierarchyNodeID)
{
      case null:
            // When it first loads, add all our nodes.
            hierarchy.AddChild(new 
                  Microsoft.SharePoint.WebControls.SPProviderHierarchyNode(
                  SqlClaims.ProviderInternalName, 
                  teamLabels[0], 
                  teamKeys[0], 
                  true));

            hierarchy.AddChild(new
                  Microsoft.SharePoint.WebControls.SPProviderHierarchyNode(
                  SqlClaims.ProviderInternalName,
                  teamLabels[1],
                  teamKeys[1],
                  true));

            hierarchy.AddChild(new
                  Microsoft.SharePoint.WebControls.SPProviderHierarchyNode(
                  SqlClaims.ProviderInternalName,
                  teamLabels[2],
                  teamKeys[2],
                  true));
            break;
      default:
            break;
} 
}

So that is all there is to it—I am only checking whether the hierarchyNodeID is null, which it always is the first time that the control loads. If it is null, I add my nodes to the tree in the left pane. To get this code functional, I recompile my assembly, add it back into the global assembly cache, and do an IISRESET. Then I can hit the site and try modifying permissions to bring up the People Picker. Figure 3 shows the People Picker after you implement this code.

Figure 3. People Picker with hierarchy node

People Picker with hierarchy node

There is one thing worth pointing out here, too. If you are following closely, you may wonder why the People Picker shows my claims provider name as Steve's SQL Claims Provider, instead of the value of the Name property that you saw in the code, Basketball Teams. Well, simply stated, I am lazy. What you see for the name is what I originally named my claims provider. As I completed the scenario, I decided to change the name but did not perform all the steps to get it updated in the claims provider list. So, that is why you see that name there.

In part 3, we examine how to enable searching for claims in the People Picker by using your custom claims provider.

Part 3: Searching Claims

In the first two parts in this article, we have seen how to create a custom claims provider, how to do claims augmentation and register the provider, and how to add a hierarchy to the People Picker. In part 3, I describe how to implement searching for our claims in the People Picker with our custom claims provider. Without having a way to select our custom claim, we cannot provision permissions in the site based on who your favorite basketball team is.

First, I want to take a moment to really let that sink in, because this is an important point that is not well understood. Provisioning permissions based on an attribute of the user, instead of on who the user is, opens many possibilities. It can mean, as it does in this scenario, that I do not care who you are. I do not care how you logged in. I do not care whether you are a Windows user or a forms-based authentication user.

All I care about is who your favorite basketball team is. Here is a classic problem from Microsoft Office SharePoint Server 2007 that still exists in SharePoint 2010—when I am at work, I log on to my SharePoint site by using my Windows credentials, Steve Peschka. But when I go home, I hit the site via our extranet and I must use forms-based authentication.

For our scenario, assume that I am a forms-based authentication user, user1. What is the problem? To SharePoint, these are two users. There is no way in SharePoint Server 2007 or SharePoint 2010 to do some kind of user mapping, where we say, "The Steve Peschka Windows user and the user1 forms-based authentication user are really the same person."

Guess what? We no longer have to care. Why? Because we are not assigning permissions based on who the person is. We are assigning permissions based on what the person's favorite basketball team is. The scenario that I have implemented is simplified. But you could imagine a piece of corporate metadata that is associated with a person, and the claims provider doing a lookup to some other system to determine all the different identities that a person uses—Windows authentication, forms-based authentication, SAP, CRM, and so on—and being able to map some other identifier or set of claims to that identity. Those claims are then used to grant access to resources.

Do you want single sign-on between your different web applications? Again, user1 in web application 1 differs from user1 in web application 2. Who cares? As long as I am using my favorite basketball team claim, that person can move seamlessly between those web applications, because we are augmenting it every time they authenticate. I do not want to tell you it is perfect, because it is not. But it sure is closer to perfect than previous security models.

Okay, while that sinks in, let's start talking about supporting search in the People Picker. For our custom claims provider to support search, we must add support for the following properties and methods of the SPClaimProvider class: SupportsSearch, SupportsResolve, FillSearch, FillResolve (the overload with SPClaim as an input parameter), FillSchema, and FillEntityTypes.

The properties should be straightforward at this point, so let's examine them first, as shown in the following code.

public override bool SupportsSearch
{
      get 
      { 
            return true; 
      }
}

public override bool SupportsResolve
{
      get 
      { 
            return true; 
      }
}

The FillSearch method is probably the most interesting to you. Let's examine that in the following code.

protected override void FillSearch(Uri context, string[] entityTypes, 
      string searchPattern, string hierarchyNodeID, int maxCount,
      Microsoft.SharePoint.WebControls.SPProviderHierarchyTree searchTree)
{

// Ensure that People Picker is asking for the type of entity that we 
// return; site collection administrator will not return, for example.
if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole))
      return;

// The counter to track what node we are in; it will be used to call into
// our helper arrays that were covered in part 1 and part 2 of this article.
int teamNode = -1;

// NOTE: If we were not using hierarchies in the People Picker, we would
// probably use a list of PickerEntity instances so that we could
// add them all to the People Picker in one call. I have stubbed it out
// here for demonstration purposes so you can see how you 
// would do it. 
// Picker entities that we will add if we find something.
// List<PickerEntity> matches = new List<PickerEntity>();

// The node where we will place our matches.
Microsoft.SharePoint.WebControls.SPProviderHierarchyNode matchNode = null;

// Look to see whether the value that is typed in matches any of our teams.
foreach (string team in ourTeams)
{
      // Increment team node tracker.
      teamNode += 1;

      // Simple way to do a string comparison to the search criteria.
      // This way, all a person has to type in to find Consolidated Messenger is "b".
      if (team.ToLower().StartsWith(searchPattern.ToLower()))
      {
            // We have a match, create a matching entity.
            // This is a helper method that I will explain later.
            PickerEntity pe = GetPickerEntity(team);

            // If we did not have a hierarchy, we would add it here
            // by using the list described previously.
            // matches.Add(pe);

            // Add the team node where it should be displayed; 
            // ensure that we have not already added a node to the tree
            // for this team's location.
            if (!searchTree.HasChild(teamKeys[teamNode]))
            {
                  // Create the node so that we can show our match in there too.
                  matchNode = new 
                  SPProviderHierarchyNode(SqlClaims.ProviderInternalName,
                  teamLabels[teamNode],
                  teamKeys[teamNode],
                  true);

                  // Add it to the tree.
                  searchTree.AddChild(matchNode);
             }
             else
                  // Get the node for this team.
                  matchNode = searchTree.Children.Where(theNode =>
                  theNode.HierarchyNodeID == teamKeys[teamNode]).First();

            // Add the picker entity to our tree node.
            matchNode.AddEntity(pe);
      }
}

// If we were adding all the matches at once,that is, if we were not using
// a hierarchy, then we would add the list of matches here.
// if (matches.Count > 0)
//    searchTree.AddEntities(matches);
}

Again, the code is probably fairly self-explanatory. Basically, what is happening is that the user typed something in the search box in People Picker and clicked the search button. Our claims provider is called because our SupportsSearch property returns true. The method that is called is FillSearch. In that method, we examine what the user typed in, which is provided to us by the searchPattern input parameter. We examine all our team names and determine whether any of them start with the value that was typed in the search box. If one does, we create a new PickerEntity, either find or create a search tree hierarchy node with the location of that team, and then add our PickerEntity entity to that node.

Now, the previous code shows us using a special helper function named GetPickerEntity where we pass in the name of the team that we found. This is shown in the following code.

private PickerEntity GetPickerEntity(string ClaimValue)
{

// Use the helper function!
PickerEntity pe = CreatePickerEntity();

// Set the claim that is associated with this match.
pe.Claim = CreateClaim(SqlClaimType, ClaimValue, SqlClaimValueType);

// Set the tooltip that is displayed when you pause over the resolved claim.
pe.Description = SqlClaims.ProviderDisplayName + ":" + ClaimValue;

// Set the text that we will display.
pe.DisplayText = ClaimValue;

// Store it here, in the hashtable **
pe.EntityData[PeopleEditorEntityDataKeys.DisplayName] = ClaimValue;

// We plug this in as a role type entity.
pe.EntityType = SPClaimEntityTypes.FormsRole;

// Flag the entry as being resolved.
pe.IsResolved = true;

// This is the first part of the description that shows
// above the matches, like Role: Forms Auth when
// you do an forms-based authentication search and find a matching role.
pe.EntityGroupName = "Favorite Team";

return pe;
}

What happens here is that we are creating a new PickerEntity object. As the code comments indicate, the CreatePickerEntity method of the SPClaimProvider class is another helper function that SharePoint provides. Again, I strongly recommend that you use the helper functions when you can.

After we create our PickerEntity object, we need some way to describe what "it" is. In this case, "it" is a claim for a favorite basketball team. The way that we describe that to the system is to create a claim by using the same approach that we did for our claims augmentation code. This is why we passed in the team name to this helper function so that it could place the person's favorite team into our favorite team claim. After that, we set several properties on the PickerEntity object so that it displays and behaves as other claims that you see for Windows claims or forms-based authentication claims. Then, we return the PickerEntity that we created.

However, we are not completely finished. If we stopped at this point, we would see the claim in the People Picker and be able to select it. When you clicked the OK button to close the People Picker dialog box, you would see the claim in the type-in control. But it would appear with the red, wavy underline indicating that it is not resolved. And you would be unable to resolve it at that point.

What actually happens is that when you click the OK button on the People Picker dialog box to close it, SharePoint tries to make another call into your claims provider, to the FillResolve method. This is an overloaded method. The method that is called into when you close the People Picker dialog box is the method that contains a SPClaim input parameter. So, let's take a look at the implementation of that method, as shown in the following code.

protected override void FillResolve(Uri context, string[] entityTypes, 
      SPClaim resolveInput, 
      List<Microsoft.SharePoint.WebControls.PickerEntity> resolved)
{

// Ensure that People Picker is asking for the type of entity that we 
// return; site collection administrator will not return, for example.
if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole))
      return;

// Same sort of code as in search, to validate that we have a match.
foreach (string team in ourTeams)
{
if (team.ToLower() == resolveInput.Value.ToLower())
{
      // We have a match; create a matching entity with helper method.
      PickerEntity pe = GetPickerEntity(team);

      // Add it to the return list of picker entries.
      resolved.Add(pe);
}
}

The FillResolve method is straightforward. You may remember from Part 1: Augmenting Claims and Registering Your Provider in this article that the Value property of the SPClaim class contains the value for the claim. Because there may have been multiple items selected in the People Picker, as we are passed in a claim, we enumerate through our team list again to see whether the claim value matches one of our teams. If it does, we call our helper method to create a PickerEntity object, and then we add it to the list of PickerEntity instances for SharePoint. Now, we see a resolved entry in the type-in control after our selection in the People Picker.

There are two more methods that you must implement: the FillSchema method and the FillEntityTypes method of the SPClaimProvider base class. In the FillSchema method, we are adding the minimal property that we want to use in the People Picker, which is the display name. We do that by adding the display name to the schema.

In the FillEntityTypes method, we want to return the kind of claim or claims that our claims provider uses. In our case, we have been defining our claim type as SPClaimEntityTypes.FormsRole in the GetPickerEntity method. Therefore, I have added that role to the list of entity types, as shown in the following code.

protected override void 
FillSchema(Microsoft.SharePoint.WebControls.SPProviderSchema schema)
{
// Add the schema element that we need at a minimum in our picker node.
schema.AddSchemaElement(new 
      SPSchemaElement(PeopleEditorEntityDataKeys.DisplayName, 
      "Display Name", SPSchemaElementType.Both));
}

protected override void FillEntityTypes(List<string> entityTypes)
{
// Return the type of entity claim that we are using (only one in this case).
entityTypes.Add(SPClaimEntityTypes.FormsRole);
}

Figure 4 shows how the People Picker appears after we search for wingtip.

Figure 4. Searching in the People Picker

Searching in the People Picker

Figure 5 shows how the type-in control appears after we add our claim and then click the OK button.

Figure 5. Type-in control display after adding a claim

Type-in control display after adding a claim

From a code perspective, we are complete. But it is time to tie this back to the discussion at the start of the article. Now we can provision permissions based on who your favorite basketball team is. In this case, I have decided that people whose favorite team is Wingtip Toys should be able to contribute content to the site. In this case, I added this claim to the Members group. Figure 6 shows how that appears (Windows Claims is the name of the site collection; do not let that confuse you).

Figure 6. Adding a claim to the Members group

Adding a claim to the Members group

Remember what I have said about not caring any longer who you are or how you logged on. By way of example, I have not granted any Windows user, Windows group, forms-based authentication user, or forms-based authentication role rights anywhere on the site. I have added my claim for Wingtip Toys to the Members group, and I also added my claim for Consolidated Messenger to the Visitors group.

By way of proof, Figure 7 shows what my list of all groups resembles for the site collection.

Figure 7. List of group permissions for a site collection

List of group permissions for a site collection

Hopefully, this will really get you thinking about exactly how powerful claims authentication and claims providers can be. In the final part in this article, I show how to implement support for people typing a claim name into the type-in control.

Part 4: Supporting Name Resolution

In the first three parts of this article, we have implemented most of the support that is required to create an end-to-end claims provider. In this last part, I describe how to implement name resolution support in the type-in control.

To add name resolution support, we must implement the following property and method of the SPClaimProvider class: the SupportsResolve property and the FillResolve method.

The good news is, I already addressed how to implement the SupportsResolve property and one of the FillResolve method overloads in Part 3: Searching Claims. Therefore, this section is short. The following is the code for implementing the other overload of the FillResolve method that was not described in "Part 3: Searching Claims."

protected override void FillResolve(Uri context, string[] entityTypes, 
      string resolveInput, 
      List<Microsoft.SharePoint.WebControls.PickerEntity> resolved)
{

// Ensure that People Picker is asking for the type of entity that we 
// return; site collection administrator will not return, for example.
if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole))
      return;
 
// Same sort of code as in search, to validate that we have a match.
foreach (string team in ourTeams)
{
      if (team.ToLower() == resolveInput.ToLower())
      {
            // We have a match;, create a matching entity.
            PickerEntity pe = GetPickerEntity(team);

            // Add it to the return list of picker entries.
            resolved.Add(pe);
       }
}
}

If you have been following along, then this chunk of code is straightforward. If it is not, read through Part 3: Searching Claims again.

We are now code complete. If I bring up the type-in control, type a team name, and then click the resolve button, the following figures show how the type-in control appears before and after name resolution. Figure 8 shows what the type-in control resembles when you type the team name in.

Figure 8. Type-in control before clicking resolve

Type-in control before resolve

Figure 9 shows the type-in control after clicking resolve.

Figure 9. Type-in control after resolve

Type-in control after resolve

And with that, we are finished. Hopefully you will find this information useful as you start to build out your claims authentication plans.

Conclusion

You can use a claims provider in SharePoint 2010 to augment claims and provide name resolution. By using claims authentication, you can assign rights based on claims without having to know who a user is, or how they are authenticated; all that you have to know is the attributes of the users. You can, for example, use a piece of corporate metadata that is associated with a person, and have the claims provider do a lookup to some other system to figure out all the different identities that particular person uses—Windows, forms-based authentication, SAP, CRM, and so on—and map some other identifier or set of claims to that identity. Those claims are then used to grant access to resources.

Additional Resources

For more information, see the following resources: