SharePoint Dragons

Nikander & Margriet on SharePoint

Tag Archives: claims

Info about custom claim provider

This one http://msdn.microsoft.com/en-us/library/gg615945.aspx is a nice starting point for creating a custom claim provider. It actually shows a nicer way to retrieve the user name as the one presented in our own article https://sharepointdragons.com/2012/04/26/claims-in-sharepoint-2010-the-sequel/, like so:

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

And uses audiences as a basis for assigning claims.

Claims in SharePoint 2010: the sequel

Or: Creating a claims provider, creating hierarchical claims, and assign permissions based on claims

This is a follow up to a previous article ( https://sharepointdragons.com/2012/01/30/claims-in-sharepoint-2010/ ) we’ve written. That article discusses how to set up a SharePoint site collection that supports claims authentication. To follow the sequel, you need to have set up such a site collection. From then on, we’ll take it one step further and discuss how to create a custom provider that issues custom claims and how to assign permissions to SharePoint objects based on such claims.

Why are we bothering you about claims, again?

After finishing our previous blog post about the topic of claims we leaned back and relaxed, enjoying our SharePoint site collection supporting claims authentication, and marveled at the sheer brilliance of our web part that displays all current claims. Then, Margriet got involved in a forum discussion at http://social.technet.microsoft.com/Forums/en-US/sharepoint2010programming/thread/415297a2-941b-411a-bfc5-e1045e0ae80e and we started realizing that the first article wasn’t enough and we had to go at least one step further.

In the aforementioned forum thread, a member of the Danish Defence IT Agency sought to implement a hierarchical claim. One that would be able to represent the following hierarchy:

  • Defence Department
    • Defence Command
      • IT Agency
        • Infrastructure
          • Server Applications
            • SharePoint

As we found out later, it’s a quite common request in military organizations to have a permission structure that is very hierarchical in nature, to have a wide variety of security clearances with a hierarchy associated to them.

Please note: For Jesper Schioett, don’t shoot us if we somehow wrongly represent your business case in this article, because seeing your profile picture at http://social.technet.microsoft.com/profile/schioett/?ws=usercard-hover we noticed that you actually can do exactly that!

One way to do that would be to create multiple claims that each represent an aspect of this hierarchy, like so:

http://claims/defence/2012/OrgLevel01 = Defence Deparment
http://claims/defence/2012/OrgLevel02 = Defence Command
http://claims/defence/2012/OrgLevel03 = IT Agency
http://claims/defence/2012/OrgLevel04 = Infrastructure
http://claims/defence/2012/OrgLevel05 = Server Applications
http://claims/defence/2012/OrgLevel06 = SharePoint

Another way would be to create a single claim and to put XML in it, like so: <l>Defence department<l>Defence Command</l></l>, which would work in scenarios where you’re programmatically iterating thru each claim (as is done in the claims web part in https://sharepointdragons.com/2012/01/30/claims-in-sharepoint-2010/), but unfortunately breaks down when you want to assign permissions to SharePoint objects based on such a claim.

Yet another way would be to use the hierarchical nature of URLs to come up with a scheme to capture the hierarchy. This is discussed in http://sharepoint.mindsharpblogs.com/NancyB/archive/2011/03/28/Custom-Claims-Providers[coln]-Hierarchical-Claims-[dash]-Part-2,-Setup-and-Claims-Augmentation.aspx However, we feel this is only a slight variation of the first approach where multiple claims are created to capture each aspect of the hierarchy. It does do a nice job of representing your actual hierarchy, so if that’s what your looking for it may still be the ideal approach for you.

At one point in the discussion, Jesper mentioned the following article: http://sharepointmetadataandclassification.typepad.com/blog/2012/03/building-a-custom-claim-provider-to-manage-security-clearances.html . It assigns multiple values to the same claim, and does an excellent job at implementing a hierarchical claim. It in fact uses another military example. While following the article, we’ve found it to be of good quality but, in our minds, it has two or three flaws (since, to our knowledge, the owner doesn’t own a machine gun we’re not afraid to point that out):

  • The code examples are screenshots. That sure was annoying.
  • Claims are added equally for everybody. We believe that even the most basic of claim provider implementations should provide some code for determining the current user.
  • The article stops too soon and doesn’t discuss what to do with these claims once set up.

Because of these shortcomings in an overall good article, we felt the need to redo parts of the article, keeping the original example intact (being fine as it is). This provides the added benefit that now the code can be copied and pasted (although we’ve put in minor changes), instead of retyped, and shows how to assign permissions to SharePoint objects based on the newly created claims.

Implementing a custom claim provider

Every SharePoint web application has a registered set of claim providers which are triggered every time authentication is performed. It doesn’t matter whether you log in using Windows authentication (NTLM or Kerberos), forms authentication (using an ASP.NET membership and role provider), or via a Security Token Service (STS, Active Directory Federation Services 2.0, or ADFS 2.0, is a good example of an STS) that issues SAML claims. In this example, we’ll create our own and add it to the existing set of claim providers. We’ve tested with both NTLM and forms authentication.

We’re not reiterating the steps listed in http://sharepointmetadataandclassification.typepad.com/blog/2012/03/building-a-custom-claim-provider-to-manage-security-clearances.html , they work and we’ll just provide our code for the implementation.

Please note: The FillClaimsForEntity method is, by far, the most interesting method to review.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Claims;
using Microsoft.SharePoint.WebControls;

namespace LoisAndClark.Claims
{
    public class MyProvider : SPClaimProvider
    {
        public MyProvider(string displayName)
            : base(displayName)
        {

        }

        internal static string ProviderDisplayName
        {
            get { return “LoisAndClark My Claims Provider display name”; }
        }

        internal static string ProviderInternalName
        {
            get { return “LoisAndClark My Claims Provider internal name”; }
        }

        private static string ClearanceClaimType
        {
            get { return “http://schemas.loisandclark.eu/myclearance”; }
        }

        private static string ClearanceClaimValueType
        {
            get { return Microsoft.IdentityModel.Claims.ClaimValueTypes.String; }
        }

        protected override void FillClaimTypes(List<string> claimTypes)
        {
            if (claimTypes == null) throw new ArgumentNullException(“empty claimtypes”);

            claimTypes.Add(ClearanceClaimType);
        }

        protected override void FillClaimValueTypes(List<string> claimValueTypes)
        {
            if (claimValueTypes == null) throw new ArgumentNullException(“empty claim value type”);

            // claim value type = Microsoft.IdentityModel.Claims.ClaimValueTypes.String
            claimValueTypes.Add(ClearanceClaimValueType);
        }

        private string[] SecurityLevels = new string[] { “None”, “Confidential”, “Secret”, “Top Secret” };

        private bool DoesClaimValueAlreadyExist(List<SPClaim> claims, string claimType, string claimValue)
        {
            if (claims == null) throw new ArgumentNullException(“empty claims list”);
            if (String.IsNullOrEmpty(claimType)) throw new ArgumentNullException(“no claim type”);
            if (String.IsNullOrEmpty(claimValue)) throw new ArgumentNullException(“no claim value”);

            foreach (SPClaim claim in claims)
            {
                if (claim.ClaimType == claimType && claim.Value == claimValue) return true;
            }

            return false;
        }

        protected override void FillClaimsForEntity(Uri context, SPClaim entity, List<SPClaim> claims)
        {
            if (entity == null) throw new ArgumentNullException(“empty entity”);
            if (claims == null) throw new ArgumentNullException(“empty claims”);
            if (String.IsNullOrEmpty(entity.Value)) throw new ArgumentException(“no entityvalue”);

            // claim type = http://schemas.loisandclark.eu/myclearance
            bool top = DoesClaimValueAlreadyExist(claims, ClearanceClaimType, “Top Secret”);
            bool secret = DoesClaimValueAlreadyExist(claims, ClearanceClaimType, “Secret”);
            bool confidential = DoesClaimValueAlreadyExist(claims, ClearanceClaimType, “Confidential”);
            bool none = DoesClaimValueAlreadyExist(claims, ClearanceClaimType, “None”);

            // Entity value in Windows authentication scenario: 0#.w|loisandclark\administrator
            // Entity value in Forms authentication scenario: 0#.f|fbamembershipprovider|anton
            string currentUser = entity.Value.Substring(entity.Value.IndexOf(“|”) + 1);

            // Now, add claims based on the current user. This logic is omitted.

            // None
            claims.Add(CreateClaim(ClearanceClaimType, SecurityLevels[0], ClearanceClaimValueType));
           
            // Confidential
            claims.Add(CreateClaim(ClearanceClaimType, SecurityLevels[1], ClearanceClaimValueType));
           
            // Secret
            claims.Add(CreateClaim(ClearanceClaimType, SecurityLevels[2], ClearanceClaimValueType));
           
            // Top Secret
            claims.Add(CreateClaim(ClearanceClaimType, SecurityLevels[3], ClearanceClaimValueType));

            //if (confidential || secret || top) claims.Add(CreateClaim(ClearanceClaimType, SecurityLevels[1], ClearanceClaimValueType));
            //if (secret || top) claims.Add(CreateClaim(ClearanceClaimType, SecurityLevels[2], ClearanceClaimValueType));
            //if (top) claims.Add(CreateClaim(ClearanceClaimType, SecurityLevels[3], ClearanceClaimValueType));

        }

        protected override void FillEntityTypes(List<string> entityTypes)
        {
            entityTypes.Add(SPClaimEntityTypes.FormsRole);
        }

        protected override void FillHierarchy(Uri context, string[] entityTypes, string hierarchyNodeID, int numberOfLevels, SPProviderHierarchyTree hierarchy)
        {
            if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole)) return;
        }

        //protected override void FillHierarchy(Uri context, String[] entityTypes, String hierarchyNodeID, int numberOfLevels, SPProviderHierarchyTree hierarchy)
        //{
        //  if (String.IsNullOrEmpty(hierarchyNodeID))
        //  {
        //    hierarchy.AddChild(CreateHierarchyNodeForNodeID(MyLoginNameClaimType));
        //    hierarchy.AddChild(CreateHierarchyNodeForNodeID(MyClaimType));
        //  }
        //  else if (String.Equals(hierarchyNodeID, MyLoginNameClaimType, StringComparison.Ordinal))
        //  {
        //    hierarchy.Name = GetHierarchyNodeNameForNodeID(hierarchyNodeID);
        //  }
        //  else if (String.Equals(hierarchyNodeID, MyClaimType, StringComparison.Ordinal))
        //  {
        //    hierarchy.Name = GetHierarchyNodeNameForNodeID(hierarchyNodeID);
        //  }
        //}

        protected override void FillResolve(Uri context, string[] entityTypes, SPClaim resolveInput, List<PickerEntity> resolved)
        {
            if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole)) return;
        }

        protected override void FillResolve(Uri context, string[] entityTypes, string resolveInput, List<PickerEntity> resolved)
        {
            if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole)) return;
        }

        protected override void FillSchema(SPProviderSchema schema)
        {
            schema.AddSchemaElement(new SPSchemaElement(PeopleEditorEntityDataKeys.DisplayName, “Display Name”, SPSchemaElementType.Both));
        }

        protected override void FillSearch(Uri context, string[] entityTypes, string searchPattern, string hierarchyNodeID, int maxCount, SPProviderHierarchyTree searchTree)
        {
            if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole)) return;
        }

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

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

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

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

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

 

A custom claim provider needs to be registered via an event receiver based on SPClaimProviderFeatureReceiver. Step 9 of the article prescribes that you need to set the Receiver assembly and Receiver class properly. You can do that by setting:

  • The receiver assembly to $SharePoint.Project.AssemblyFullName$.
  • The receiver class to LoisAndClark.Claims.MyProviderEventReceiver.

We’ve implemented the event receiver responsible for registering our custom claim provider like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Claims;

namespace LoisAndClark.Claims
{
    public class MyProviderEventReceiver : SPClaimProviderFeatureReceiver
    {
        private void ExecBaseFeatureActivated(SPFeatureReceiverProperties props)
        {
            base.FeatureActivated(props);
        }

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

        public override string ClaimProviderDescription
        {
            get { return “my provider description”; }
        }

        public override string ClaimProviderDisplayName
        {
            get { return MyProvider.ProviderDisplayName; }
        }

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

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

Once you deploy it and log in, you should see the new claims issued by our custom claim provider, as shown in the next Figure.

image

Two tips. One. Every time you’re changing your claim provider, deploy it and then perform an iisreset before changes are visible. Two. You can debug your custom claim provider by attaching to the w3wp process.

Assigning permissions to SharePoint objects

With all these brand new claims, the urge rises to do something useful with them. You can do this by assigning a claim to a SharePoint web site. We’ve done this twice, both for the claim values “None” and “Top Secret”. This is the code (again, we didn’t have to be very creative, we just borrowed and augmented (to use some claims terminology) the code from http://blog.mastykarz.nl/programmatically-granting-permissions-claims/):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration.Claims;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            //string permission = “None”;
            string permission = “Top Secret”;

            using (SPSite site = new SPSite(“http://astro:46454″))
            {
                using (SPWeb web = site.OpenWeb())
                { SPClaimProviderManager claimMgr = SPClaimProviderManager.Local;
                    if (claimMgr != null)
                    {
                        SPClaim claim = new SPClaim(
                            “http://schemas.loisandclark.eu/myclearance”,
                            permission,
                            Microsoft.IdentityModel.Claims.ClaimValueTypes.String,
                            SPOriginalIssuers.Format(SPOriginalIssuerType.ClaimProvider,
                            “LoisAndClark My Claims Provider internal name”));
                        string userName = claimMgr.EncodeClaim(claim);

                        SPUserInfo info = new SPUserInfo { LoginName = userName, Name = permission };
                        SPRoleAssignment roleAssignment = new SPRoleAssignment(info.LoginName, info.Email, info.Name, info.Notes);
                        roleAssignment.RoleDefinitionBindings.Add(web.RoleDefinitions[“Read”]);
                        web.RoleAssignments.Add(roleAssignment);
                        web.Update();
                    }
                }
            }

        }
    }
}

You’ll see them again in the Groups list:

image

Or, in some ways, the user.aspx application page even shows it clearer:

image

After that, we created a custom list called MySecrets containing the items None and Top Secret:

image

If you want to see the None list item , you need to either be a site collection administrator or have the None claim. This can be seen in the next Figure:

image

To see the Top Secret list item, you’ll need to have the Top Secret claim (or be an administrator):

image

You can experiment with the custom claim provider and verify that this indeed works. Please note that in our example we’ve provided all claims for everyone. If you modify the example and only provide a None claim, you’ll see the difference. In our case, it’s poor Anton the forms user that has to suffer:

image

Conclusion

Claims are cool! But didn’t we all know this already?

Claims in SharePoint 2010

When we set out to write a blog post we had the intention of documenting the process of setting up a claims aware SharePoint web application. After consulting numerable resources, we’ve found out that it’s not needed at all.

First off, if you want to read more about claims authentication, go to http://technet.microsoft.com/en-us/library/hh487289.aspx and read the claims-based authentication white paper.

If you want to set up claims authentication in SharePoint Server 2010 we found that the following post is the best resource: http://donalconlon.wordpress.com/2010/02/23/configuring-forms-base-authentication-for-sharepoint-2010-using-iis7/ This post describes a visual way to set it up, and since it’s so easy to make tiny mistakes, it’s better to execute the process aided by visual tools.

We have three comments about the post that might help you though:

  1. You can also use the MembershipSeeder tool to set up test accounts, we feel it’s the best way to set up a larger number of test accounts. The MembershipSeeder tool can be found on CodePlex: http://cks.codeplex.com/releases/view/7450.
  2. Be sure to create a new SQL Server user account as specified in the blog post.
  3. Adjust the People Picker when you’re done following the steps in the blog post.

1. Setting up users via the MembershipSeeder tool

The MembershipSeeder tool is a client dedicated to communicating with the Membership database. It’s the best way for adding larger amounts of test users to the Membership database.

Please note: Don’t get too excited about the comment that the MembershipSeeder tool has been superseded by CKS: FBA. If you follow that path, it will actually be more work for you. We like the fact that the MembershipSeeder tool directly accesses the membership database and works in a simple, no bells and whistles kinda way.

Before you’re able to use the MembershipSeeder tool, you need to download it (duh!) and change some configuration data:

  1. Download the MembershipSeeder tool, extract it, and go to the Debug folder.
  2. Locate the <connectionStrings> element.
    Notice that the connection string is pointing to the wrong direction: server=o12; database=aspnetdb; Trusted_Connection=True. Replace it, in our case it looks like this:

    <connectionStrings>
      <add name=”AspNetSqlMembershipProvider” connectionString=”Data Source=moon;Initial Catalog=MyAccounts;User ID=administrator;Password=*****” />
    </connectionStrings>

Please note that we’re using SQL Server authentication, as specified in the blog post, so while your data source and user credentials may differ, don’t change that aspect. It’s a great way to test if the SQL Server user account has sufficient permissions. Now, let’s add some roles and users:

  1. Double-click MembershipSeeder.exe. This opens the ASP.NET Membership Form window.
  2. We feel the UI is a bit messy (we’re not criticizing, we feel this is a very handy tool), so be careful. Check out the next Figure to get the idea.

    image

  3. First, check the two checkboxes about adding, creating or deleting only 1 user.
  4. The upper right part allows you to create new roles. Create two new roles: Developers and Managers.
  5. Then, click the Get Roles button to verify this has succeeded.
  6. The upper left part allows you to add new users. Create three new users: anton (with the password: anton), bill (pwd: bill), and daisy (pwd: daisy).
  7. After you’ve done that, add the users to roles in the upper right part by typing a role name in the Role textbox and typing the user name in the User Prefix textbox.
  8. Then, click the Add to Role button. Add anton and daisy to the Managers role. Click the Get Users button to verify this has succeeded.
  9. Now, add bill and daisy to the Developers role.

At this point we’ve created a Developers role with the members Bill and Daisy, and a Managers role with the members Anton and Daisy. This will give us some users to test with later.

2. Create a new SQL Server user account

At first we used a SQL Server user account that we’ve created during installation. Apparently this is a special kind of user account that can’t be used when connecting to the membership database. When you’re doing direct manual configuration of the web.config files, this mistake is hard to detect. We liked the approach of creating a fresh SQL Server user account, even for development purposes.

3. Configure People Picker

If you want to be able to type parts of user names and want the People Picker to autocomplete those names add the following line to the web.config file of every web application where you want to use People Picker autocompletion:

<add key=”fbamembershipprovider” value=”%” />

So that it looks like:

<PeoplePickerWildcards>
  <clear />
  <add key=”AspNetSqlMembershipProvider” value=”%” />
  <add key=”fbamembershipprovider” value=”%” />
</PeoplePickerWildcards>

What now?

If everything goes well, you should be able to log in both using Windows accounts and form accounts. To check out what claims are associated to each user, first download and install the Windows Identity Foundation SDK at http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=4451. We’ll be using it within a web part, so you’ll need install the SDK 3.5 version because SPS 2010 doesn’t support .NET 4.

Now, create an empty SharePoint project in Visual Studio.NET 2010, add a visual web part and add a label on it. Add the following code to it, deploy the package to SPS 2010 and add the web part to a page:

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.IdentityModel.Claims;

namespace LCWebParts.ClaimsVisualPart
{
    public partial class ClaimsVisualPartUserControl : UserControl
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            IClaimsIdentity cidentity = (IClaimsIdentity) Page.User.Identity;
            Label1.Text = String.Format(“User name = {0}<br/>”, cidentity.Name);
           
            foreach (Claim claim in cidentity.Claims)
            {
                Label1.Text += String.Format(“type: {0} value = {1}<br/>”, claim.ClaimType, claim.Value);
            }           
        }
    }
}

It’s interesting to see the different set of claims associated to different user accounts, and this allows you to make programmatic authorization decisions based on claims.

Claims documentation

So far the best resource we’ve found online about claims authentication and SharePoint 2010: http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=27569