To expand on Ladislav's answer:
No. A custom UserNamePasswordValidator cannot be used as a role provider. The UserNamePasswordValidator runs in a separate context (or thread, or something) from the OperationContext that you want to mess with.
What you need to do instead is implement custom authorization. I found this page most useful for doing this. Warning: there's a lot of plumbing before you get to the interesting bits.
Essentially, you start with a ServiceCredentials
-derived class, registered in App.config
, as follows:
<serviceBehaviors>
<behavior name="...">
<serviceAuthorization principalPermissionMode="Custom" />
<serviceCredentials type="MyNamespace.MyServiceCredentials, MyAssembly">
<userNameAuthentication userNamePasswordValidationMode="Custom" />
<serviceCertificate etc. />
</serviceCredentials>
Associate the behavior with your service.
Override ServiceCredentials.CreateSecurityTokenManager
to return a MySecurityTokenManager
, derived from ServiceCredentialsSecurityTokenManager
. On that, override CreateSecurityTokenAuthenticator
, returning a MySecurityTokenAuthenticator
. That should be derived from CustomUserNameSecurityTokenAuthenticator
. In that, override ValidateUserNamePasswordCore
. Call the base class, which will return a list of authorization policies.
To that list, add a new one: MyAuthorizationPolicy
, which implements IAuthorizationPolicy
. In that, you merely (hah) need to do the following:
public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
IList<IIdentity> identities = GetIdentities(evaluationContext);
// Find the GenericIdentity with our user-name in it.
IIdentity currentIdentity = identities.SingleOrDefault(
i => i is GenericIdentity &&
StringComparer.OrdinalIgnoreCase.Equals(i.Name, UserName));
if (currentIdentity == null)
throw new InvalidOperationException("No Identity found");
// Replace the GenericIdentity with a new one.
identities.Remove(currentIdentity);
var newIdentity =
new GenericIdentity(_userName, currentIdentity.AuthenticationType);
identities.Add(newIdentity);
// This makes it available as
// ServiceSecurityContext.Current.PrimaryIdentity later.
evaluationContext.Properties["PrimaryIdentity"] = newIdentity;
// This makes it available as Thread.CurrentPrincipal.
IPrincipal newPrincipal = new GenericPrincipal(newIdentity, _roles);
evaluationContext.Properties["Principal"] = newPrincipal;
return true;
}
private static IList<IIdentity> GetIdentities(
EvaluationContext evaluationContext)
{
object identitiesProperty;
if (!evaluationContext.Properties.TryGetValue(
"Identities", out identitiesProperty))
throw new InvalidOperationException("No Identity found");
var identities = identitiesProperty as IList<IIdentity>;
if (identities == null)
throw new InvalidOperationException("No Identity found");
return identities;
}
And then, having done that lot, you can mark up your service operations with PrincipalPermission
:
[PrincipalPermission(SecurityAction.Demand, Role = "Editor")]