Role-Based Authorization
Authorization is the
practice of managing access to protected resources based on the rights
of an individual consumer or a class of consumers. Authorization assumes
that authentication (the practice of validating a consumer’s identity)
has already occurred.
Role-Based Authorization
[MPP] is a pattern based on the association of one or more roles with a
user or consumer. Access to resources is made available to consumers
associated with an appropriate role. It is important to understand that a
role does not correlate directly to a group of consumers. A role is
simply a specific tag that is assigned to a consumer or group of
consumers when the service is deployed.
Role membership can be
assigned explicitly (as with Windows security groups) or implicitly
according to a set of characteristics possessed by role members. The
role itself is a service-specific entity that usually defines a set of
permissions.
Users and groups are
typically stored in a user directory. The directory can be specific to
the purpose, as with Active Directory, or it can be as generic as a SQL
Server database.
The
service itself defines roles specific to its needs. At runtime it binds
the roles to individual service consumers, users, or groups of
users/consumers and determines resource access based on these roles. Table 1 demonstrates how a resource access request might be resolved to a specific user.
Table 1. The Payroll Data resource is restricted to two roles: Managers
and HR Administrators. The Managers group is bound to the Executives and
Managers security groups. The HR Administrators role is explicitly
bound to two individual users who perform that role.
Resource | Role | Role Members |
---|
Payroll Data | Managers | Executives |
Managers |
HR Administrators | Ted |
Mary |
Authorization Roles in WCF
The .NET Framework
provides a built-in mechanism for authorizing access to resources using
roles. An authenticated user is represented to the service by a security
principal that is attached to the requesting thread. The security
principal contains the caller’s identity and the roles associated with
that user. As a result, the security principal provides the necessary
information for the service to perform authorization checks.
The security principal is any type that implements the IPrincipal interface. IPrincipal contains two members: Identity and IsInRole(). Identity is a read-only property that returns the IIdentity instance associated with the security principal, while IsInRole() validates the principal’s membership in a given role:
Example 1.
public interface IPrincipal { bool IsInRole(string role); IIdentity Identity { get; } }
|
The IIdentity
interface represents the identity of the security principal and
contains the user’s name and the associated authentication type. Once a
user is authenticated, IIdentity is created with the user’s name and one of a number of pre-defined authentication types.
.NET provides WindowsIdentity, FormsIdentity, PassportIdentity, X509Identity and GenericIdentity as built-in types. The security principal that contains the identity can be a WindowsPrincipal, GenericPrincipal or a RoleProviderPrincipal, depending upon how the authorization policy of the service is specified.
Example 2.
public interface IIdentity { string AuthenticationType { get; } bool IsAuthenticated { get; } string Name { get; } }
|
The security principal is associated with the execution when a service request is invoked. The IPrincipal type is determined by the authorization policy of the service while the IIdentity is appropriate for the security credential received by the service.
The following example illustrates a Windows Authentication binding for a service.
Example 3.
<bindings> <wsHttpBinding> <binding name="WindowsAuthBinding"> <security mode="Message"> <message clientCredentialType="Windows" /> </security> </binding> </wsHttpBinding> </bindings>
|
When
the security token is received from the consumer, it is authenticated
against the Windows domain, which is the default for the Windows
credential type. Once authenticated, IIdentity and IPrincipal are created and attached to the executing thread. Authorization takes place according to the value set by the PrincipalPermissionMode
attribute. The default authorization mode is Windows Groups, which
relies on the Windows domain as the source of authorization information.
User/consumer
credentials can take the form of a username and password, which may not
necessarily belong to the Windows domain. In this case, it is possible
to specify a custom authentication mode and authorization mode by
explicitly providing values for the PrincipalPermissionMode and userNamePasswordValidationMode.
In the next example, the service is configured to accept a
username/password combination to be authenticated against an ASP.NET
role provider (authorization is also performed against the provider).
Example 4.
<serviceBehaviors> <behavior name="externalServiceBehavior"> <serviceAuthorization PrincipalPermissionMode="UseAspNetRoles" /> <serviceCredentials> <userNameAuthentication userNamePasswordValidationMode= "MembershipProvider" /> </serviceCredentials> </behavior> </serviceBehaviors>
|
In this example, PrincipalPermissionMode is set to UseASPNetRoles.
This setting enforces the use of an ASP.NET role provider to enable
authorization for the service. Therefore, authorization is performed
against the role provider when a service request is made. The resultant
security principal would be a RoleProviderPrincipal containing a GenericIdentity to hold the client credentials.
Authorizing Operations with Roles
Activities can be authorized using either declarative or explicit means once the security principal is created. A PrincipalPermission
demand should be used in order to ensure consistency across multiple
types of security principals due to the existence of underlying
differences in the way credentials are validated.
An imperative authorization can be performed by explicitly creating and initializing a PrincipalPermission object and calling its Demand method:
Example 5.
public bool RaiseSalary(double amount) { PrincipalPermission permission = new PrincipalPermission(null, "HRAdmins"); Permission.Demand(); }
|
Likewise, a declarative demand can be used to decorate an operation enforcing a security check at runtime.
Example 6.
[PrincipalPermission (SecurityAction.Demand, Role="HRAdmins")] public bool RaiseSalary(double amount) { ... raise the salary ... }
|
In either example, the PrincipalPermission is used to validate the identity of the consumer and its right to perform the requested operation.