views:

234

answers:

1

I'm currently using Castle-Windsor with the WCF Facility to inject all my WCF services. I've just started adding permission requirements using a custom IAuthorizationPolicy, which seems to work when done on a per-method basis on the service, but when the service class itself is marked up with the requirements, I get an exception thrown.

I've set things up based on the example at How To – Use Username Authentication with Transport Security in WCF from Windows Forms. I didn't set up the custom HTTP Module class as I'm using an existing Membership implementation. My IAuthorizationPolicy implementation (HttpContextPrincipalPolicy) is essentially identical.

The essential part of my Web.config is:

<serviceBehaviors\>
  <behavior name="MyBehavior">
    <serviceMetadata httpGetEnabled="true" />
    <serviceDebug includeExceptionDetailInFaults="false" />
    <serviceAuthorization principalPermissionMode="UseAspNetRoles"
                          roleProviderName="UserProvider">
      <authorizationPolicies>
        <clear/>
        <add policyType="Website.FormsAuth.HttpContextPrincipalPolicy,Website"/>
      </authorizationPolicies>
    </serviceAuthorization>
  </behavior>
</serviceBehaviors>

Everything seems to work fine when I put the requirements on the method. This is being done like so:

[PrincipalPermission(SecurityAction.Demand, Role = RoleNames.USER_ADMINISTRATION)]

If this is on an OperationContract method, things work as expected. However, if it is moved to the class itself (which implements the ServiceContract) I get the following exception (with most of the extra stuff pruned out):

Castle.MicroKernel.ComponentActivator.ComponentActivatorException {
    Message = "ComponentActivator: could not instantiate Services.UserService"
    InnerException = System.Reflection.TargetInvocationException {
        Message = "Exception has been thrown by the target of an invocation."
        InnerException = System.Security.SecurityException {
            Message = "Request for principal permission failed."
        }
    }
}

I've debugged and found that the constructor on HttpContextPrincipalPolicy is being called but Evaluate() is not when the demand is attached to the class. When it is attached to the method Evaluate() is being called. So at this point I've gone as far as my newbie .NET/WCF/Castle-Windsor skills will take me.

Is there a way to tell Castle-Windsor to invoke the service constructor while honoring the IAuthorizationPolicy? Or tell WCF that Evaluate() needs to be called for the creation of the class? Or is there some other way around WCF that does the same thing? I don't want to have to mark up every single method with the exact same bit of attribute declaration.

A: 

When you mark the class itself up with a PrincipalPermissionAttribute it's effectively saying to the runtime that at the point when the class is used the permission demand must be met. So now when Castle-Windsor is trying to instantiate the class, the permission demand is being made and of course it can't be fulfilled because the security context isn't established correctly at that point.

AFAIK, PrincipalPermissionAttribute is not supported on the class level for WCF due to the nature of its runtime even though it is allowed from a pure .NET perspective. Castle-Windsor is therefore unable to create your service instance for the same reason.

Drew Marsh
That seems to match what I'm seeing. I wonder though, is there another way to do something similar without having to mark up each method?
Michael Johnson
Well you can do anything you want if you're writing your own IAuthorizationPolicy. So if you wanted to create a security model where access was controlled at a service level you could check the claims on the current identity to see if they're allowed to access that service. The only difference is you're responsible for storing the access control list data in your own form as opposed to using PrincipalPermissionAttribute. This could be a custom attribute you write yourself or you could store it outside of the static code in a database/configuration file.
Drew Marsh