programming4us
programming4us
SECURITY

Programming WCF Services : Security - Intranet Application Scenario (part 6) - Authorization

10/15/2013 7:46:06 PM

7. Authorization

While authentication deals with verifying that the client is indeed who the client claims to be, most applications also need to verify that the client (or more precisely, the identity it presents) has permission to perform the operation. Since it would be impractical to program access permissions for each individual identity, it is better to grant permissions to the roles clients play in the application domain. A role is a symbolic category of identities that share the same security privileges. When you assign a role to an application resource, you are granting access to that resource to anyone who is a member of that role. Discovering the roles clients play in your business domain is part of your application-requirements analysis and design, just like factoring services and interfaces. By interacting with roles instead of particular identities, you isolate your application from changes made in real life, such as adding new users, moving existing users between positions, promoting users, or users leaving their jobs. .NET allows you to apply role-based security both declaratively and programmatically, if the need to verify role membership is based on a dynamic decision.

7.1. The security principal

For security purposes, it is convenient to lump together an identity and the information about its role membership. This representation is called the security principal.

The principal in .NET is any object that implements the IPrincipal interface, defined in the System.Security.Principal namespace:

public interface IPrincipal
{
IIdentity Identity
{get;}
bool IsInRole(string role);
}

The IsInRole() method simply returns true if the identity associated with this principal is a member of the specified role, and false otherwise. The Identity read-only property provides access to read-only information about the identity, in the form of an object implementing the IIdentity interface. Out of the box, .NET offers several implementations of IPrincipal. GenericPrincipal is a general-purpose principal that has to be preconfigured with the role information. It is typically used when no authorization is required, in which case GenericPrincipal wraps a blank identity. The WindowsPrincipal class looks up role membership information inside the Windows NT groups.

Every .NET thread has a principal object associated with it, obtained via the CurrentPrincipal static property of the Thread class:

public sealed class Thread
{
public static IPrincipal CurrentPrincipal
{get;set;}
//More members
}

For example, here is how to discover the username as well as whether or not the caller was authenticated:

IPrincipal principal = Thread.CurrentPrincipal;
string userName = principal.Identity.Name;
bool isAuthenticated = principal.Identity.IsAuthenticated;

7.2. Selecting an authorization mode

As presented earlier, the ServiceHostBase class provides the Authorization property of the type ServiceAuthorizationBehavior. ServiceAuthorizationBehavior has the PrincipalPermissionMode property of the enum type PrincipalPermissionMode, defined as:

public enum PrincipalPermissionMode
{
None,
UseWindowsGroups,
UseAspNetRoles,
Custom
}
public sealed class ServiceAuthorizationBehavior : IServiceBehavior
{
public PrincipalPermissionMode PrincipalPermissionMode
{get;set;}
//More members
}

Before opening the host, you can use the PrincipalPermissionMode property to select the principal mode; that is, which type of principal to install to authorize the caller.

If PrincipalPermissionMode is set to PrincipalPermissionMode.None, principal-based authorization is impossible. After authenticating the caller (if authentication is required at all), WCF installs GenericPrincipal with a blank identity and attaches it to the thread that invokes the service operation. That principal will be available via Thread.CurrentPrincipal.

When PrincipalPermissionMode is set to PrincipalPermissionMode.UseWindowsGroups, WCF installs a WindowsPrincipal with an identity matching the provided credentials. If no Windows authentication took place (because the service did not require it), WCF will install a WindowsPrincipal with a blank identity.

PrincipalPermissionMode.UseWindowsGroups is the default value of the PrincipalPermissionMode property, so these two definitions are equivalent:

ServiceHost host1 = new ServiceHost(typeof(MyService));

ServiceHost host2 = new ServiceHost(typeof(MyService));
host2.Authorization.PrincipalPermissionMode =
PrincipalPermissionMode.UseWindowsGroups;


When using a config file, you need to reference a custom behavior section assigning the principal mode:

<services>
<service name = "MyService" behaviorConfiguration = "WindowsGroups">
...
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name = "WindowsGroups">
<serviceAuthorization principalPermissionMode = "UseWindowsGroups"/>
</behavior>
</serviceBehaviors>
</behaviors>


7.3. Declarative role-based security

You apply service-side declarative role-based security using the attribute PrincipalPermissionAttribute, defined in the System.Security.Permissions namespace:

public enum SecurityAction
{
Demand,
//More members
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class PrincipalPermissionAttribute : CodeAccessSecurityAttribute
{
public PrincipalPermissionAttribute(SecurityAction action);

public bool Authenticated
{get;set; }
public string Name
{get;set;}
public string Role
{get;set;}
//More members
}


The PrincipalPermission attribute lets you declare the required role membership. For the intranet scenario, when you specify a Windows NT group as a role, you don’t have to prefix the role name with your domain or machine name (if you wish to authorize against its roles). You can also explicitly specify another domain, if you have a trust relationship with it.

In Example 4, the declaration of the PrincipalPermission attribute grants access to MyMethod() only to callers whose identities belong to the Managers group.

Example 4. Declarative role-based security on the intranet
[ServiceContract]
interface IMyContract
{
[OperationContract]
void MyMethod();
}
class MyService : IMyContract
{
[PrincipalPermission(SecurityAction.Demand,Role = "Manager")]
public void MyMethod()
{...}
}

If the caller is not a member of that role, .NET throws an exception of type SecurityException.


Note:

When experimenting with Windows role-based security, you often add users to or remove users from user groups. Because Windows caches user-group information at login time, the changes you make are not reflected until the next login.


If multiple roles are allowed to access the method, you can apply the attribute multiple times:

[PrincipalPermission(SecurityAction.Demand,Role = "Manager")]
[PrincipalPermission(SecurityAction.Demand,Role = "Customer")]
public void MyMethod()
{...}

When multiple PrincipalPermission attributes are used, .NET verifies that the caller is a member of at least one of the demanded roles. If you want to verify that the caller is a member of both roles, you need to use programmatic role membership checks, discussed later.

While the PrincipalPermission attribute by its very definition can be applied on methods and classes, in a WCF service class you can apply it only on methods. The reason is that in WCF, unlike with normal classes, the service class constructor always executes under a GenericPrincipal with a blank identity, regardless of the authentication mechanisms used. As a result, the identity under which the constructor is running is unauthenticated and will always fail any kind of authorization attempt (even if the client is a member of the role and even when not using Windows NT groups):

//Will always fail
[PrincipalPermission(SecurityAction.Demand,Role = "...")]
class MyService : IMyContract
{...}


Warning:

Avoid sensitive work that requires authorization in the service constructor. With a per-call service, perform such work in the operations themselves, and with a sessionful service, provide a dedicated Initialize() operation where you can initialize the instance and authorize the callers.


By setting the Name property of the PrincipalPermission attribute, you can even insist on granting access only to a particular user:

[PrincipalPermission(SecurityAction.Demand,Name = "John")]

or to a particular user that is a member of a particular role:

[PrincipalPermission(SecurityAction.Demand,Name = "John",
Role = "Manager")]

These practices are inadvisable, however, because it is best to avoid hardcoding usernames.


Note:

Declarative role-based security hardcodes the role name. If your application looks up role names dynamically you have to use programmatic role verification, as presented next.


7.4. Programmatic role-based security

Sometimes you need to programmatically verify role membership. Usually, you need to do that when the decision as to whether to grant access depends both on role membership and on some other values known only at call time, such as parameter values, time of day, and location. Another case in which programmatic role membership verification is needed is when you’re dealing with localized user groups. To demonstrate the first category, imagine a banking service that lets clients transfer sums of money between two specified accounts. Only customers and tellers are allowed to call the TransferMoney() operation, with the following business rule: if the amount transferred is greater than 50,000, only tellers are allowed to do the transfer. Declarative role-based security can verify that the caller is either a teller or a customer, but it cannot enforce the additional business rule. For that, you need to use the IsInRole() method of IPrincipal, as shown in Example 5.

Example 5. Programmatic role-based security
[ServiceContract]
interface IBankAccounts
{
[OperationContract]
void TransferMoney(double sum,long sourceAccount,long destinationAccount);
}
static class AppRoles
{
public const string Customer = "Customer";
public const string Teller = "Teller";
}
class BankService : IBankAccounts
{

[PrincipalPermission(SecurityAction.Demand,Role = AppRoles.Customer)]
[PrincipalPermission(SecurityAction.Demand,Role = AppRoles.Teller)]
public void TransferMoney(double sum,long sourceAccount,long destinationAccount)
{
IPrincipal principal = Thread.CurrentPrincipal;
Debug.Assert(principal.Identity.IsAuthenticated);

bool isCustomer = principal.IsInRole(AppRoles.Customer);
bool isTeller = principal.IsInRole(AppRoles.Teller);

if(isCustomer && ! isTeller)
{
if(sum > 50000)
{
string message = "Caller does not have sufficient authority to" +
"transfer this sum";
throw new SecurityException(message);
}
}
DoTransfer(sum,sourceAccount,destinationAccount);
}
//Helper method
void DoTransfer(double sum,long sourceAccount,long destinationAccount)
{...}
}


Example 5 also demonstrates a number of other points. First, even though it uses programmatic role membership verification with the value of the sum argument, it still uses declarative role-based security as the first line of defense, allowing access only to clients who are members of the Customer or Teller roles. Second, you can programmatically assert that the caller is authenticated using the IsAuthenticated property of IIdentity. Finally, note the use of the AppRoles static class to encapsulate the actual string used for the role to avoid hardcoding the roles in multiple places.


Note:

There is a complete disconnect between role-based security and the actual principal type. When the PrincipalPermission attribute is asked to verify role membership, it simply gets hold of its thread’s current principal in the form of IPrincipal, and calls its IsInRole() method. This is also true of programmatic role membership verification that uses only IPrincipal, as shown in Example 5. The separation of the IPrincipal interface from its implementation is the key to providing other role-based security mechanisms besides Windows NT groups, as you will see in the other scenarios.
Other  
 
PS4 game trailer XBox One game trailer
WiiU game trailer 3ds game trailer
Top 10 Video Game
-   Unravel | Live Gameplay from E3 2015
-   Destiny: Xur, Agent of the Nine, Reef location and exotic items
-   Metal Gear Solid 5: The Phantom Pain | E3 2015 Stage Demo
-   OVERKILL's The Walking Dead | The VR Experience Trailer
-   Batman: Arkham Knight | NVIDIA GameWorks Gameplay Video
-   World of Warcraft: Warlords of Draenor | Patch 6.2 – Fury of Hellfire
-   Call of Duty: Black Ops III | Cyber Core Tutorial and Co-Op Playthrough
-   Back to Bed - E3 2015 Trailer
-   Whispering Willows - E3 2015 Trailer
-   Velocibox - E3 2015 Trailer
-   Anno 2025 - E3 2015 Gameplay Trailer
-   Anno 2025 - E3 2015 Intro Trailer
-   Awesome GTA V Sniper Chopper Kill
-   Awesome GTA V Parachute Video
-   GTA V Explosive Ammo Rounds with Bikini
Game of War | Kate Upton Commercial
programming4us
 
 
programming4us