jfbeaulieu80

Can't get AF-SDK to foward user's credentials to PI server

Discussion created by jfbeaulieu80 on Apr 24, 2012
Latest reply on Jan 22, 2013 by pcombellick

Hi,

 

We are building a system that uses PI-AF to feed Silverlight custom controls. We use a Silverlight 5 client, a WCF service, AF-SDK and PI-SDK. The Silverlight client and the WCF Services are hosted on IIS 7.5 (Windows 2008 R2) running on the same machine than the AF server. The PI Server is running on another server (Windows 2008 R2). For the moment, all these servers are part of the same domain drove by ActiveDirectory. Here is representation of our architecture.

 

2671.Sch_E900_ma-applicatif.jpg

 

 

 

Because we are using advanced features of the AF SDK, we cannot switch to PI-WebServices.

 

Ok enough with the introduction. With this architecture, we are experiencing issue getting values from the PI-Server. I looked at the PI Message Log and found the WebService was using his credentials to call the PI-Server (GetValue of a PIPoint using AF-ADK) instead of the client’s credentials.

 

I’m well aware that it might be a “double hop” kind of issue. But I double checked everything (that I could think of) and it’s still not working.

 

Here are some observations I made:

 

-       I made sure to impersonate the caller in the WebService

 

o   This part is working. The AF current user contains the client credential.

 

-       I checked at the ImpersonationLevel of the ServiceSecurityContext.Current.Identity and it is Delegation (so it should delegate the user credentials to PI-Server).

 

-       Kerberos is used by the client to connect to the WebService (Security Audit log event)

 

o   Settings in ActiveDirectory seems good

 

o   Settings in IE8 seems good

 

-       Kerberos is also used by AF-SDK to connect to PI, but with the WebService Credentials

 

o   I can see that in the PI Message Log and in the PI-Server Security Audit log event.

 

o   Setting in ActiveDirectory seems good

 

This is how I try to achieve my task:

 

First, here is the servicereference.clientconfig

 

<configuration>
    <system.serviceModel>
        <bindings>
          <basicHttpBinding>
                <binding name="BasicHttpBinding_IAFService" maxBufferSize="2147483647"
                    maxReceivedMessageSize="2147483647">
                  <security mode="None" />
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>

 

          <endpoint address="http://servername:8080/AFWCFService.svc"
              binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IAFService"
              contract="AFServiceProxy.IAFService" name="BasicHttpBinding_IAFService"/>

 

        </client>
    </system.serviceModel>
</configuration>

 

Second, the Web.config file of the WCF Service

 

 <?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appSettings>
    <add key="AFServerName" value="AF_SERVER" />
    <add key="AFDatabaseName" value="AF_DATABASE" />
  </appSettings>

 

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
        <authorization>
            <deny users="?" />
        </authorization>
    <authentication mode="Windows" />
    <identity impersonate="true" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="AFWCFServiceLibrary.AFService">
        <endpoint binding="basicHttpBinding" bindingConfiguration="basicHttpSecureBinding" contract="AFWCFServiceLibrary.IAFService">
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true" />
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceCredentials>
            <windowsAuthentication includeWindowsGroups="true" allowAnonymousLogons="false" />
            <issuedTokenAuthentication allowUntrustedRsaIssuers="true" />
          </serviceCredentials>
          <serviceAuthorization principalPermissionMode="UseWindowsGroups" impersonateCallerForAllOperations="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <basicHttpBinding>
        <binding name="basicHttpSecureBinding">
          <security mode="TransportCredentialOnly">
            <transport clientCredentialType="Windows"/>
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
  </system.webServer>
 
</configuration>

 

Now, here is how impersonation/delegation takes place. 

 

 

 
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class AFService: IAFService
{
     ...
     
     public GetElementsWithAttributesResponse GetElementsWithAttributes(GetElementsWithAttributesRequest req)
     {
          using (WindowsImpersonationContext impersonationContext = ServiceSecurityContext.Current != null ? ((WindowsIdentity)ServiceSecurityContext.Current.WindowsIdentity).Impersonate() : null)
          {
               ...

               // AFHelper is a Helper class that contains all AF-SDK calls
               AFHelper afHelper = new AFHelper(ConfigurationManager.AppSettings["AFServerName"],
                                                        ConfigurationManager.AppSettings["AFDatabaseName"])
               // Add a log to display the current connected AF user
               log.Info(string.Format(Constants.LogMessage.ConnectedToAFAs, afHelper.GetCurrentConnectedUserName(ConfigurationManager.AppSettings["AFServerName"])));
               
               ...
               // Retreiving AF Values 
               afValues = attributList.GetValue(req.QueryDate);
               ...
          }
          
          ...
     }
     
     ....
}

 

 

At this point, I know that impersonation is working because I can see the current user in AF. I also ran some tests and established I was able to view information available based on security policies I set up in AF. So the client credentials are used here.  But the call to PI seems to use the wcf service credentials. So from here, I suppose delegation is not kicking in. But I can’t figure why.

 

Last, but not least, here is how I setup my connection to AF. I used the Connect method (with no parameter) from the PISystem object which should pass the impersonated user’s credentials. As I mentioned earlier, this seems to work.

 
public class AFHelper
{

     public AFHelper(string afServerName, string afDatabaseName)
     {
          _piSystems = new PISystems();
          PISystem thisPISystem;
          if (_piSystems != null)
          {
               thisPISystem = _piSystems[afServerName];
               // Use the impersonated user (client credentials)
               thisPISystem.Connect();

               _db = thisPISystem.Databases[afDatabaseName];
               _db.Refresh();
               _afServerName = afServerName;
          }
     }
}

 

 

So anybody knows why can’t I get AF-SDK to pass user’s credentials to the PI server ?

Outcomes