共用方式為


Creating Custom Claims Providers in SharePoint 2010

Summary: Support for claims-based security in Microsoft SharePoint 2010 enables SharePoint developers to create custom claims providers for use to add custom claims to the security token of the current user. Site administrators can also see and use these custom claims when they configure permissions on securable objects such as sites, lists, items, and documents. Use the sample application associated with this article to learn how to create a custom claims provider by using the SharePoint development tools in Microsoft Visual Studio 2010 that issues custom claims based on whether the current user is a member of an Audience created by using the User Profile web service of SharePoint 2010.

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

Provided by:  Ted Pattison, Critical Path Training, LLC (SharePoint MVP) | Scot Hillier, Critical Path Training, LLC (SharePoint MVP)

Content

  • Introduction to Creating Custom Claims Providers in SharePoint 2010

  • Installing and Testing the AudienceClaimProvider Claims Augmentation Provider

  • Custom Claims Provider Walkthrough: AudienceClaimProvider

  • Conclusion

  • Additional Resources

  • About the Author

Click to get codeDownload the code: MSDN Claim Provider Sample (from Critical Path Training)

Introduction to Creating Custom Claims Providers in SharePoint 2010

Microsoft SharePoint 2010 introduces a new claims-based security model in Microsoft SharePoint Foundation 2010. The support for claims-based security is scoped to the level of the web application. Whenever you create a SharePoint web application, you must decide whether the new web application will run under Claims Mode or under the older SharePoint 2007 mode known as Classic Mode. When a web application that runs in Claims Mode authenticates a user, it creates a security token for the user by using Security Assertion Markup Language (SAML). For example, if the user is authenticated by using Windows Authentication against an Active Directory account, the SAML token will contain the security identifier (ID) for the user's Active Directory account and the security ID for all the Active Directory groups that the user is in.

What is powerful about creating a custom claims provider is that your code is called each time the hosting web application authenticates the user and creates a SAML token. Your code is then given the opportunity to do the following:

  • Determine who the user is

  • Query for information about the user

  • Add claims about the user into the resulting SAML token

You can create two main types of claims providers: people picker claims provider and claims augmentation provider.

You can use the first type, a people picker claims provider, to enable a user to search and resolve entities in the standard people editor controls provided by SharePoint Foundation. These types of claims providers are used in scenarios where the authentication provider is SAML-based, and rich search and resolve experience is needed. For example, some of the SAML-based authentication providers that are included with SharePoint Foundation, such as the one used in forms-based authentication, do not offer rich search and resolve functionality. You can develop a people picker claims provider to offer a better search and resolve experience in sites that use forms-based authentication.

The second type of claims provider is a claims augmentation provider, which is used to augment the claims (such as attributes) of a user. For example, a claims augmentation provider can add custom claims to a user's security token, which provides more flexibility when you configure permissions on sites, lists, items, and documents. When the custom claims you create must be exposed in a people picker (search and resolve), this type of claims provider should also implement this aspect of the claims provider interface, as shown in the following Custom Claims Provider Walkthrough: AudienceClaimProvider and in the Visual Studio 2010 sample project (MSDN Claim Provider Sample (from Critical Path Training)) that accompanies this article.

The sample Visual Studio 2010 project is a SharePoint project named WingtipClaimProviders. This project contains a claims augmentation provider named AudienceClaimProvider, which is built on top of the Audience feature that is supplied by the User Profile web service in SharePoint Server 2010. When a user is authenticated against a web application that is running in Claims Mode, the AudienceClaimProvider claims augmentation provider checks whether the current user is a member of any audiences. For each audience that the user is in, AudienceClaimProvider adds a claim to the user's SAML token to indicate that the user is a member of that audience.

A claims provider has another important job along with adding claims to a user's SAML token during authentication. A claims provider must also implement several different methods to interact with the People Picker page that is provided by SharePoint Foundation. A custom claims provider supplies an important point of interaction for SharePoint site administrators who are configuring permissions on lists and sites.

The idea of a custom claims provider represents a paradigm shift in the configuration of security permissions. For example, a site collection owner is no longer confined to configuring permissions solely in terms of users and groups. Now a site collection owner can configure security permissions in terms of new abstractions. You could create a claims provider that can query a back-end system for the user's birth date and make the claim as to whether this user is over 21 years old. Another claims provider could look up information about a user and make a claim about whether the user is a naturalized citizen of a particular country, such as the United States. In this sample, the claims provider creates claims that verify that the current user is a member of one or more audiences. As you will see, the presence of these claims in SAML tokens is what makes it possible to configure site and list permissions in terms of audiences, which is something that was not possible in SharePoint Server 2007 and Windows SharePoint Services 3.0.

This article walks you through the process of deploying and testing the AudienceClaimProvider claims augmentation provider. The walkthrough assumes the following:

  • You have installed SharePoint 2010.

  • You have correctly configured the User Profile web service and created some audiences.

To properly test AudienceClaimProvider, you must be running under the context of a user who is in one or more audiences. You must also test the sample in a SharePoint site that runs inside a web application that is running in Claims Mode, not in Classic Mode.

Installing and Testing the AudienceClaimProvider Claims Augmentation Provider

Installation of the sample claims provider is fairly straightforward, as described in the following steps.

  1. Ensure that you have created a few audiences and that you are logged on as a user in at least one audience.

  2. In Visual Studio 2010, open the WingtipClaimProviders.sln project.

  3. In the Properties sheet for the project, modify the Site URL property to point to a test site in your local farm. Ensure that this test site is running in a web application that was created in Claims Mode.

  4. In Solution Explorer, right-click the WingtipClaimProviders project node, and then click Deploy.

    This action deploys the project's solution package, which will install WingtipClaimProviders.dll in the global assembly cache and install a farm-level feature that registers the claims provider in the current farm. For testing purposes, the solution package also deploys an application page named ClaimsViewer.aspx and a site-level feature that adds a menu item to the Site Actions menu. This enables you to navigate to ClaimsViewer.aspx, so that you can see the claims that are added to the current user's SAML token.

  5. Run an iisreset operation.

  6. Navigate to the test site in the browser.

  7. On the Site Actions menu, click Wingtip Claims Viewer. A page opens that displays all the claims in the SAML token for the current user. You should be able to verify that there is a claim for each audience of which the current user is a member.

    Figure 1. SAML token for current user

    SAML token for current user

  8. On the Site Actions menu, click Site Permissions.

  9. On the ribbon on the Site Permissions page, click the Grant Permission button. This action displays the Grant Permission page, which enables you to configure permissions in terms of users and groups. This page also enables you to configure permissions in terms of claims. Notice the two small buttons under the input control for Users/Groups. The button with the person and the check mark is the Check User button, which enables you to resolve a user, group, or claims from text that you have typed. The button with the open book is the Browse button, which displays the People Picker page.

    Figure 2. Grant Permissions page

    Grant Permissions page

  10. Click the Browse button on the Grant Permissions page to open the People Picker page. When the People Picker page opens, type the name of an audience in the Find text box, and then click the search button to the right. Ensure that your search text matches an audience in your farm. The search should find one or more audiences and display them in the column on the right. Select an audience, and then click Add at the bottom of the page. The audience should then appear with an underlined format in the text box to the right. Click OK to close the People Picker page and return to the Grant Permissions page.

    Figure 3. People Picker page

    People Picker page

  11. On the Grant Permissions page, you should see the audience in an underlined format in the Users/Groups box. Select a permission to grant to the audience, and then click OK.

At this point, you have configured permissions so that they are granted to any user who is a member of the audience you selected. From this example, you can see that you can now use audiences as first-class security principals when you are configuring permissions on sites, lists, items, and documents.

Custom Claims Provider Walkthrough: AudienceClaimProvider

The SharePoint project that is associated with this article is named WingtipClaimProviders.sln. It contains a class named AudienceClaimProvider that demonstrates how to create a custom claims provider, and a farm-level feature named AudienceClaimProviderRegistration that is used to register and unregister the claims provider in the local SharePoint farm. The project also contains an application page named ClaimsViewer.aspx, and a feature named MainSite that adds a Site Actions menu item to navigate to this application page. When you are creating a custom claims provider, the application page and feature are not required, but they are included so that you can see all the claims for the current user when you are testing the behavior of the code.

Figure 4. WingtipClaimProviders.sln in Solution Explorer

WingtipClaimProviders.sln in Solution Explorer

The first thing to do when you create a custom claims provider is to create a new class that inherits from SPClaimProvider. You must also add a public constructor that takes a single string parameter and passes it to the base class constructor, as shown in the following code.

namespace WingtipClaimProviders {
  class AudienceClaimProvider : SPClaimProvider {
    public AudienceClaimProvider(string displayName)
      : base(displayName) {
    }
  }
}

When creating a custom claims provider, you typically must define one or more claim types. A claim type is identified by a unique string, and by convention it is formatted as a URI that identifies the company that created it. Each claim type must also define a type for its underlying value, which can be defined in terms of the ClaimValueTypes enumeration, as shown in the following code.

// This is the only claim type supported by this provider.
protected const string AudienceClaimType = "http://www.wingtip.com/identity/claims/audience";
protected const string StringTypeClaim = Microsoft.IdentityModel.Claims.ClaimValueTypes.String;

A claims provider requires a name and a description for registration. The AudienceClaimProvider provides these values using constant fields, as shown in the following code.

internal const string ClaimProviderDisplayName = "Audience Claim Provider";
internal const string ClaimProviderDescription = "SharePoint 2010 Demoware from Critical Path Training";

When you create a custom claims provider by inheriting SPClaimProvider, there are several methods and properties that you must override. For example, each claims provider must implement the read-only Name property, as shown in the following code.

public override string Name {
  get { return ClaimProviderDisplayName; }
}

You must override four methods that return Boolean values to indicate the functionality that the claims provider is able to support. The AudienceClaimProvider claims augmentation provider returns true for each of these methods, as shown in the following code.

public override bool SupportsEntityInformation {
  get { return true; }
}
public override bool SupportsHierarchy {
  get { return true; }
}
public override bool SupportsSearch {
  get { return true; }
}
public override bool SupportsResolve {
  get { return true; }
}

Now we will walk through each of the remaining overridable methods, which will bring the claims provider to life. We will start with two methods named FillClaimTypes and FillClaimValueTypes. Both methods pass a List of strings as an incoming parameter, and expect you to add one or more strings to this list. The FillClaimTypes method is implemented by adding the string that identifies the custom claim type. The FillClaimValueTypes method is implemented by adding the string that indicates that the custom claim type has a string-based value, as shown in the following code.

protected override void FillClaimTypes(List<string> claimTypes) {
  if (claimTypes == null) throw new ArgumentException("claimTypes");
  claimTypes.Add(AudienceClaimType);
}
protected override void FillClaimValueTypes(List<string> claimValueTypes) {
  if (claimValueTypes == null) throw new ArgumentException("claimValueTypes");
  claimValueTypes.Add(StringTypeClaim);
}

The next method to implement is FillEntityTypes. Like the previous two methods, this method passes you a List of strings to which you can add one or more strings. AudienceClaimProvider implements this method by adding a single string to indicate that our claims provider creates claims based on form roles, as opposed to a claim that might identify a user or a Windows group, as shown in the following code.

protected override void FillClaimsForEntity(System.Uri context, SPClaim entity, List<SPClaim> claims) {
  claims.Add(CreateClaim(AudienceClaimType, "Hello World", StringTypeClaim));  
}

At this point, things get a bit complicated. The FillClaimsForEntity method does not execute inside the same web application or the same application pool as the site that the user is attempting to access. Furthermore, the FillClaimsForEntity method does not run under the identity of the current user who is being authenticated. Instead, the FillClaimsForEntity method runs in a separate application pool named SecurityTokenServiceApplicationPool under the identity of a service account.

The AudienceClaimProvider implements FillClaimsForEntity by creating a SPServiceContext object that is based on an SPSite object that is created from the URL to the Central Administration application. This approach provides flexibility because it does not require hard-coding of any URLs. The code then creates an instance of the SharePoint Server 2010 AudienceManager class and enumerates through each audience to determine whether the current user is a member of that audience.

There is one more complexity concerning the identity of the current user. Although you can obtain the identity of the current user by querying the Value property of the incoming parameter named entity, this string-based identity has a prefix of 0#.w|, which indicates that it is an encoded claims-based identity. The following code shows you how to extract the decoded logon name, which makes it possible to call the IsMember method of the Audience class.

protected override void FillClaimsForEntity(System.Uri context, SPClaim entity, List<SPClaim> claims) {
  if (entity == null) throw new ArgumentException("entity");
  if (claims == null) throw new ArgumentException("claims");

  // Decode user name from SAML token.
  string userLoginEncoded = SPUtility.FormatAccountName("i", entity.Value);
  string userLogin = SPClaimProviderManager.Local.DecodeClaim(userLoginEncoded).Value;

  // Determine whether current user is in any audiences.
  using (SPSite siteCollection = new SPSite(CentralAdminUrl)) {
    SPServiceContext ctx = SPServiceContext.GetContext(siteCollection);
    AudienceManager mgr = new AudienceManager(ctx);
    foreach (Audience audience in mgr.Audiences) {
      if (audience.IsMember(userLogin)){

        // If so, create a new claim and add it to SAML token of current user.
        claims.Add(CreateClaim(AudienceClaimType, audience.AudienceName, StringTypeClaim));
      }
    }
  }
}

At this point, we have addressed the code that adds claims to the user's SAML token. Now, we also must implement the methods of the SPClaimProvider class that interact with the People Picker page.

The first method you must override to accomplish this is FillSearch. This method is called when a user runs a search in the People Picker page. A key observation is that a custom claims provider has the opportunity to return entities in search results that represent custom claims, such as an audience. This search functionality is what enables a site administrator to discover a custom claim and use it to grant permissions on securable objects, such as sites and lists.

When the FillSearch method executes, the text that the user typed into the search text box is passed in the searchPattern parameter. The implementation in AudienceClaimProvider enumerates through each audience and finds each one that matches the pattern. It then creates a picker entity object for each audience match by using a utility method named CreatePickerEntityForAudience, and adds the picker entity object to the column on the right side of the People Picker page by calling the AddEntity method of the incoming searchTree parameter, as shown in the following code.

protected override void FillSearch(System.Uri context, string[] entityTypes, 
                                   string searchPattern, string hierarchyNodeID, 
                                   int maxCount, SPProviderHierarchyTree searchTree) {
  if (EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole)) {
    List<string> audiences = new List<string>();

    // Get SPSite object for Central Admininistration.
    using (SPSite ca = new SPSite(CentralAdminUrl)) {

      // Get SharePoint Server 2010 Audience Manager.
      SPServiceContext ctx = SPServiceContext.GetContext(ca);
      AudienceManager mgr = new AudienceManager(ctx);

      // Determine if search string matches any audience names.
      foreach (Audience audience in mgr.Audiences) {
        if (audience.AudienceName.StartsWith(searchPattern, StringComparison.CurrentCultureIgnoreCase))

          // Add any audiences that match search string.
          audiences.Add(audience.AudienceName);
      }
    }

    // Add picker entity for each audience that matches.
    foreach (string audienceName in audiences)
      searchTree.AddEntity(CreatePickerEntityForAudience(audienceName));
  }
}

At first, it might be confusing when you begin to think about picker entity objects. However, you should consider a picker entity object as a representation of a security principal that could be a user, a Windows group, a forms role, or a custom claim. What is important to remember about SharePoint Foundation is that any picker entity object can be used to configure permissions. Furthermore, when you assign a permission to a custom claim or add it to a SharePoint group within a specific site collection, SharePoint Foundation must create a profile for the claim in a special hidden list known as the User Information List. This profile can be seen on the Users and Groups page, and can track several different fields, such as Display Name, Email, Work Phone, and Department.

When you create a custom claims provider, you must implement a method named FillSchema. This method enables you to specify the SharePoint entity profile fields that you want to track in the entity picker objects that represent your custom claims. AudienceClaimProvider provides the minimal implementation and adds the field for Display Name by calling the AddSchemaElement method, as shown in the following code.

protected override void FillSchema(SPProviderSchema schema) {

  // Add schema element for display name.
  schema.AddSchemaElement(new SPSchemaElement(
                              PeopleEditorEntityDataKeys.DisplayName,
                              "Display Name",
                              SPSchemaElementType.Both));
}

Now that you have seen how to add fields to the schema for a claims provider's entity picker objects, we can walk through the utility method named CreatePickerEntityForAudience that was called from the FillSearch method. The method implementation begins by creating a new PickerEntity object and initializing it with an SPClaim object, which is created by calling the CreateClaim method provided by the base class SPClaimProvider. Most of the other properties of the PickerEntity object supply text somewhere inside the user interface of the People Picker page. However, it is important to observe that the CreatePickerEntityForAudience method assigns the name of the audience to the DisplayName item of the EntityData collection. This display name value actually is stored in the User Information List in the claims profile. In some scenarios, in addition to the Display Name, it might make sense to include other entity fields, such as Email and Work Phone.

private PickerEntity CreatePickerEntityForAudience(string audience) {

  // Create and initialize new Picker Entity.
  PickerEntity entity = CreatePickerEntity();
  entity.Claim = CreateClaim(AudienceClaimType, audience, StringTypeClaim);
  entity.Description = ClaimProviderDisplayName + ":" + audience;
  entity.DisplayText = audience;
  entity.EntityType = SPClaimEntityTypes.FormsRole;
  entity.IsResolved = true;
  entity.EntityGroupName = "Wingtip Audience";
  entity.EntityData[PeopleEditorEntityDataKeys.DisplayName] = audience;
  return entity;
}

At this point, there is only one more method to address, named FillResolve. However, you must implement it twice because it is overloaded with two different signatures. The first implementation of FillResolve is the one that is called by SharePoint Foundation when a user clicks the OK button on the People Picker page, which then returns to the Grant Permissions page. This is the implementation that has an SPClaim object as the third parameter. Because any audience claim has already been resolved by the AudienceClaimProvider class on the People Picker page, the implementation is fairly simple, as shown in the following code.

protected override void FillResolve(System.Uri context, 
                                    string[] entityTypes, 
                                    SPClaim resolveInput, 
                                    List<PickerEntity> resolved) {
  if (resolveInput.ClaimType == (AudienceClaimType))
    resolved.Add(CreatePickerEntityForAudience(resolveInput.Value));
}

The second implementation of FillResolve is more complex. It is called when a user types text into the Users/Groups input control on the Grant Permissions page, and then clicks the Check User button. In other words, a user is trying to resolve a user, group, or claim without using the People Picker page. The third parameter is not an SPClaim object, but just a string parameter that contains the text that the user has typed. This implementation of FillResolve takes the text input the user has typed and enumerates through each audience looking for a match. If a match is found, a picker entity is created by using the CreatePickerEntityForAudience method, and it is added to the List of resolved PickerEntity objects. When your code resolves text into a picker entity, the user is able to recognize it because the text is underlined.

protected override void FillResolve(System.Uri context, 
                                    string[] entityTypes, 
                                    string resolveInput, 
                                    List<PickerEntity> resolved) {
  List<string> audiences = new List<string>();
  using (SPSite ca = new SPSite(CentralAdminUrl)) {
    SPServiceContext ctx = SPServiceContext.GetContext(ca);
    AudienceManager mgr = new AudienceManager(ctx);
    foreach (Audience audience in mgr.Audiences) {
      if (audience.AudienceName.StartsWith(resolveInput, StringComparison.CurrentCultureIgnoreCase))
        audiences.Add(audience.AudienceName);
    }
  }
  foreach (string audienceName in audiences) {
    resolved.Add(CreatePickerEntityForAudience(audienceName));
  }
}

At this point, we have walked through the entire implementation of the AudienceClaimProvider. The only thing that remains to examine is how a claims provider is registered. Although it would be possible to write code to explicitly register and unregister a claims provider, SharePoint Foundation provides a utility class named SPClaimProviderFeatureReceiver that does the work for you. You can start by creating a new feature in a SharePoint project and changing the scope to farm scope. Next, add a feature receiver. After you add the feature receiver class, change the base class of the feature receiver from SPFeatureReceiver to SPClaimProviderFeatureReceiver. The reason that this approach makes things easier for you is that the base class SPClaimProviderFeatureReceiver provides implementations of FeatureActivated and FeatureDeactivating, which register and unregister the claims provider in the local farm. All you have to do is override four properties to supply the base class with information about the claims provider to register, as shown in the following code.

namespace WingtipClaimProviders.Features.AudienceClaimProviderRegistration {
  [Guid("24535b36-fee1-4285-a33e-b99e3e52b1e9")]
  public class AudienceClaimProviderRegistrationEventReceiver : SPClaimProviderFeatureReceiver {
    public override string ClaimProviderDisplayName {
      get { return AudienceClaimProvider.ClaimProviderDisplayName; }
    }
    public override string ClaimProviderDescription {
      get { return AudienceClaimProvider.ClaimProviderDescription; }
    }
    public override string ClaimProviderAssembly {
      get { return this.GetType().Assembly.FullName; }
    }
    public override string ClaimProviderType {
      get { return typeof(AudienceClaimProvider).FullName; }
    }  
  }
}

Conclusion

The ability to create custom claims providers for Microsoft SharePoint 2010, described in this article, enables you to move beyond simple security schemes where permissions can be configured only in terms of user account names and groups. By adding custom claims into the SAML security tokens of your users, you can make it possible for site administrators to configure security permissions in terms of new abstractions—such as Users Over 21, Naturalized United States Citizens, Managing Partners—and, as shown in this walkthrough and in the accompanying sample, Audiences, which are defined by using SharePoint 2010.

About the Author

Ted Pattison is an author, instructor, and co-founder of Critical Path Training, a company dedicated to education on SharePoint technologies. As a Microsoft SharePoint Most Valuable Professional (MVP), Ted frequently works with the Microsoft Developer Platform Evangelism group to research and author SharePoint training material for developers early in the product life cycle while in its alpha and beta stages. Ted also writes a developer-focused column for MSDN Magazine titled Office Space.

Additional Resources

For more information, see the following resources: