6. Impersonation
Some resources, such as the file system, SQL Server,
sockets, and even DCOM objects, grant access to themselves based on
the caller’s security token. Typically, the host process is assigned
an identity with elevated permissions that are required to access such
resources, so that it can function properly. Clients, however,
typically have restricted credentials compared with those of the
service. Legacy technologies such as unmanaged Visual Basic or C++ did
not offer role-based security support, so developers used
impersonation to address this credentials gap.
Impersonation lets the service assume the client’s identity, primarily
in order to verify whether the client is authorized to perform the
work it’s asking the service to do. Impersonation has a number of key
detrimental effects on your application, which will be discussed at
the end of this section. Instead of impersonation, you should apply
role-based security to authorize the callers, coupled with a trusted
subsystem pattern across layers. That said, many developers are used
to designing systems using impersonation, so both .NET and WCF support
this technique.
6.1. Manual impersonation
The service can impersonate its calling client by calling the
Impersonate() method of the
WindowsIdentity
class:
public class WindowsIdentity : IIdentity,...
{
public virtual WindowsImpersonationContext Impersonate();
//More members
}
public class WindowsImpersonationContext : IDisposable
{
public void Dispose();
public void Undo();
}
Impersonate() returns an
instance of WindowsImpersonationContext containing the
service’s previous identity. To revert back to that identity, the
service calls the Undo() method.
To impersonate a client, the service needs to call Impersonate() on the identity of the
caller, which is available via the WindowsIdentity property of its security
call context, as shown in Example 2.
Example 2. Explicit impersonation and reversion
class MyService : IMyContract { public void MyMethod() { WindowsImpersonationContext impersonationContext = ServiceSecurityContext.Current.WindowsIdentity.Impersonate(); try { /* Do work as client */ } finally { impersonationContext.Undo(); } } }
|
Note in Example 2 that the call to
Undo() is in the finally statement, so the service will
revert to its old identity even if exceptions occur. To somewhat
simplify reverting, the WindowsImpersonationContext implementation
of Dispose() also reverts, which
enables you to use it in a using
statement:
public void MyMethod()
{
using(ServiceSecurityContext.Current.WindowsIdentity.Impersonate())
{
/* Do work as client */
}
}
6.2. Declarative impersonation
Instead of impersonating manually, you can instruct WCF to
automatically impersonate the caller of the method. The OperationBehavior attribute offers the
Impersonation property of the
enum type ImpersonationOption:
public enum ImpersonationOption
{
NotAllowed,
Allowed,
Required
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class OperationBehaviorAttribute : Attribute,IOperationBehavior
{
public ImpersonationOption Impersonation
{get;set;}
//More members
}
The default value is ImpersonationOption.NotAllowed. This value
indicates that WCF should not auto-impersonate, but you can write
code (as in Example 2)
that explicitly impersonates.
ImpersonationOption.Allowed
instructs WCF to automatically impersonate the caller whenever
Windows authentication is used, but it has no effect with other
authentication mechanisms. When WCF auto-impersonates, it will also
auto-revert to the previous service identity once the method
returns.
The ImpersonationOption.Required value
mandates the use of Windows authentication and will throw an
exception if any other authentication mechanism is used. As its name
implies, with this setting WCF will always auto-impersonate (and
revert) in every call to the operation:
class MyService : IMyContract
{
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public void MyMethod()
{
/* Do work as client */
}
}
Note that there is no way to use declarative impersonation
with the service constructor because you cannot apply the OperationBehavior attribute on a
constructor. Constructors can only use manual impersonation. If you
do impersonate in the constructor, always revert as well in the
constructor, to avoid side effects on the operations of the service
(and even other services in the same host).