SharePoint Federation Utility ( SPFedUtil.exe )

I have been thinking about this for a little while, but finally found a reason to make it myself. If you have worked with Windows Identity Foundation SDK in the past you might have spotted a very handy utility called FedUtil.exe. If you installed the SDK in the default location its located at “C:\Program Files (x86)\Windows Identity Foundation SDK\v3.5″ folder. Or if you are a developer you might have noticed the “Update STS reference” button while working on a standard ASP.NET web application. This tool is great when you are working with ADFS 2.0 or any other standards compliant identity provider. This utility configures the ASP.NET web application and produces relying party metadata which could be later sent to the identity provider so that too could be configured to issue claims to this relying party.

SharePoint is a ASP.NET web application however because of SharePoint specific deployment scenarios this tool is not very useful in a SharePoint context. So i have attempted to create a utility that could be used for similar purposes. SPFedUtil.exe features as it stands today [4:40pm 12/05/2010]

  • Displays currently configured SharePoint trusted login providers
  • Enables configuration of trusted login provider name and realm
  • Federation metadata consumption from a server or file system
  • Identity provider certificate verification and CA configuration
  • Augmenting list of claims provided by identity provider with a CSV file
  • Specifying SharePoint user identity claim type
  • Specifying SharePoint claim provider for this trusted login provider
  • SharePoint replying party metadata configuration, including contact and organization info
  • Configures SharePoint trusted login provider using PowerShell (can be run from the util)
  • Emailing of generated relying party metadata to identity provider technical contact

This utility can be found here [download id="2" format="1"] Please feel free to mention this blog as the source of this utility.

I wont clutter up this post with all the images and how to instructions for this utility. I have created a separate project site here. I will endevour to put full how-to documentation on that page. But for now here are a couple of screenshots



 

[4:44 21/05/2010] Update : bug fixed – Identity provider sign in URL not configured properly during SharePoint trusted provider setup. This now retrieves the sign in URL from the IdP metadata.

This is a beta tool and thus should be used with caution :D

Custom social bookmarking ribbon tab in SharePoint 2010

I have been investigating SharePoint 2010 ribbon lately. While doing so i came across some useful documentation posted on both Microsoft and personal blogs. I have listed some of the more useful resources at one of my earlier post. Based on these resources I have created a social bookmarking ribbon tab similar to the ones found in some blogs and social sites. This post is to share the implementation and some basic findings. Some of these findings are based on experiences in creating the social bookmarking ribbon and might not be the best practice for the final version of SharePoint 2010.

End result … download at the end of the post

See it in action here …
httpv://www.youtube.com/watch?v=trQgdexZC6I

Following list summaries the minimal steps that are required to create a ribbon tab or contextual group

  • Location attribute of CustomAction must be as follows
    <CustomAction Id="Cecil.Ribbon.Socialize" Title="Socialize" Location="CommandUI.Ribbon">
  • Location attribute of CommandUIDefinition must be either of the following
    <CommandUIDefinition Location="Ribbon.Tabs._children">
    or
    <CommandUIDefinition Location="Ribbon.ContextualTabs._children">

    Former is required for Tabs and later is for ContextualGroup

  • Fill in CommandUIDefinition while adhering to the schema which can be found at {14HIVE}\TEMPLATE\GLOBAL\XML\CMDUI.XML
  • Create a custom CUI.Page.PageComponent and place it in the _layouts folder
  • Place all the command names in either getGlobalCommands or getFocusedCommands
  • If your ribbon is in a state when the tab or contextual group should be visible then make sure canHandleCommand returns true
  • If your button or other control is in a state when they can be pressed make sure canHandleCommand returns true
  • Run code specific to each command in handleCommand method based on the commandId
  • Include a reference to Microsoft.Web.CommandUI.dll in your project
  • Include the CUI.Page.PageComponent source file then register it by making it available in a web control.
     public class SocializeBootstrapper : WebControl
        {
            protected override void OnPreRender(EventArgs e)
            {
                ScriptLink.RegisterScriptAfterUI(this.Page, "SP.Core.js", false, true);
                ScriptLink.RegisterScriptAfterUI(this.Page, "CUI.js", false, true);
                ScriptLink.RegisterScriptAfterUI(this.Page, "core.js", true, false);
                ScriptLink.RegisterScriptAfterUI(this.Page, "SP.Ribbon.js", false, true);
                ScriptLink.RegisterScriptAfterUI(this.Page, "SP.Runtime.js", false, true);
                ScriptLink.RegisterScriptAfterUI(this.Page, "SP.js", false, true);
                ScriptLink.RegisterScriptAfterUI(this.Page,
                            "SPCecil.Socialize/Socialize.UI.js", false, true);
    
                SPRibbon currentRibbon = SPRibbon.GetCurrent(this.Page);
                currentRibbon.MakeTabAvailable("Cecil.Ribbon.Socialize.Tab");
    
                base.OnPreRender(e);
            }
        }
  • The load the control using DelegateControl
    <Control
    		Id="AdditionalPageHead"
    		Sequence="200"
    		ControlClass="$SharePoint.Project.FileNameWithoutExtension$.SocializeBootstrapper"
    		ControlAssembly="$SharePoint.Project.AssemblyFullName$">
    </Control>

[download id="1" format="1"]
If you want to change the button images around its pretty easy to do so, check out this social image set and replace them in the _layouts folder.
if you find this solution helpful please have a link back to this blog.

Resources for creating your own Ribbon in SharePoint 2010

After the Microsoft presentation yesterday i am becoming more and more convinced about the usability and effectiveness of office ribbon in SharePoint 2010. Being the geek i am i would not fully convinced till i had a go at making my own. I am still trying to figure it out so hopefully in the near future I will have something to share with other developers. Its very hard to find good documnetation on Ribbon at this stage so i thought i post the link that i found most useful thus far.

Microsoft’s SharePoint hands on labs, not that detailed yet but should help get started

Microsoft SharePoint Developer Documentation Blog, just found it very good [7:39 16/4/2010]

Chris O’Brien’s Blog, found this to be most helpful

Wictor Wilén Blog, there is a custom action that he created for quick list creation might use that myself :D

Hope this helps others

Displaying personal session Claims in SP2010 with BCS

I have seen some posts on the web showing how to display session claims for the logged in user by iterating through the claims HttpContext.Current.User in a webpart. However it seemed a bit dull for SharePoint 2010 which has so much fire in its belly. I quite like the concept of Business Connectivity Services (BCS) and while investigating it for possible use i thought this might be the simplest implementation that anyone could ever try out. However there are some benefits to using BCS. For example you can create custom view from the UI without writing additional code, for example show all course role claims in alphabetical order. You can also connect it up with other lists using SharePoint WebPart connections to drive another webpart, for example based on the course role claims selected show course details web part. Again the examples are Academic institution related but i am sure you guys can think of examples that relate to your organization. So this is the code. SPCecil.Connect.MyClaimsDataModel is the namespace for my classes.

    public partial class MyClaim
    {
        public string Identifier { get; set; }
        public string ClaimType { get; set; }
        public string Value { get; set; }
        public string Issuer { get; set; }
        public string OriginalIssuer { get; set; }
        public string ValueType { get; set; }
    }

    public class MyClaimService
    {
        public static MyClaim ReadItem(string id)
        {
            IClaimsIdentity identity = GetClaimsIdentity();
            string[] parts = id.Split(new string[] { "::" },
                        2, StringSplitOptions.RemoveEmptyEntries);
            if (parts != null &amp;&amp; parts.Length == 2)
            {
                var claimsOfType = (from c in identity.Claims
                                    where c.ClaimType == parts[0]
                                    select c).ToArray();
                int index = Convert.ToInt32(parts[1]);
                if (claimsOfType != null &amp;&amp; claimsOfType.Length &gt; index)
                {
                    Claim selectedClaim = claimsOfType[index];
                    return new MyClaim()
                    {
                        Identifier = id,
                        Value = selectedClaim.Value,
                        ValueType = selectedClaim.ValueType,
                        ClaimType = selectedClaim.ClaimType,
                        Issuer = selectedClaim.Issuer,
                        OriginalIssuer = selectedClaim.OriginalIssuer
                    };
                }
            }
            return null;
        }

        public static IEnumerable ReadList()
        {
            IClaimsIdentity identity = GetClaimsIdentity();
            var claimTypes = (from c in identity.Claims select c.ClaimType).Distinct();
            List myClaims = new List();
            foreach (string claimType in claimTypes)
            {
                var claims = from c in identity.Claims
                             where c.ClaimType == claimType
                             select c;
                int index = 0;
                foreach (Claim c in claims)
                {
                    myClaims.Add(new MyClaim()
                     {
                         Identifier = c.ClaimType + "::" + index,
                         Value = c.Value,
                         ValueType = c.ValueType,
                         ClaimType = c.ClaimType,
                         Issuer = c.Issuer,
                         OriginalIssuer = c.OriginalIssuer
                     });
                    index++;
                }
            }
            return myClaims.ToArray();
        }

        private static IClaimsIdentity GetClaimsIdentity()
        {
            IClaimsPrincipal claimsPrincipal = Thread.CurrentPrincipal as IClaimsPrincipal;
            if (claimsPrincipal != null)
            {
                return (IClaimsIdentity)claimsPrincipal.Identity;
            }
            return new ClaimsIdentity();
        }
    }

And the model definition should look something like this…

<?xml version="1.0" encoding="utf-8"?>
<Model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/windows/2007/BusinessDataCatalog" Name="MyClaimsDataModel">
  <LobSystems>
    <LobSystem Name="MyClaimsDataModel" Type="DotNetAssembly">
      <LobSystemInstances>
        <LobSystemInstance Name="MyClaimsDataModel" DefaultDisplayName="My Claims" />
      </LobSystemInstances>
      <Entities>
        <Entity Name="MyClaim" Namespace="SPCecil.Connect.MyClaimsDataModel" EstimatedInstanceCount="1000" Version="1.0.0.31">
          <Properties>
            <Property Name="Class" Type="System.String">SPCecil.Connect.MyClaimsDataModel.MyClaimService, MyClaimsDataModel</Property>
          </Properties>
          <Identifiers>
            <Identifier Name="Identifier" TypeName="System.String" />
            <!-- TODO:change the name of the ID and if needed the TypeName of your identifier.-->
          </Identifiers>
          <Methods>
            <!-- start finder method-->
            <Method Name="ReadList">
              <!-- TODO:change the name of the method if needed.-->
              <Parameters>
                <Parameter Direction="Return" Name="returnParameter">
                  <TypeDescriptor TypeName="System.Collections.Generic.IEnumerable`1[[SPCecil.Connect.MyClaimsDataModel.MyClaim, MyClaimsDataModel]]" IsCollection="true" Name="MyClaimList">
                    <TypeDescriptors>
                      <TypeDescriptor Name="MyClaim" TypeName="SPCecil.Connect.MyClaimsDataModel.MyClaim, MyClaimsDataModel">
                        <TypeDescriptors>
                          <TypeDescriptor Name="Identifier" TypeName="System.String" IdentifierName="Identifier" />
                          <TypeDescriptor Name="ClaimType" TypeName="System.String" DefaultDisplayName="Claim Type" />
                          <TypeDescriptor Name="Value" TypeName="System.String" />
                          <TypeDescriptor Name="ValueType" TypeName="System.String" DefaultDisplayName="Claim Value Type" />
                          <TypeDescriptor Name="Issuer" TypeName="System.String" />
                          <TypeDescriptor Name="OriginalIssuer" TypeName="System.String" DefaultDisplayName="Original Issuer" /></TypeDescriptors></TypeDescriptor></TypeDescriptors>
                  </TypeDescriptor>
                </Parameter>
              </Parameters>
              <MethodInstances>
                <MethodInstance Type="Finder" ReturnParameterName="returnParameter" Default="true" Name="ReadList" DefaultDisplayName="All my claims" />
              </MethodInstances>
            </Method>
            <!-- end finder method-->
            <!-- start specific finder method-->
            <Method Name="ReadItem">
              <Parameters>
                <Parameter Direction="In" Name="id">
                  <TypeDescriptor TypeName="System.String" IdentifierName="Identifier" Name="Identifier" />
                </Parameter>
                <Parameter Direction="Return" Name="returnParameter">
                  <TypeDescriptor TypeName="SPCecil.Connect.MyClaimsDataModel.MyClaim, MyClaimsDataModel" Name="MyClaim">
                    <TypeDescriptors>
                      <TypeDescriptor TypeName="System.String" IdentifierName="Identifier" Name="Identifier" />
                      <TypeDescriptor TypeName="System.String" Name="ClaimType" DefaultDisplayName="Claim Type" />
                      <!-- TODO: add typedescriptors when you add properties to entity1.-->
                      <TypeDescriptor Name="Value" TypeName="System.String" />
                      <TypeDescriptor Name="ValueType" TypeName="System.String" DefaultDisplayName="Claim Value Type" />
                      <TypeDescriptor Name="Issuer" TypeName="System.String" />
                      <TypeDescriptor Name="OriginalIssuer" TypeName="System.String" DefaultDisplayName="Original Issuer" /></TypeDescriptors>
                  </TypeDescriptor>
                </Parameter>
              </Parameters>
              <MethodInstances>
                <MethodInstance Type="SpecificFinder" ReturnParameterName="returnParameter" Default="true" Name="ReadItem" DefaultDisplayName="Read my claim" />
              </MethodInstances>
            </Method>
            <!-- end specific finder method-->
          </Methods>
        </Entity>
      </Entities>
    </LobSystem>
  </LobSystems>
</Model>

After deploying a solution containing these artifacts, you should be able to see the model in Central Administration, if you provider the appropriate permissions to the model in Certral Administration the model should work just fine.

Populating SharePoint picker entity with additional attributes

When granting permission to an individual or a group within SharePoint 2010 picker picker is used to identify then pick the person or group. Identifying is based on the display text shown to the user in a table format. This display text may not be enough for the user to identify the person correctly. This is when the list view of the people picker could be used to distinguish the picker entities a bit more. This requires filling in additional attribute values when generating picker entity from SPClaimProvider.

Details vs List View :

Above picture show the email address field filled in during picker entity creation from SPClaims provider. This is infact very easy to do. Here is how, i will provide code snippet for both a User entity and Group entity.

For User picker entities,

    private PickerEntity CreatePickerEntityFromUserProfile(UserProfile profile)
    {
        PickerEntity entity = CreatePickerEntity();
        entity.Key = profile.PrincipalName;
        entity.DisplayText = profile.Fullname;
        entity.Description = profile.PrincipalName;
        if (string.IsNullOrEmpty(entity.DisplayText))
        {
            entity.DisplayText = profile.PrincipalName;
        }
        entity.Description = profile.PrincipalName;
        entity.EntityData[UserProfileEntityInfo.PrincipalName] = profile.PrincipalName;
        entity.EntityData[UserProfileEntityInfo.Forenames] = profile.Forenames;
        entity.EntityData[UserProfileEntityInfo.Surname] = profile.Surname;
        entity.EntityData[UserProfileEntityInfo.PreferedName] = profile.PreferedName;
        entity.EntityData[UserProfileEntityInfo.UPI] = profile.UPI;
        entity.EntityData[UserProfileEntityInfo.PrimaryEmail] = profile.PrimaryEmail;
        entity.EntityData[UserProfileEntityInfo.Fullname] = entity.DisplayText;

        entity.Claim = SPClaimProviderManager.CreateUserClaim(profile.PrincipalName,
            SPOriginalIssuerType.TrustedProvider, trustedProviderName);

        entity.EntityType = OrganisationalEntityTypes.User;
        entity.EntityGroupName = OrganisationalEntityTypes.PeopleGroup;
        entity.IsResolved = true;

        return entity;
    }

For Group picker entities,

    private PickerEntity CreatePickerEntityFromContextRole(ContextRole contextGroup)
    {
        PickerEntity entity = CreatePickerEntity();
        entity.Key = contextGroup.ContextRoleName;
        entity.DisplayText = contextGroup.ContextRoleName.ToLower();
        entity.Description = entity.DisplayText;
        entity.EntityData[ContextEntityInfo.Name] = entity.DisplayText;
        entity.EntityData[ContextEntityInfo.PrimaryEmail] = contextGroup.Email;
        entity.EntityData[ContextEntityInfo.DisplayName] = entity.DisplayText;

        if (contextGroup.ContextTypeName == "Stream")
        {
            entity.EntityType = OrganisationalEntityTypes.Stream;
            entity.EntityGroupName = OrganisationalEntityTypes.StreamGroupsGroup;
            entity.Claim = new SPClaim(OrganisationalClaimTypes.StreamRole,
                contextGroup.ContextRoleName, OrganisationalClaimTypes.StreamRoleValueType,
                SPOriginalIssuers.Format(SPOriginalIssuerType.TrustedProvider, trustedProviderName));
        }
        else if (contextGroup.ContextTypeName == "Course")
        {
            entity.EntityType = OrganisationalEntityTypes.Course;
            entity.EntityGroupName = OrganisationalEntityTypes.CourseGroupsGroup;
            entity.Claim = new SPClaim(OrganisationalClaimTypes.CourseRole,
                contextGroup.ContextRoleName, OrganisationalClaimTypes.CourseRoleValueType,
                SPOriginalIssuers.Format(SPOriginalIssuerType.TrustedProvider, trustedProviderName));
        }
        entity.IsResolved = true;
        return entity;
    }

In the above code snippets, UserProfile and ContextRole are custom classes that hold information about User and Group respectively. You can probably tell this snippet is from a SPClaimProvider specialized for a university, so you don’t have to use all the attributes only the ones that apply to you organization. PeopleEditorEntityDataKeys class has the attribute names that are most often used withing SharePoint. UserProfileEntityInfo and ContextEntityInfo hold string constants specifying the additional column names.

    public class UserProfileEntityInfo
    {
        public const string PrincipalName = "Principal Name";
        public const string Forenames = "Forenames";
        public const string Surname = "Surname";
        public const string PreferedName = "Prefered Name";
        public const string UPI = "UPI";
        public static string Fullname
        {
            get
            {
                return PeopleEditorEntityDataKeys.DisplayName;
            }
        }

        public static string PrimaryEmail
        {
            get
            {
                return PeopleEditorEntityDataKeys.Email;
            }
        }
    }

    public class ContextEntityInfo
    {
        public static string Name
        {
            get
            {
                return "Context Name";
            }
        }

        public static string DisplayName
        {
            get
            {
                return PeopleEditorEntityDataKeys.DisplayName;
            }
        }

        public static string PrimaryEmail
        {
            get
            {
                return PeopleEditorEntityDataKeys.Email;
            }
        }
    }

There are couple of additional benefits to specifying these attributes. Apart from macking user and group identification easy, it lets any custom code which requires people picker to be able to use these additional attributes in its code in an implementation independent way, which is always a very good thing to do while designing new functionality. Second benefit is in regard to groups. I haven’t been able to create custom profiles for groups, however the only profile functionality i wanted for groups is email attribute. If we populate our picker entities during permission granting SharePoint will copy this email address and store it in the site collection. Thus If you did grant permissions to a group and someone wanted to send an email to this group, this is the email address that would be used. We use a distribution list email per group to full fill this requirement. Btw, if anyone else have another way of doing this please let me know, always keen to learn.