Custom ServiceAuthorizationManager

Hi,

In Feb CTP I tried it :

ServiceHost host = new ServiceHost(typeof(Hello), "http://localhost:9090/Hello");
host.AddServiceEndpoint(
typeof(IHello), new BasicHttpBinding(), "");
host.Authorization.PrincipalPermissionMode = System.ServiceModel.Description.
PrincipalPermissionMode.Custom;
host.Authorization.ServiceAuthorizationManager =
new AuthorizationDomain();
host.Open();

public class AuthorizationDomain : ServiceAuthorizationManager
{
public override bool CheckAccess(OperationContext operationContext)
{
return true;
}
protected override ReadOnlyCollection<IAuthorizationPolicy> GetAuthorizationPolicies
(
OperationContext operationContext)
{
return new ReadOnlyCollection<IAuthorizationPolicy>(new IAuthorizationPolicy[]
{
new AuthorizationDomainPolicy() });
}
}

public class AuthorizationDomainPolicy : IAuthorizationPolicy
{
private string _id = Guid.NewGuid().ToString();
public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
return true;
}

public ClaimSet Issuer
{
get { return ClaimSet.Anonymous; }
}

public string Id
{
get { return _id; }
}
}

In the Client I receive this exception :

"The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error."

Does anybody know how to solve

I tried this example too, but receive same exception.

http://forums.microsoft.com/MSDN/ShowPost.aspx PostID=97471&SiteID=1
http://forums.microsoft.com/MSDN/ShowPost.aspx PostID=156731&SiteID=1

Thanks,
Alexnaldo Santos



Answer this question

Custom ServiceAuthorizationManager

  • Buckley2232

    Here is my IAuthorizationPolicy implementation - I use this to identify application users (either using username/password authentication or Windows authentication) and then construct a custom principal.

    Hope it helps...

    internal sealed class AppUserAuthorizationPolicy
    : IAuthorizationPolicy {

    Guid id;
    ClaimSet issuer;

    public AppUserAuthorizationPolicy() {
    this.id = Guid.NewGuid();
    this.issuer = ClaimSet.System;
    }

    public bool Evaluate(EvaluationContext evaluationContext, ref object state) {
    object identities;
    List<IIdentity> identitiesList = null;
    IIdentity authorisedIdentity;

    if (evaluationContext.Properties.TryGetValue("Identities", out identities)) {
    identitiesList = identities as List<IIdentity>;
    }
    if (identitiesList == null
    || identitiesList.Count == 0) {
    AppDiagnostics.Current.TraceInformation(
    "AppUser Authorisation Policy evaluation failed: No " +
    "Identities available.");
    return false;
    }

    // have we got an identity authorised by our username/password validator ...
    authorisedIdentity = identitiesList.Find(
    delegate(IIdentity match) {
    return String.Equals(match.AuthenticationType,
    "AppUserNamePasswordValidator",
    StringComparison.Ordinal);
    });

    // if not, we also support windows authentication...
    if (authorisedIdentity == null) {
    authorisedIdentity = identitiesList.Find(
    delegate(IIdentity match) {
    return match is WindowsIdentity;
    });
    }

    // if neither idendity, then not authorised
    if (authorisedIdentity == null) {
    AppDiagnostics.Current.TraceInformation(
    "AppUser Authorisation Policy evaluation failed: No " +
    "authorised identity available.");
    return false;
    }

    // so we are authorised (username/password or windows)...
    // go get custom Principal data...
    AppUserPrincipal appPrincipal = null;
    try {
    appPrincipal = AuthenticationProvider.Current.CreateAppPrincipal(
    authorisedIdentity);
    }
    catch (SqlException sqlError) {
    AppDiagnostics.Current.TraceError(sqlError);
    return false;
    }
    catch (SecurityException securityError) {
    AppDiagnostics.Current.TraceError(securityError);
    return false;
    }
    Debug.Assert(appPrincipal != null);

    evaluationContext.Properties["Principal"] = appPrincipal;

    evaluationContext.AddToTarget(this, new DefaultClaimSet(
    ClaimSet.System,
    new Claim(
    "mynamepsace",
    "AppUser",
    Rights.PossessProperty)));

    return true;
    }

    public ClaimSet Issuer {
    get { return this.issuer; }
    }

    public string Id {
    get { return this.id.ToString(); }
    }
    }


  • netexplorer

    I used another idea from it :

    0) I have only one 'class' for ALL services contract and per Session

    [ServiceBehavior(AutomaticSessionShutdown=true,InstanceContextMode=InstanceContextMode.PerSession)]
    public class MySampleServices :ISampleService1,ISampleService2
    {
    }

    1) Uses 'username' credentials with 'aspnet' 2.0 security


    <bindings>
    <
    wsHttpBinding>
    <
    binding name="bindingWS" transactionFlow="true">
    <
    reliableSession enabled="true"/>
    <
    security mode="Message" >
    <
    message clientCredentialType="UserName"/>
    </
    security>
    </
    binding>
    </
    wsHttpBinding>
    </
    bindings>
    <behaviors>
    <
    behavior name="BehaviorWS" returnUnknownExceptionsAsFaults="true" >
    <
    serviceCredentials>
    <
    userNameAuthentication userNamePasswordValidationMode="MembershipProvider"/>
    <
    serviceCertificate findValue="Rentsoftware" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
    </
    serviceCredentials>
    <
    serviceAuthorization principalPermissionMode="UseAspNetRoles"/>
    </
    behavior>
    </
    behaviors>

    2) Create a personal 'principal' for my security.
    3) Now, I can write two types of security :

    [PrincipalPermission(SecurityAction.Demand, Role = "admin")]
    [
    OperationBehavior(TransactionScopeRequired = true)]
    public void MyMethod1()
    {
    }

    or

    [OperationBehavior(TransactionScopeRequired = true)]
    public void MyMethod2()
    {
    SecureAccessByRole("admin");
    }

    Here's the code :

    private Nano.Security.NanoPrincipal _principal;
    public Nano.Security.NanoPrincipal Principal
    {
    get
    {
    if (_principal == null)
    {
    lock (AppDomain.CurrentDomain)
    {
    System.Security.Principal.
    IIdentity identity = Thread.CurrentPrincipal.Identity;
    string[] permissions = Nano.AspnetPlus.Operations.GetAuthorizedOperationsForUser(identity.Name).ToArray();
    string[] roles = Nano.AspnetPlus.RolesPlus.GetAuthorizedRolesForUser(identity.Name);
    _principal =
    new Nano.Security.NanoPrincipal(identity, roles, permissions);
    }
    }
    return _principal;
    }
    }

    bool IsAdministrator
    {
    get { return Principal.IsInRole(RentsoftwareRolesNames.Administrators); }
    }

    void SecureAccessByRole(string roleName)
    {
    if (!Principal.IsInRole(roleName) && !IsAdministrator)
    {
    string errorMsg = string.Format("User '{0}' must be a member of '{1}' !", System.Threading.Thread.CurrentPrincipal.Identity.Name, roleName);
    throw new System.IdentityModel.Tokens.SecurityTokenException(errorMsg);
    }
    }

    void SecureAccessByPermission(string permissionName)
    {
    if (!Principal.IsAuthorized(permissionName) && !IsAdministrator)
    {
    string errorMsg = string.Format("User '{0}' doesn't have permission to '{1}' !", System.Threading.Thread.CurrentPrincipal.Identity.Name, permissionName);
    throw new System.IdentityModel.Tokens.SecurityTokenException(errorMsg);
    }

    }


  • Bharat Gadhia

    Indigo sets Thread.CurrentPrincipal for you when you set the Impersonation property on OperationBehavior attribute in your contract to "ImpersonationOption.Allowed" or "ImpersonationOption.Required" or when you use PrincipalPermission contract attribute.

    Indigo creates IPrincipal implementation based on the authentication mode and the information contained in the incoming security tokens. Can you explain to me the scenario where the Indigo provided implementation of the IPrincipal is not sufficient for you so that you need to create your own implementation of IPrincipal

    Thanks,

    --Jan


  • praveen_josh

    Hi Alexanaldo,

    Is it possible for you to post your code. This is exactly what i need for my project too.

    I greatly appreciate it!

    Thanks,

    Kumar


  • Eugene Nalimov

    I need to use 'UserName' authentication, so I will create IPrincipal implementing the
    IAuthorizationPolicy.

    The code doesn't work only with Feb CTP !

    --------------------- Authentication Code -------------------------------------

    public class AuthorizationDomain : ServiceAuthorizationManager
    {
    public override bool CheckAccess(OperationContext operationContext)
    {
    return true;
    }

    protected override ReadOnlyCollection<IAuthorizationPolicy> GetAuthorizationPolicies(OperationContext operationContext)
    {
    return new ReadOnlyCollection<IAuthorizationPolicy>(new IAuthorizationPolicy[] { new
    AuthorizationDomainPolicy
    () });
    }
    protected override bool CheckAccessCore(OperationContext operationContext)
    {
    return true;
    }
    }

    public class AuthorizationDomainPolicy : IAuthorizationPolicy
    {
    private string _id = Guid.NewGuid().ToString();
    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
    Object primaryIdentity;
    if (!evaluationContext.Properties.TryGetValue("PrimaryIdentity", out primaryIdentity))
    return false;

    evaluationContext.Properties[
    "Principal"] = new GenericPrincipal(new GenericIdentity(((IIdentity)primaryIdentity).Name), new string[] { "admin" });
    return true;
    }
    public ClaimSet Issuer
    {
    get { return ClaimSet.Anonymous; }
    }
    public string Id
    {
    get { return _id; }}
    }

    --------------------------- Server Code -------------------------------------------------------

    NetTcpBinding binding = new NetTcpBinding(SecurityMode.Message);
    binding.Security.Message.ClientCredentialType =
    MessageCredentialType.UserName;
    binding.Security.Transport.ProtectionLevel = System.Net.Security.
    ProtectionLevel.EncryptAndSign;

    Uri[] uriBase = new Uri[] { new Uri("net.tcp://localhost:9090/Hello") };
    ServiceHost host = new ServiceHost(typeof(Hello), uriBase);
    host.AddServiceEndpoint(
    typeof(IHello), binding, "");

    host.Authorization.PrincipalPermissionMode = System.ServiceModel.Description.
    PrincipalPermissionMode.Custom;

    host.Authorization.ServiceAuthorizationManager =
    new AuthorizationDomain();

    host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator =
    new MyUserNamePasswordValidator();

    host.Open();

    Exception : The service certificate is not provided. Please specify a service certificate in ServiceCredentials.

    Do I need to specify a service certificate for "UserName" authentication


  • J Walker

    You don't have to implement ServiceAuthorizationManager in order to add IAuthorizationPolicy to the processing. You can use ServiceHost.Authorization.ExternalAuthorizationPolicies to add custom implementation of IAuthorizationPolicy that will be used every time a service is called.

    Please note that February CTP is the first CTP based on our RC0 code base. This means that if your code does not work in February CTP it will likely not work on any other subsequent releases including RTM.

    The issue that you are probably hitting is that in your IAuthorizationPolicy implementation you are looking for "PrimaryIdentity" key inside the EvaluationContext.Propeties bag. We no longer use this key, you need to look for "Identities" key instead. The value type, if present, is IList<IIdentity> because there might be multiple identities after the message authentication step.

    To answer your last question: yes you need a service certificate for username authentication. The service certificate is used to provide confidentiality of the message and to authenticate the service to the client.

    Cheers,

    --Jan


  • Paulovsky

    Hi Alexnaldo,

    our claims framework is built around IAuthorizationPolicy class. When a message comes we take all security tokens present in the message, deserialize them into a CLR classes and then we use SecurityTokenAuthenticator associated with a given token type to validate the token information. The SecurityTokenAuthenticator returns a collection of IAuthorizationPolicies that represent information contained in the token.

    The IAuthorizationPolicy collection from all incoming security tokens is used by SecurityAuthorizationManager to generate a set of ClaimSet instances that is stored in the ServiceSecurityContext.AuthorizationContext. You can add your own IAuthorizationPolicy implementations into the processing by using ExternalAuthorizationPolicies property. Such implementation will be automatically added to collection of IAuthorizationPolicy instances generated from message tokens.

    During the IAuthorizationPolicy evaluation your implementation can take the ClaimSet that were added to the EvaluationContext by other IAuthorizationPolicy implementation and transform the information to the canonical ClaimSet that your application/authorization manager uses to perform access control and other claims-related activities.

    At the end of IAuthorizationPolicy evaluation the EvaluationContext contains set of ClaimSet instances and a property bag. The properties in the property bag are used to generate auxiliary instances like Thread.CurrentPrincipal or ServiceSecurityContext.PrimaryIdentity to integrated with existing .NET and ASP.NET infrastructure (PrincipalPermission, ASP.NET MembershipProvider and RoleProvider). The set of ClaimSets contained in the ServiceSecurityContext.AuthorizationContext is considered to be the primary API for WCF-based applications.

    The best place to cache the user information is your implementation of IAuthorizationPolicy. Alternatively, you can create your implementation of ServiceAuthorizationManager and cache the information there. We call both IAuhorizationPolicy.Evaluate and ServiceAuthorizationManager.CheckAccessCore every time a request is received for the service and is successfully authenticated.

    Does this answer you question

    Cheers,

    --Jan


  • GoodMorningSky

    I need it because 'PrincipalPermission' only work with 'roles' and
    I have one custom 'PrincipalPermission' for permission-based security, so I can do in my services :

    public class AccountService: IAccountService
    {

    [MyPrincipalPermission(Permission="can_trasfer")]
    public void TransferAccount(...);

    [MyPrincipalPermission(Role="admin")]
    public void DeleteAccount(...);

    }

    As you can see, I need to create 'MyPrincipal' by hand with correct 'identity','roles' and new 'permissions' properties.

    http://windowscommunication.net/ControlGallery/default.aspx Category=3&tabindex=2


    The above example is great, but doesn't work with 'Feb CTP'.

    Thanks,
    Alexnaldo Santos


  • Razim

    Thanks,

    I need to create my IPrincipal implementation and set it on Thread.Principal.

    What is the better place to do it

    See the links above(last post) that he do it.

    Cheers,
    Alexnaldo Santos


  • LiquidBlaze

    Hi Jan,

    I did it and now work fine....thanks..

    Why we need to do it

    evaluationContext.AddToTarget(this, new DefaultClaimSet(
    ClaimSet.System,
    new Claim("mynamepsace",
    "AppUser",
    Rights.PossessProperty)));

    As I can see, you create a 'IPrincipal' for every call.

    I would like to know how to cache the authenticated user using a 'unique id' for each user. Are you using a 'token' solution

    See this post :
    http://forums.microsoft.com/MSDN/ShowPost.aspx PostID=293820&SiteID=1

    Thanks,
    Alexnaldo Santos


  • Will K

    Alexanldo,

    have you tried to put the IAuthorizationPolicy implementation to the ServiceHost.Authorization.ExternalAuthorizationPolicies collection instead of implementing custom ServiceAuthorizationManager I beleive that the ServiceAuthorizationManager implementation that you have shown in one of the posts above cannot work with Feb CTP and later CTPs.

    If yes and it still fails, can you describe how it fails, what exception is thrown and where

    Thanks,

    --Jan


  • DLJOHNST

    Frank ,

    Please, can you create a 'sample' and upload to 'Sample Gallery'

    This new 'entry' have the same problem :

    http://forums.microsoft.com/MSDN/ShowPost.aspx PostID=293820&SiteID=1

    Thanks,
    Alexnaldo Santos


  • shrooman

    Hi Alexnaldo,

    If you want to customize the ServiceAuthorizationManager, it is enough to override the CheckAccessCore() method. You should return true if you want to allow the caller access to the service and false if you don't want to allow access.

    There is no need to create your own IAuthorizationPolicy implementation unless you want to modify the claims that we generate from the security tokens in the incoming message.

    It is not clear from the sample what you are trying to accomplish though. Do you want to perform access checks for you service If not, the ServiceAuthorizationManager is not a good place to extend, because this extensibility point is meant just for the authorization related activities.

    If you want to change the logic that authenticates tokens in the request message, you should create your custom SecurityTokenAuthenticator implementation. Look at the TokenAuthenticator sample in the Extensibility/Security directory.

    I hope this will help.

    Cheers,

    --Jan


  • ColSchmoll

    Hi Jan,

    Yes! Thanks.

    Cheers,
    Alexnaldo Santos


  • Custom ServiceAuthorizationManager