3

how to use [PrincipalPermission(SecurityAction.Demand, Role = "Administrators")] attribute on a class?

I am looking for some way to restrict the access on my object i.e if some object is being accessed in a service method and if the user has rights for accessing the service method but does not have rights accessing the object an exception should be thrown

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
taher chhabrawala
  • 4,110
  • 4
  • 35
  • 51
  • possible duplicate of [How to secure data over WCF (dynamic security)](http://stackoverflow.com/questions/4714482/how-to-secure-data-over-wcf-dynamic-security) – Ladislav Mrnka Feb 19 '11 at 13:53
  • any guidance on how to build such a system... – taher chhabrawala Feb 19 '11 at 14:18
  • you can only restrict service methods from being called, based on roles. You cannot restrict access to data the same way. – marc_s Feb 19 '11 at 16:32
  • i want to mimic the windows operating system folder structure..each user has different access right for each folder and different actions on that folder can be performed by different users according to their roles.. – taher chhabrawala Feb 21 '11 at 10:00
  • The restriction you need is possible. See my answer. –  Feb 27 '11 at 22:40

2 Answers2

6

PrincipalPermission attribute can adorn method or class. Therefore it is possible to restrict access to an instance of an object. Several things need to be done:

  1. Configure selected service and client binding to use security. Specify Windows as client credential type.
  2. Configure service to use Windows groups for authorization.
  3. Adorn class that will contain confidential information with PrincipalPermission attribute.

If singleton instance needs to be passed to ServiceHost constructor, do following:

  1. Create service singleton instance. Thread.CurrentPrincipal must have permissions necessary to access the confidential object.
  2. Create ServiceHost instance by passing service singleton instance. Property InstanceContextMode of ServiceBehavior attribute must be set to InstanceContextMode.Single.

Otherwise:

  1. Create ServiceHost instance by passing the service type.

Optionally, adorn the service method with FaultContract attribute and throw FaultException from it in order to avoid faulting the client channel.

Here is an example:

Service configuration file:

<system.serviceModel>
    <services>
        <service name="Server.Service" behaviorConfiguration="Authorization">
            <endpoint address=""
                      binding="netTcpBinding" bindingConfiguration="TCP"
                      contract="Common.IService" />
            <host>
                <baseAddresses>
                    <add baseAddress="net.tcp://localhost:13031/Service"/>
                </baseAddresses>
            </host>
        </service>
    </services>
    <bindings>
        <netTcpBinding>
            <binding name="TCP" openTimeout="00:30:00" closeTimeout="00:00:10" maxReceivedMessageSize="2147483647">
                <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
                <security mode="Message">
                    <message clientCredentialType="Windows" />
                </security>
            </binding>
        </netTcpBinding>
    </bindings>
    <behaviors>
        <serviceBehaviors>
            <behavior name="Authorization">
                <serviceAuthorization principalPermissionMode="UseWindowsGroups" />
            </behavior>
        </serviceBehaviors>
    </behaviors>
</system.serviceModel>

Client configuration file:

<system.serviceModel>
    <client>
        <endpoint name="NetTcpBinding_IService"
                  address="net.tcp://localhost:13031/Service"
                  binding="netTcpBinding" bindingConfiguration="TCP"
                  contract="Common.IService" />
    </client>
    <bindings>
        <netTcpBinding>
            <binding name="TCP" openTimeout="00:30:00" closeTimeout="00:00:10" sendTimeout="00:30:00" receiveTimeout="00:30:00" maxReceivedMessageSize="2147483647">
                <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
                <security mode="Message">
                    <message clientCredentialType="Windows" />
                </security>
            </binding>
        </netTcpBinding>
    </bindings>
</system.serviceModel>

Confidential information class:

[PrincipalPermission(SecurityAction.Demand, Role = "Administrators" ) ]
public class ContactInfo
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public ContactInfo()
    {
        FirstName = "John";
        LastName = "Doe";
    }
    public override string ToString()
    {
        return string.Format( "{0} {1}", FirstName, LastName );
    }
}

Service contract and its implementation:

[ServiceContract]
public interface IService
{
    [OperationContract]
    [FaultContract( typeof( string ) )]
    string GetName( int id );
}

[ServiceBehavior]
// Use following if singleton instance needs to be passed to `ServiceHost` constructor
//[ServiceBehavior( InstanceContextMode = InstanceContextMode.Single )]
public class Service : IService
{
    private Dictionary<int, ContactInfo> Contacts { get; set; }
    public Service()
    {
        Contacts = new Dictionary<int, ContactInfo>();
        IPrincipal originalPrincipal = Thread.CurrentPrincipal;
        try
        {
            Thread.CurrentPrincipal = new WindowsPrincipal( WindowsIdentity.GetCurrent() );
            Contacts.Add( 1, new ContactInfo() );
        }
        finally
        {
            Thread.CurrentPrincipal = originalPrincipal;
        }
    }
    public string GetName( int id )
    {
        if ( Contacts.Count < id )
            return null;
        try
        {
            return Contacts[ id ].ToString();
        }
        catch ( Exception ex )
        {
            throw new FaultException<string>( ex.Message );
        }
    }
}
  • ..what is the relation on instance context mode with the principal permission attribute..will there be any problem if i use per call mode.. – taher chhabrawala Feb 28 '11 at 05:16
  • @taher chhabrawala: `PerCall` instance context mode can be used as well. It's a leftover of my code trying to avoid multiple service instancing (in order to pass singleton object to `ServiceHost` constructor, the service implementation must be adorned with `ServiceBehavior` attribute having the `InstanceContextMode` set to `InstanceContextMode.Single`). I will edit the answer in a minute. –  Feb 28 '11 at 09:48
  • @taher chhabrawala: Does this work for you? Or you need something more like returning the whole confidential object instance? –  Feb 28 '11 at 14:40
  • @Rest...this worked for me...actually i will have to modify this a bit...but it surely pointed me in the right direction...thanks..i am marking yours as answer :) – taher chhabrawala Mar 01 '11 at 06:07
1

If you are familiar with .NET permission coding (either imperative or declarative), the pattern is exactly the same. In the declarative form, the PrincipalPermissionAttribute is applied to the method in the class that implements the service’s contract:

[PrincipalPermission(SecurityAction.Demand, Role = "Updaters")]
public bool Update()
{
return true;
}

In this example, the current principal is checked to see whether it belongs to a role called Updaters. In the actual implementation of the attribute, the IsInRole method on the principal is called.

For imperative determination of the PrincipalPermissionAttribute, an instance of the PrincipalPermission class is created. The constructor for PrincipalPermission takes the username and role as a parameter. When instantiated, the Demand method can be called to determine whether the current principal has the necessary permissions. The following code provides an example:

PrincipalPermission p = new PrincipalPermission(null, "Updaters");
p.Demand();

the configuration should look like this:

<behaviors>
  <serviceBehaviors>
    <behavior>
      ...
      <serviceAuthorization principalPermissionMode="UseWindowsGroups" />
    </behavior>
  </serviceBehaviors>
</behaviors>

for a working sample please look at: Authorizing Access to Service Operations

Nima M
  • 718
  • 10
  • 18