Share-n-dipity

SharePoint serendipity is the effect by which one accidentally discovers something fortunate, especially while looking for something else entirely. In this case, it is the occassional musings, observations, and Ouija board readings about the phabulously

The Claims, Azure and SharePoint Integration Toolkit Part 2

The Claims, Azure and SharePoint Integration Toolkit Part 2

  • Comments 1
  • Likes

This is part 2 of a 5 part series on the CASI (Claims, Azure and SharePoint Integration) Kit. 

·         Part 1:  an introductory overview of the entire framework and solution and described what the series is going to try and cover.

·         Part 2:  the guidance part of the CASI Kit.  It starts with making WCF the front end for all your data – could be datasets, Xml, custom classes, or just straight up Html.  In phase 1 we take your standard WCF service and make it claims aware – that’s what allows us to take the user token from SharePoint and send it across application or data center boundaries to our custom WCF applications.  In phase 2 I’ll go through the list of all the things you need to do take this typical WCF application from on premises to hosted up in Windows Azure.  Once that is complete you’ll have the backend in place to support a multi-application, multi-datacenter with integrated authentication.

·         Part 3:  describes the custom toolkit assembly that provides the glue to connect your claims-aware WCF application in the cloud and your SharePoint farm.  I’ll go through how you use the assembly, talk about the pretty easy custom control you need to create (about 5 lines of code) and how you can host it in a page in the _layouts directory as a means to retrieve and render data in web part.  The full source code for the sample custom control and _layouts page will also be posted.

·         Part 4:  the web part that I’m including with the CASI Kit.  It provides an out of the box, no-code solution for you to hook up and connect with an asynchronous client-side query to retrieve data from your cloud-hosted service and then display it in the web part.  It also has hooks built into it so you can customize it quite a bit and use your own JavaScript functions to render the data.

·         Part 5:  a brief walk-through of a couple of sample applications that demonstrate some other scenarios for using the custom control you build that’s described in part 3 in a couple of other common scenarios.  One will be using the control to retrieve some kind of user or configuration data and storing it in the ASP.NET cache, then using it in a custom web part.  The other scenario will be using the custom control to retrieve data from Azure and use it a custom task; in this case, a custom SharePoint timer job.  The full source code for these sample applications will also be posted.

In this post we’ll focus on the pattern for the approach:

1.       Use a custom WCF application as the front end for your data and content

2.       Make it claims aware

3.       Make some additional changes to be able to move it up into the Windows Azure cloud

Using WCF

The main premise of CASI Kit framework is that all application data uses a WCF application as the front end.  Like all custom applications, this is a piece that you, the developer will need to create.  There’s virtually no SharePoint-specific knowledge required for this part of your project – any .NET developer that can use Visual Studio to create a WCF application can do this.  If your ultimate goal is to host this WCF service in Windows Azure then I highly recommend using the Windows Azure development kit to download the templates for creating Azure applications, and start out making an Azure WCF application from the beginning.  There is one important limitation to understand with the current version of the CASI Kit that’s worth pointing out here.  The CASI kit really only supports sending core .NET datatypes as parameters to WCF methods.  So strings, bools, ints and dates work fine, but there isn’t a method to pass in a custom class as a parameter.  If you need to do that however, I recommend creating the parameter as a string and deserializing your parameter to Xml before calling your WCF method, then serializing it back to an object instance in your WCF code.  Beyond that there aren’t any significant limitations I’ve seen so far, but I’m sure a wish list will start forming pretty soon after this is more broadly adopted and used.  As a brief side note, the kit you see today is really just my own v 1.0 ideas of how we could stitch all of these things together and was designed to meet the core scenarios that I’ve thought about and decided were important.  I have no doubt that there will be lots of room for improvement as folks use this.

Make it Claims Aware

Once the WCF application has been created, the next step is to make it claims aware.  For this step I can take absolutely zero credit – I started down this path and will point you to the excellent four part blog post that Eric White from the Office team did to describe how to integrate the claims from SharePoint into a WCF application.  NOTE:  See the update below about getting a sample web.config file if you don't want to work through the integration or if Eric's blog is down. Assuming you’ve built your WCF service already, I would start with part 2 of Eric’s blog series at http://blogs.msdn.com/b/ericwhite/archive/2010/05/13/determining-caller-identity-within-a-wcf-web-service.aspx.  Also you MUST continue on and do the steps he outlines in part 3 at http://blogs.msdn.com/b/ericwhite/archive/2010/06/18/establishing-trust-between-a-wcf-web-service-and-the-sharepoint-2010-security-token-service.aspx starting with the section titled  Procedure: Establish Trust between the Web Service and the SharePoint Server.  You need to do the all of the steps from that point forward, which effectively is copying the thumbprint of the SharePoint STS token signing certificate and copying it and some other information into the web.config file for your WCF application.  There are a couple of important additional points though:

  • I would not follow the SSL steps in part 3 step by step because using a self-signed certificate is not really going to be useful when your application is hosted in Windows Azure.  If you don’t have anything else available then that’s what you’ll need to do, but in general you should plan on getting a proper SSL certificate from an appropriate certificate authority for your Windows Azure WCF application. 
  • There is one point in this procedure that is slightly inaccurate.  Eric describes adding the SSL certificate used on the web service to the list of SPTrustedRootAuthorities in SharePoint only if you use a self-signed certificate.  In fact, you should always add any certs in the SSL cert trust chain to SharePoint's SPTrustedRootAuthorities.  For example, my WCF is secured with a wildcard certificate that looks something like *.vbtoys.com.  *.vbtoys.com has a root authority certificate of vbtoys.com, so I need to add the .CER file (i.e. the public key) for the vbtoys.com root certificate to my trusted root authorities in SharePoint.  You can do this in PowerShell as I've described in various blog entries or via central admin, as I described here:  http://blogs.technet.com/b/speschka/archive/2010/07/07/managing-trusted-root-authorities-for-claims-authentication-in-sharepoint-2010-central-admin.aspx.

NOTE:  You DO NOT need to do the steps on part 4 of Eric’s blog.  Now, once you’ve followed the steps described above you have a working WCF, SharePoint claims –aware application.  In the final phase of this posting, I’ll walk you through the additional steps you need to do in order to move it up into Windows Azure.

Make it Work in Windows Azure

Now that you have your working WCF Azure application, there are a few other things that need to be done in order to continue have it support claims authentication and tokens through Windows Identity Framework (WIF) and host it up in the Windows Azure cloud.  Let’s just knock out the list right here:

1.       Configure your WebRole project (i.e. your WCF project) to use a local virtual directory for debugging.  I find this much easier to work with than the VS.NET development server for things using certificates, which you will want to do.  To change this, double-click on the WebRole project properties then click on the Web tab.  Select the radio button that says “Use Local IIS Web server” and then click the Create Virtual Directory button.  Once the virtual directory has been created you can close the project properties.

2.       Add a reference to Microsoft.Identity in your WebRole project.  You MUST change the reference to Copy Local = true and Specific Version = false.  That is necessary to copy the WIF assembly up into the cloud with your application package.

3.       Get this WCF Hotfix:  http://code.msdn.microsoft.com/KB981002/Release/ProjectReleases.aspx?ReleaseId=4009 for Win2k8 R2, http://code.msdn.microsoft.com/KB971842/Release/ProjectReleases.aspx?ReleaseId=3228 for Win2k8.

4.       You MUST add this attribute to your WCF class:  [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)].  So for example, the class looks like this:

namespace CustomersWCF_WebRole

{

    [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]

    public class Customers : ICustomers

    {

 

5.       You MUST include the following configuration data in the behavior element used by your service.  It fixes issues that can occur with random port assignments in the Azure environment.  To test it locally you will need to get the hotfix described in #3 above:

          <useRequestHeadersForMetadataAddress>

            <defaultPorts>

              <add scheme="http" port="80" />

              <add scheme="https" port="443" />

            </defaultPorts>

          </useRequestHeadersForMetadataAddress>

 

Here’s an example in context, of the web.config for my WCF service:

    <behaviors>

      <serviceBehaviors>

        <behavior name="CustomersWCF_WebRole.CustomersBehavior">

          <federatedServiceHostConfiguration name="CustomersWCF_WebRole.Customers"/>

          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>

          <serviceDebug includeExceptionDetailInFaults="false"/>

          <useRequestHeadersForMetadataAddress>

            <defaultPorts>

              <add scheme="http" port="80" />

              <add scheme="https" port="443" />

            </defaultPorts>

          </useRequestHeadersForMetadataAddress>

        </behavior>

      </serviceBehaviors>

    </behaviors>

 

6.       Upload the SSL certificate you are using for the WCF application to the Azure developer portal first.  NOTE:  The CASI Kit base class is hard-wired to use SSL so you MUST implement SSL support in your WCF Windows Azure application.  Hopefully this should be an expected requirement for passing potentially sensitive data between a cloud service and SharePoint farm. Then add the certificate to the Azure role properties in Visual Studio, by double-clicking on the WebRole project name (in the Roles folder).  I found that using a wildcard certificate worked fine.  You need a PFX certificate though, and make sure you export all certificates in the chain when you create the PFX file.  Azure will expand them all out when you upload it to the developer portal.

7.       Your SSL certificate should be for someName.yourDnsName.com, even though all Azure apps are hosted at cloudapp.net.  For example, my SSL certificate was a wildcard cert for *.vbtoys.com.  In DNS I created a new CNAME record called azurewcf.vbtoys.com, and it referred to myAzureApp.cloudapp.net.  So when I make a connection to https://azurewcf.vbtoys.com my certificate works because my request and SSL certificate is for *.vbtoys.com, but DNS redirects my request based on the CNAME record, which is myAzureApp.cloudapp.net.

8.       In your Azure project double-click on the WebRole project name (in the Roles folder) and set these properties as follows:

a.       Configuration tab:  uncheck the Launch browser for:  HTTP and HTTPS endpoint

b.      Certificates tab:  add the certificate you are going to use for SSL with your service.  For example, in my lab I use a wildcard certificate that’s issued by my domain for all my web servers, so I added my wildcard certificate here.

c.       Endpoints tab:  check both the box for HTTP and HTTPS (the names should be HttpIn and HttpsIn respectively).  In the HTTPS section, the SSL certificate name drop down should now contain the SSL certificate that you added in step b.

9.       If you have a WCF method that returns script, the script tag must include the DEFER attribute in order to work properly when using the web part that is included with the CASI Kit, or if your own JavaScript function assigns it to the innerHTML of a tag.  For example, your script tag should look like this: <script defer language='javascript'>

10.   If you have a WCF method that returns content that includes other formatting tags, like <style>, you need to wrap them in a <pre> tag or they will not be processed correctly when using the web part that is included with the CASI Kit, or if your own JavaScript function assigns it to the innerHTML of a tag.  For example, the content you return with a style tag should look like this: <pre><style>.foo {font-size:8pt;}</style></pre>

Those are the steps that are needed to configure the WCF application to be hosted in Azure; here are some additional tips that may be useful and in some cases required, depending on your implementation:

1.       Use the fully-qualified name when creating the endpoint address that consumes the service, i.e. machineName.foo.com rather than just machineName.  This will transition more cleanly to the final format hosted on Windows Azure and may also clear up errors that occur when your SSL certificate is designed to use a fully-qualified domain name.

2.       You MAY want to add this attribute: httpsGetEnabled="true" to this element: <serviceMetadata httpGetEnabled="true" />, if you want to get your WSDL over SSL.  There is currently a bug in SharePoint Designer though that prevents you from using SSL for WSDL.

3.       For debugging and data connection tips see my post at http://blogs.technet.com/b/speschka/archive/2010/09/19/azure-development-tips-for-debugging-and-connection-strings.aspx.

4.       In most cases you should assume your WCF service namespace is going to be http://tempuri.org.  For instructions on how to change it you can read the post at http://blogs.infosupport.com/blogs/edwinw/archive/2008/07/20/WCF_3A00_-namespaces-in-WSDL.aspx.

The Finished WCF Service

If you’ve followed all of the above configuration steps and deployed your WCF application to Windows Azure, when a user makes a call to that WCF service from a SharePoint site you will also get his or her entire user token, with all the claims that are associated with it.  It’s also worth noting that after you make these changes the WCF service will also work on premise, so it is quite easy to test if you want to try out some incremental changes before pushing the application up to the cloud.  Having that user token allows you to do some really interesting things in your WCF service.  For example, within your WCF service you can enumerate all of a user’s claims and make any kind of fine-grained permissions decisions based on that.  Here’s an example of using LINQ against the set of user claims to determine whether the current user is an administrator, because if he or she is then some additional level of detail will be returned in the request:

//look for the claims identity

IClaimsIdentity ci =

System.Threading.Thread.CurrentPrincipal.Identity as IClaimsIdentity;

 

if (ci != null)

{

//see if there are claims present before running through this

       if (ci.Claims.Count > 0)

       {                       

       //look for a group claim of domain admin

var eClaim = from Microsoft.IdentityModel.Claims.Claim c in ci.Claims

              where c.ClaimType ==

"http://schemas.microsoft.com/ws/2008/06/identity/claims/role" &&

                     c.Value == "Domain Admins"

                     select c;

 

              //see if we got a match

              if (eClaim.Count() > 0)

                     //there’s a match so this user has the Domain Admins claim

                     //do something here

}

}

 

What’s equally cool is that you can also make permission demands directly on your WCF methods.  For example, suppose you have a WCF method that queries a data store and returns a list of customer CEOs.  You don’t want this information available to all employees, only Sales Managers.  One very slick and easy way to implement this is with a PrincipalPermission demand on the method, like this:

//the customer CEO list should not be shared with everyone,

//so only show it to people in the Sales Manager role

[PrincipalPermission(SecurityAction.Demand, Role = "Sales Managers")]

public string GetCustomerCEOs()

{

//your code goes here

}

 

Now if anyone tries to call this method, if they don’t have a claim for “Sales Managers” then they will get an access denied if they are running code that tries to call this method.  Very cool!

It’s also important to understand that this cannot be spoofed.  For example, you can’t just create your own domain in a lab, add an account to it and create a Sales Manager role to which you add that account.  The reason it won’t work goes back to the steps that you did when going through Eric White’s blog (in the section above titled Make It Claims Aware).  If you recall, you added the thumbprint of the token signing certificate used by the SharePoint STS.  That means when a claim comes into your WCF application, it will look for the token to be signed by the public key of the SharePoint STS.  Only the SharePoint STS can sign it with that public key, since it’s the only entity that has the private key for that token signing certificate.  This assures you that only someone who has authenticated to that SharePoint farm will be able to use the WCF service, and users will only have the claims that were granted to them at the time they logged in.  What’s also interesting about that though is that it includes not only claims that were granted to them when they authenticated to their user directory, but ALSO any additional claims that were given to them via claims augmentation in SharePoint with any custom claims providers.  So this really is a truly integrated end-to-end solution.

UPDATE 8/6/2011:  Since Eric White's blog has now been moved and I'm not sure how long the old one will be up, I created a sample web.config file with all the SharePoint SAML trust goodness baked in and have included it in the zip file that's part of this posting.  The parts you need to change for your project are clearly marked with an html comment and five stars, like this:  <!-- *****.  Just look for those and update to reflect your project and SharePoint farm and you should be good to go.

Next Steps

In the next post, I’ll start describing the custom base class and web part that comes with the CASI Kit, which will allow you to connect to your new Azure WCF application very quickly and easily.  Also, from this point forward I’ll be using a WCF service I wrote for the CASI Kit as means to demonstrate the functionality.  I’m attaching the .cs file that I used for this service to this posting.  You won’t be able to use it as is, but it’s included merely so you can see the different methods that it contains, the type of data it contains, and how certain features were implemented for this kit.  Primarily during the following postings you’ll mainly see me using the a) GetAllCustomersHtml, b) GetCustomerCEOs, and c) GetAllCustomers methods.  They are interesting because they a) return HTML (which is going to be the defacto preferred return type for displaying data in web parts), b) use a PrincipalPermission demand and c) show how you can return a custom class type from your WCF application and use that same rich class type after you get that data back in SharePoint with the CASI Kit.

Attachment: CASI_Kit_Part2.zip
Comments
Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment