views:

1395

answers:

2

We have a rather large application my team and I are developing that contains a number of WCF NetTCP-based services. The Windows service this system will be running under won't be a local account, but instead a standard domain user (with admin privileges on the servers hosting the service). In the middle of testing connectivity I ran into an issue where SSPI calls fail. Based on a few hours of research this has led me down the path of me missing the following line from my client config:

<identity>
     <userPrincipalName value="MACHINE\user" />
</identity>

The problem with using this is I don't use VS or svcutil to generate a client/proxy for this service - the proxies being used are completely written in code and they inherit System.ServiceModel.ClientBase. I believe the original reason this option was chosen was so we could use the exact same DataMember objects that pass through the services on either side of the fence - third party groups won't need to connect to our services so this wasn't a problem.

Does anyone know a way for me to set userPrincipalName in the client (code or through a config) when I don't have endpoints specified in the standard system.serviceModel configuration section?

Here's what my client-side web.config looks like for reference:

    <system.serviceModel>
    <diagnostics>
        <messageLogging logEntireMessage="true" logMalformedMessages="true"
         logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" />
    </diagnostics>
    <behaviors>
        <serviceBehaviors>
            <behavior name="includeExceptions">
                <serviceDebug includeExceptionDetailInFaults="true"/>
                <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
            </behavior>
        </serviceBehaviors>
    </behaviors>
    <bindings>
        <netTcpBinding>
            <binding name="NetTcpBinding_Default" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="Infinite" sendTimeout="01:00:00" portSharingEnabled="true" transferMode="Buffered" maxReceivedMessageSize="2147483647">
                <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
                <security mode="Transport">
                    <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign"/>
                </security>
            </binding>
        </netTcpBinding>
    </bindings>

</system.serviceModel>
+1  A: 

Although I am probably not answering your question directly, to use the same datamember on both side of the fence, you don't need to create the proxies manually. What you do is that you generate your proxies using svcutil and you pass in the dll that has your datamember as /r

e.g

svcutil http://localhost/service/service.svc /r:AssemblyThatHasDataMembers.dll /out:ServiceProxy.cs

With this, the datamember types are not repeated in your ServiceProxy.cs file. You can customize this extensively by passing wsdl/xsd (true contract first approach), customise the collectiontypes with /ct, etc etc.

This will save you many hours manually crafting your proxies, and at the same time avoid issues like above that you may encounter because everything then becomes stock standard.

Ash M
+1  A: 

Creating your proxies manually does not impede you from putting the configuration in the config file; you just need to expose the right constructor overload in your ClientBase-derived proxy class that delegates to the right constructor in ClientBase that takes the name of an endpoint to look up in the configuration.

That said, you can of course fill in the endpoint identity through code, you just need to create the right kind of EndpointIdentity-derived class and attach it to the EndpointAddress object you use when you instantiate the proxy class. Something like this:

EndpointIdentity epid = EndpointIdentity.CreateUpnIdentity("MACHINE\user");
EndpointAddress epaddr = new EndpointAddress(uri, epid);

MyClient client = new MyClient(epaddr);
tomasr
The first comment by Ash got me started down a path that was completed with the assistance of tomasr and now everything works perfectly.As a side note, to create a new EndpointAddress, you have to specify a Uri, EndpointIdentity and an AddressHeader object to get it to work correctly. Their API is setup a little confusingly (especially with some obscure errors if you don't use base.New correctly), but all told it functions.Thanks for the help!
digitall