If there is one thing in software development which I don't really like, it is for sure something called "Trusted Delegation". I'm astonished how this "trouble making feature" is almost completely unknown in the world of software developers. I do not know what the reason is, but something's telling me, that it will make all (windows and none-windows) developers many, many troubles in the near future.
To understand what I'm talking about, just take a look on following very common scenario. Imagine you implement the system which consist of one WCF based client (C), one WCF based service (S). The client C invokes one operation O at the service S. The operation O has to open the file F and return some data from file to the client C.
To be honest, this example is a little more advanced Hello World.
Unfortunately, this "Hello World" example is much complicated than most developers would expect. If you ask one architect about this example he will probably ask some additional question like:
- "What is the user security account running the client C"?
- "What is the user security account running the service S?"
- Who is permitted to access the file F?
- Last but not least: "Does the service need to impersonate the user account running on the client?"
- Is there a scenario which requires that the users' account from the client has to be delegated
Without of answering ALL of these questions, you will NEVER be able to deploy such service to your customer. Why?
When the client C and the services, for the sake of simplicity, are both running on the same machine and the file F is stored on some other machine, all will work fine. The only requirement to make it working is to use proper binding and to set the Endpoint Identity like shown in following example:
<endpoint address=""
binding="wsHttpBinding"
contract="Microsoft.ServiceModel.Samples.IMyService" >
<identity>
<userPrincipalName value="svcuseraccount@DOMAIN " />
</identity>
</endpoint>
The user principal name is the name of the user account of the service: DOMAIN\svcuseraccount. The same identity has to be set on the service and on all clients. This is the ability of WCF to prevent phishing attacks.
The service operation AccessFile looks like:
public void AccessFile()
{
TryFileAccess();
}
private static void TryFileAccess()
{
try
{
File.OpenRead(@\\REMOTEHOST\testpermit.txt);
Console.WriteLine("ok");
}
catch (Exception ex)
{
Console.WriteLine("deny");
}
}
The client would invoke the operation as shown below:
CalculatorProxy proxy = new CalculatorProxy();
proxy.AccessFile();
When the service tries to open the file the user who is opening the file is the service user. In this case DOMAIN\svcuseraccount. If this user has the access permission to the file, the file will be opened. If the client is started on the same machine as service (which is an EXE) client's and service's users are same: DOMAIN\svcuseraccount. If you start now the client in context of some other user i.e.: DOMAIN\clientuser, the service will still access the file as DOMAIN\svcuseraccount.
Now, what if the requirement is that he file should be accessed on behalf of the user who is running the client? That means, that the code in operation AccessFile should run as it would be invoked by user DOMAIN\clientuser. This is in many enterprises very common scenario.
To do that following changes have to be performed. Set the impersonation at the operation in the service
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public double Add(double n1, double n2)
and request the kind of impersonation at the client side:
proxy.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
CalculatorProxy proxy = new CalculatorProxy();
proxy.AccessFile();
When you now starts the client with "runas" as user DOMAIN\clientuser the service will in the operation impersonate the user as it would be DOMAIN\clientuser and not the service account DOMAIN\svcuseraccount. Now, the file will be opened if the DOMAIN\clientuser has a permission to open the file independent of the permission of service user DOMAIN\svcuseraccount.
Following code can be used to display various user identities:
static void DisplayIdentityInformation()
{
Console.WriteLine("\t\tWindows Identity :{0}", WindowsIdentity.GetCurrent().Name);
Console.WriteLine("\t\tThread Impersonation level :{0}", WindowsIdentity.GetCurrent().ImpersonationLevel);
Console.WriteLine("\t\thToken :{0}", WindowsIdentity.GetCurrent().Token.ToString());
Console.WriteLine("\t\tThread Identity :{0}", Thread.CurrentPrincipal.Identity.Name);
}
In this post I showed how to impersonate the client's user account in the service. Describes solution is very simple. Unfortunately thing get complicated whet the client C, service S and the file F are all on different machines. In this example we used the TokenImpersonationLevel impersonate. This level is used when the resources accessed from the service are on the same machine as service. If the service has to access the resource (in this case file F), which is on some other machine then the TokenImpersonationLevel delegate has to be used. In my next post I will describe how to do that.
Posted
Oct 14 2007, 12:01 AM
by
Damir Dobric