November 2011

Volume 26 Number 11

SharePoint Security - Custom Claims-Based Security in SharePoint 2010

By Ivory Feng | November 2011

Microsoft SharePoint 2010 introduced a new claims-based identity model for building claims-aware applications. When a user comes to a claims-aware application, she presents an identity to the application as a set of claims. These claims enable more granular authorization to content. Thus, users with certain attributes, such as Country Code and Department Code, are granted access to the appropriate content.

Implementing claims-based security at the enterprise level presents some interesting challenges. We’ll explore the process of building a custom claims provider (CCP) in SharePoint, integrating this with Microsoft FAST Search Server 2010 for SharePoint, managing claims-enabled content from a user’s perspective and avoiding certain gotchas. As we do this, you’ll learn how to design and implement a dynamic custom claims-based security model in SharePoint 2010. We’ll use the Contoso company’s human resource portal, ContentWeb, for our example.

Overview of the Application

Contoso is a global company with close to 100,000 employees, and ContentWeb holds a wealth of information that content authors need to manage on a daily to weekly basis. In most cases, the content is applicable to folks in one country but not another, to one discipline but not another, or to all Contoso employees. Some of the content, however, is sensitive data that needs to be available to only a small subset of employees (such as VPs).

To cover such a broad audience, ContentWeb uses SharePoint Groups (SPGs) with custom claims to explicitly secure and target content. Claims specific to Contoso’s needs are stored in a database and used to define a user’s claim “set.” It is this set of claims that determines what an employee can and can’t see. An administrative interface lets the information workers that support the portal change or augment their own claims set, giving them the ability to view the portal as a different user. This powerful custom feature is called View Portal As (VPA).

ContentWeb uses claims-based Windows authentication in SharePoint 2010 and FAST Search Server 2010 for SharePoint as the search engine in a federated enterprise search environment.

Why a CCP?

When a user logs into ContentWeb, the claims-based Windows authentication provides a set of claims from Active Directory, which acts as the identity provider. However, these claims alone are not sufficient for ContentWeb to secure and target content. ContentWeb requires additional user information from enterprise data repositories such as SAP to build a full claim set that describes the user. Thus, we need a CCP to augment the claims. We use these custom claims to explicitly secure and target content to the user.

Having a CCP allows Contoso to determine whatclaim types and values it wants to use to describe employees, vendors and others it works with. This model is scalable; it allows Contoso to efficiently change over time as its business changes.

Because you can’t easily edit the claims coming from Active Directory, you also need a CCP to support VPA. VPA is primarily used when an authorized person (User A) wants to view the portal as another person (User B). In such cases, the custom claims provider adds User B’s custom claims into User A’s claim set, thus authorizing User A to view content that User B can access.

The claims a user possesses are attributes that represent his “profile.” Users can have both simple and compound claims. Simple claims, such as CountryCode and UserType, are easy to understand. ContentWeb uses a predefined set of simple claims—or, more precisely, simple claim types. However, sometimes an employee needs to be granted permission to content that has more than one claim type; for example, CountryCode=US AND UserType=FullTimeEmployee—the AND condition. In that case, you create compound claims.

Out of the box, SharePoint 2010 evaluates claims in a user’s claim set using OR logic only. This means SharePoint 2010 supports only the OR condition for security and targeting, and that’s not always sufficient. Let’s look at an example. ContentWeb has both a CountryCode claim and a UserType claim. It needs to secure some content for full-time U.S. employees using CountryCode = US AND UserType = FullTimeEmployee. We can’t satisfy this requirement directly in SharePoint 2010 using the simple claims CountryCode or UserType, so we need to create a compound claim to represent using both claims.

We could create a new claim type for full-time U.S. employees. However, if the company creates new offices in other countries, we then need to update our CCP source code to add corresponding claim types. Clearly, this approach can be costly and time-consuming. Moreover, the ContentWeb business team wants to be able to add and remove compound claim types without needing a new application release each time. As you can see, ContentWeb requires a dynamic and extensible security model.

How will we manage all of the claims data? We’ll use a database called ContentWebAdminDB to store claim metadata. ContentWebAdminDB is the claims source back end; it consolidates all the claims source data from SAP and other enterprise databases. We’ll delve into this database more in a bit. And we’ll use an ordinary ASP.NET administrative application called ContentWebAdmin to connect to this database to create and update compound claims metadata and VPA configuration. (Details of this application are out of scope for this article.) Compound claims are created using simple claims. For example, in the ContentWebAdmin application we can create a compound claim called US-FullTimeEmployee using the CountryCode = US and UserType = FullTimeEmployee simple claims. With this coumpound claim metadata, for any user who has CountryCode = US and UserType = FullTimeEmployee simple claims, a new compound claim US-FullTimeEmployee = true will be added into the user’s claim set. Note that no compound claim US-FullTimeEmployee = false appears in any user’s claim set. With this dynamic compound claims design, each user has only a few dozen claims in total even though the permutation of compound claims can be pretty high. This is the key to ensuring good performance.

When a user signs in to ContentWeb, the CCP is invoked. The CCP calls a custom stored procedure, sp_GetUserClaims, in ContentWebAdminDB while passing in the user’s identity claim. The stored procedure checks VPA configuration and returns all the simple and compound claims for that user to the CCP. As you can see, dynamic compound claims are not the same as dynamic authorization rules. Our compound claims solution doesn’t change the way SharePoint 2010 evaluates claims. We simply inject compound claims into user claim sets based on the compound claims metadata configuration. Figure 1 shows a high-level overview of ContentWeb.

A High-Level View of ContentWeb
Figure 1 A High-Level View of ContentWeb

Inside the ContentWeb Custom Claims Provider

The ContentWeb CCP has a special class that inherits from Microsoft.SharePoint.Administration.SPClaimProvider. Figure 2 shows part of this class. Please note line 16 of the code (where the DisplayText property is set). We added it to help make our custom claims types and values display in a more user-friendly and readable manner.

Figure 2 Making Claims More Readable

protected override void FillResolve(
  Uri context, string[] entityTypes, SPClaim resolveInput,
  List<Microsoft.SharePoint.WebControls.PickerEntity> resolved)
{
  string claimTypeName = string.Empty;
  string claimValue = string.Empty;
 
  // Handles only ContentWeb claim types
  if (null != resolveInput && resolveInput.ClaimType.Contains(
    ContentWebClaimTypes.ContentWebClaimTypePrefix))
  {
    claimValue = resolveInput.Value.ToLower();
    claimTypeName = GetName(resolveInput.ClaimType);
 
    PickerEntity entity = CreatePickerEntity();
    entity.Claim = resolveInput;
    entity.Description = resolveInput.ClaimType + " = " + claimValue;
    entity.DisplayText = claimTypeName + " = " + claimValue;
    entity.EntityData[PeopleEditorEntityDataKeys.DisplayName] =
      claimTypeName + " = " + claimValue;
    entity.EntityType = SPClaimEntityTypes.User;
    entity.IsResolved = true;
    resolved.Add(entity);
  }
}

The ContentWeb CCP needs a connection string to call a stored procedure in ContentWebAdminDB for user claims. Because the CCP is a farm-level feature, it’s not within the scope of any Web application, so we can’t use the web.config file to configure the database connection string. We could use machine.config, but that’s not ideal because we have multiple Web front ends. Instead, we’ll use the SharePoint hierarchical object store, SPPersistedObject, to store the database connection string. This is stored in the SharePoint configuration database, making the database connection string available to all Web front ends in the SharePoint farm.

Inside the ContentWeb CCP, we create a class that inherits from Microsoft.SharePoint.Administration.SPPersistedObject, as shown in Figure 3.

Figure 3 The ConnectionStringStorage Class

[Guid("56705e15-abd3-44f0-adea-91488da1a572")]
public class ConnectionStringStorage
  : Microsoft.SharePoint.Administration.SPPersistedObject
{
    [Persisted]
    private string m_connectionString;
 
    public ConnectionStringStorage()
    {
    }
 
    public ConnectionStringStorage(
      string name, SPPersistedObject parent)
      : base(name, parent)
    {
    }
 
    public ConnectionStringStorage(
      string name, SPPersistedObject parent, Guid guid)
      : base(name, parent, guid)
    {
    }
 
    public string ConnectionString
    {
      get { return m_connectionString; }
      set { m_connectionString = value; }
    }
}

We’ll use the following Windows PowerShell script to register the database connection string:

[System.Reflection.Assembly]::LoadWithPartialName(
  "Microsoft.Sample.ContentWeb.ClaimsSecurity")
$id = new-object System.Guid("56705e15-abd3-44f0-adea-91488da1a572")
$farm = Get-SPFarm
$existingObject = $farm.GetObject($id)
$existingObject.Delete()
$newObject =
  new-object Microsoft.Sample.Contoso.ClaimsSecurity.ConnectionStringStorage(
  "ConnectionString", $farm, $id)
$newObject.ConnectionString = "Data Source=ContentWebAdminSQLServer;
  Initial Catalog=ContentWebAdminDB;Integrated Security=True;Timeout=30"
$newObject.Update();
Iisreset

As you can see, we reference the CCP dll. We create a new ConnectionStringStorage object and set its ConnectionString property. Finally, we call its Update method to save it into the SharePoint config database.

We recommend the connection string have a timeout value of less than 60 seconds. The CCP has an execution default timeout of 60 seconds. This means that if the database connection times out, you have the ability to capture the real exception from the connection string instead of a misleading one from the CCP execution.

Inside the CCP’s constructor, we’ll retrieve the connection string and store it in a module-level variable:

public class ContentWebClaimProvider : SPClaimProvider
{
  private static string connectionString = null;
 
  public ContentWebClaimProvider(string displayName)
    : base(displayName)
  {
    Guid guid = new Guid(@"56705e15-abd3-44f0-adea-91488da1a572");
    ConnectionStringStorage storage =
      (ConnectionStringStorage)SPFarm.Local.GetObject(guid);
    connectionString = storage.ConnectionString;
  }
}

We want to be able to trace when the CCP connects to the database for user claims. SharePoint 2010 has a new feature that’s perfect for this. It’s called the Microsoft.SharePoint.Utilities.SPMonitoredScope object, and it monitors performance and resource use for a specified block of code. The following code logs the CCP’s database calls:

using (new SPMonitoredScope("ContentWeb.CCP.GetUserClaims", 5))
{
  userClaimsDataSet = CustomClaimSourceDA.GetUserClaims(
    userAlias, connectionString);
}

We can also configure the event level and trace level of this event in SharePoint Central Administration. These are controlled by the Monitoring object in the SharePoint Foundation category under Monitoring | Reporting | Configure diagnostic logging.

Inside the ContentWebAdminDB Database

As noted earlier, ContentWebAdminDB consolidates the user’s claim data from SAP and other enterprise databases, and it contains the custom claim source. The ContentWeb CCP calls a stored procedure, sp_GetUserClaims, to retrieve a user’s custom claims.

The ContentWebAdmin application allows authorized users to configure compound claims and VPA for a user. ContentWebAdminDB stores the compound claims configuration metadata and the VPA configuration in tables. When the ContentWeb CCP calls sp_GetUserClaims, this stored procedure will check VPA configuration and then return both simple claims and compound claims. The detailed design and implementation of this database is out of the scope of this article. However, we included a SQL script for a dummy sp_GetUserClaims stored procedure with hardcoded claim values within the downloadable code package.

Using SharePoint Groups

An SPG is a logical wrapper of SharePoint users. This is handy for ContentWeb, which uses hundreds of compound claims. Without SPGs, it would be very difficult for content authors to manage all of the claims.

Using SPGs provides the following benefits:

  • Friendly Naming: SPGs let users create friendly and meaningful group names for potentially cryptic claim values. For example, ContentWeb has an SPG named “US FullTime Employees” that describes users who have the claim values Country=United States, CompanyCode=1010 and UserSubType=FTE.
  • People Picker Name Resolution Support: The People Picker control in SharePoint 2010 natively supports name resolution for SPGs. This makes it easier to find and select SPGs, as shown in Figure 4.

SharePoint Groups in People Picker
Figure 4 SharePoint Groups in People Picker

  • Dynamic Claim Security Management: Suppose you needed to change the definition of “US FullTime Employees,” adding UserType=Corp to the other attributes—Country=United States, CompanyCode=1010 and UserSubType=FTE—due to changing business needs. It’s easy to make the change in just that one SPG, and all objects secured in that SPG would automatically inherit this change.
  • **Just One SPG for Securing and Targeting Content:**SharePoint 2010 now supports the ability to secure and target an object using the same SPG. This eliminates the typical overhead associated with creating audiences, importing user profiles, running the profile sync job and so forth for targeting.
  • Multiple Claims Associated with One SPG: Let’s say you need to secure a page for full-time employees whose level ranges from 60 to 65. You can easily do this by creating an SPG that has compund claims for UserSubType=FTE and Level=60, UserSubType=FTE and Level=61… UserSubType=FTE and Level=65. SharePoint 2010 interprets all of these claims as “OR.”

Managing many SPGs has the potential to be time-consuming, cumbersome and error-prone. The ContentWeb business team uses an Excel file to manage the hundreds of groups with associated custom claims.

There are various options for automating the creation of SPGs. We created a Windows PowerShell script that reads data from an XML file to automate this. (You’ll find this script in the code download, which can be downloaded from code.msdn.microsoft.com/mag201111SPSecurity.) We populated the XML data file using the business team’s Excel file. Figure 5 shows the sample XML configuration file.

Figure 5 Sample XML Configuration File

<?xml version="1.0" encoding="utf-8"?>
<SharePointGroups url="https://contentweb" owner="contoso\ivfeng" >
  <SharePointGroup name="ContentWeb-FTE"
    description="ContentWeb-FTE" permissionLevel="Read">
    <Claim type="UserType" value="emp"/>
  </SharePointGroup>
  <SharePointGroup name="ContentWeb_US_0000_Emp_Corp_HRPro"
    description="ContentWeb_US_0000_Emp_Corp_HRPro">
    <Claim type="CompoundClaim" value="US+emp+corp+hrpro"/>
  </SharePointGroup>
  <SharePointGroup name="ContentWeb_US_0000_Emp_Corp_Manager"
     description="ContentWeb_US_0000_Emp_Corp_Manager">
    <Claim type="CompoundClaim" value="us+emp+corp+manager"/>
    <Claim type="CompoundClaim" value="US+emp+corp+hrpro"/>
  </SharePointGroup>
</SharePointGroups>

Using SPGs to Target

To use targeting, you need the User Profile service application enabled for the Web app.

There’s one tricky issue to watch out for when using SPGs for targeting. We initially found that the SPGs containing Active Directory users or security groups were all working fine, but the SPGs containing custom claim users didn’t work at all. The root cause was related to the permission level of those groups. When we created the SPGs with custom claims, we didn’t want to assign any default permission level. However, if an SPG is not assigned a permission level when created and later used for security, it will be flagged with a special permission level called Limited Access, as shown in Figure 6.

A SharePoint Group with Limited Access
Figure 6 A SharePoint Group with Limited Access

Because ContentWeb has hundreds of SPGs and most of them are used only for the target audience, they don’t have any permission level set. However, the target audience processor can’t pull in these SPGs without any permission level. We were lucky and discovered a workaround via the permission UI of the page we were applying to the target audience. We granted “Read” permission to the SPGs being used for the target audience, then immediately undid this permission. This process causes SharePoint to flag these SPGs with the Limited Access permission level, and the target audience happily honors that permission level. It’s important to note that you don’t have to go through this grant and undo process for the SPGs multiple times. You need to do it just once within the security inheritance tree. Be aware that if you break security inheritance, you’ll need to do it again, even if you’ve done it at the parent level.

Implementing Search for ContentWeb

One of our major goals with this application was to improve the search experience for Contoso employees. We wanted ContentWeb to make content far more discoverable than it had been in the past.

To do this for ContentWeb, we used FAST Search Server 2010 for SharePoint hosted in a federated environment. Refer back to Figure 1 for Contoso’s federated FAST Search environment. This shows a SharePoint farm called the FAST Search Federation Farm. Behind this farm is the Microsoft FAST Search Server for SharePoint engine and database. The FAST Search farm contains all the Query Search Service Applications (Query SSAs) and a Content SSA.

Naturally, we created a content source within the existing Content SSA. (FAST Search Server recommends only one Content SSA.) Once this is done, the Content SSA will crawl the ContentWeb content database and make the search index available to ContentWeb and other intranet applications.

We then created a separate Query SSA and published it to ContentWeb. With the initial search test on ContentWeb, we found that basic search worked, but search security trimming wasn’t working as expected. For any pages secured with ContentWeb custom claims, users could not see the links within the search results even though they had the security permissions. We finally figured out that we needed to have the ContentWeb custom claim type mappings registered in the FAST Search SharePoint farm for search security trimming to work. The simplest way to do this is to deploy the CCP into the FAST Search SharePoint farm. Not only did we have to confirm the claim type mappings were registered, we also had to make sure all claim IDs (shown as Account in Figure 7) were the same across all the SharePoint farms for the same claim type value pair.

Claim IDs
Figure 7 Claim IDs

Currently, even with the SharePoint 2010 June 2011 cumulative updates, claim type mappings in SharePoint 2010 are immutable. Once a CCP is deployed on a SharePoint 2010 farm, the claim type mappings are registered and remain unchanged even if you remove and redeploy the CCP. Because you may have CCPs on multiple SharePoint farms, it’s very important that you deploy all the CCPs in the same sequence to ensure the claim IDs are the same across all farms. If you don’t do this, the sequence IDs won’t be aligned and search won’t return accurate results.

When ContentWeb content is crawled, the CCP is not invoked. All that’s needed is the claim type mappings registered in the FAST Search farm so that the crawler engine can decode the ContentWeb custom claims. It then stores the decoded custom claims for security in the search index. Removing the ContentWeb CCP is optional because the claim type mappings will remain. Moreover, we don’t really need the ContentWeb CCP to augment user claims on the FAST Search farm.

Security Trimming Search Results: Contoso Intranet Portals

In order for the ContentWeb content to be searchable from other Contoso intranet portals, you need to ensure the following:

  • All intranet portals that need to surface the ContentWeb content must be built on SharePoint 2010 and be claims-based Web applications that use Windows authentication.
  • All intranet portals must query the same FAST Search index.
  • All intranet portals must have the ContentWeb CCP installed on them. (If there’s more than one CCP, they all must be installed in the same sequence to ensure claim IDs are aligned.)

If all of these conditions are met, the ContentWeb content will be searchable across all of the Contoso intranet portals.

Wrapping Up

SharePoint 2010 provides strong integration with claims-based security. SharePoint groups, claims and a custom claims provider allow you to authorize content for the enterprise broadly as well as at a granular level. As you can see, you can satisfy many scenarios with relative ease.

This design also lets developers build robust tools that allow information workers (such as support personnel) to alter their claims and view a site as a different user, and then revert.

As with most new design implementations, our experience didn’t come without “opportunities to learn,” but the end product was well worth it. The sample code we’ve provided should help get you going in the right direction and off to a great start on deploying your next-gen enterprise portal!


Jinhui (Ivory) Feng is a senior software development engineer at Microsoft working on its internal human resources applications.

Shabbir Darugar is a senior software development engineer and project lead at Microsoft working on its internal human resources applications.

Patrick Stanko is a lead program manager at Microsoft working on its internal human resources applications.

Thanks to the following technical expert for reviewing this article: Tom Wisnowski