Thursday, January 21, 2010

Impersonation in Project Server 2010 beta

There are a lot of articles about impersonation in Project Server. It is almost about two additional headers in each request to PSI web services: “PjAuth” with impersonation data created by PSContextInfo class and “ForwardedFrom” with target relative path. One additional thing you have to add is ConnectionGroupName property of a web request to your site UID. So it looks like:

webRequest.UseDefaultCredentials = true;
webRequest.Headers.Add("PjAuth", context.GetImpersonationHeader());
webRequest.Headers.Add("ForwardedFrom", forwardFrom);
webRequest.PreAuthenticate = true;
webRequest.ConnectionGroupName = context.SiteId.ToString();

if (webRequest is HttpWebRequest)
    ((HttpWebRequest) webRequest).UnsafeAuthenticatedConnectionSharing = true;

You can do this by overriding "GetWebRequest" method in web service proxy classes or by interceptors using “method by name” injection, it is up to you.

But there are several changes in Project Server 2010 beta regarding the impersonation feature. Actually there are only two issues: a claim instead of the user name and the secure URLs for the web services.


We are completely ok with the claim, just pass the user claim which can be created by SPClaimProviderManager to PSContextInfo structure. Here is the example of how to get a claim for the specified user name:

public static string GetClaim(string userAccount)
{
    string claimsRepresentation = userAccount;
    if (!SPClaimProviderManager.IsEncodedClaim(userAccount))
    {
        SPClaim claim = SPClaimProviderManager.Local.ConvertIdentifierToClaim(userAccount, SPIdentifierTypes.WindowsSamAccountName);
        claimsRepresentation = claim.ToEncodedString();
    }
    return claimsRepresentation;
}

But there is a trick about secure URLs. Secure path looks like “/ad7c153145e443d6a8dea014cab422e6/PSI/project.asmx” and as we can see the first part is some UID. There is no information about how we could get a secure URL or UID of the web service in runtime. Of course you always can find UID in the IIS Manager and add it to the configuration file.


But what if you want to avoid any manual actions at deployment? And there is a way. It can’t be recommended due to possible future changes, but the idea is to invoke “GetPsiBaseUrl” method of PSI class from Microsoft.Office.Project.PWA namespace. It is not a public one but static and will find for you a secure URL to any service using loading balancer. So the following code will get a secure URL:

MethodInfo methodInfo = typeof(PSI).GetMethod("GetPsiBaseUrl",                                                                   BindingFlags.Static | BindingFlags.NonPublic);
object url = methodInfo.Invoke(null, new object[] { serviceName });

The impersonation in the RTM build is pretty much the same as the impersonation in Project Server 2007, with some minor issues. We’ll talk about it some other time. For more information there is a great article in MSDN about impersonation with WCF http://msdn.microsoft.com/en-us/library/ff181538(office.14).aspx.

5 comments:

  1. Have you been able to use impersonation to add timesheet lines in 2010. I was able to do this in 2007 w/asmx, but unable to do this in 2010 using wcf. Any examples or hints would be appreciated.

    ReplyDelete
  2. We had similar if not same problem using Statusing WCF service for submitting user's working time.
    Sounds weird, but the resolution was to "impersonate user as current user".
    Anyway, we are planning to blog our story more widely in near future.

    ReplyDelete