Programming4us
         
 
 
Programming

User-Level Security : Authorization and Impersonation (part 2) - Claims-Based Authorization

10/17/2010 6:01:06 PM

2. Claims-Based Authorization

Along with being able to be integrated into Windows authentication, WCF also supports a claims-based technique for authorization. Before going into the mechanics of claims-based authorization, a couple of moments defining the terminology are helpful.

A claim describes an individual right or action that is applicable to a specific resource. It is defined by a combination of three pieces of information: the type of claim, the right that is being claimed, and the resource to which the claim applies. As an example, an identity claim might consist of an identity claim type, a possess propertyusername resource. This claim might be used to represent the identity associated with the requester (as indicated by the username resource) in a WCF communication. It is not difficult to imagine that the client could make claims of other types to represent concepts such as public keys, e-mail addresses, roles, and so on. right, and a

A claim set (or ClaimSet) is an ordered list of the claims that come from a common issuer. For example, a number of claims can be made based on the use of an X.509 certificate. This includes the common name, the DNS name, the public key, and the e-mail address. Although each of these items is an individual claim, the fact that there is a common issuer for the claims means that the claims can be considered as a single group. This abstraction allows a group of claims to be applied or rejected en masse.

An authorization policy is an extensibility point by which new claim sets can be added to the current WCF calling context. There are two sources (in WCF) for authorization policies. First, these policies are created based on the security tokens supplied by the requester. The idea with these policies is to provide a common representation for tokens that are generated by heterogeneous sources. The tokens can come from X.509, Kerberos, or username/password combinations. The authorization policy permits a representation that is independent of the source for the data.

The second type of authorization policy comes from configuration details set by an administrator. The goal here is to add claims to the calling context for use with authorization by the service. The types of claims that fit into this category are ones related to local security policy and information. For example, perhaps a service-side database maps requesters to roles. This list of roles is a claim set that is not known to the client but is instead added to the request when it reaches the service.

As part of the authorization process, the service authorization manager is the final authority for deciding whether a particular requester should be granted or denied access to a service. The manager is called for each request that arrives at the service. It examines the operation that is being called, along with any available evidence about the caller (found in the AuthorizationContext object). The AuthorizationContext contains all the claim sets that are created based on the various authorization policies, so at the point at which the manager makes its decision, it has access to any claims regarding identity, public keys, e-mail addresses, and so on. After this information has been digested, the service authorization manager makes a go/no go decision for the incoming request.


Note: Although the claims-based authorization system is quite powerful, it is not a replacement for the common language runtime (CLR) authorization model that is built into the .NET Framework. Indeed, in many instances, the claims-based authorization mechanism will make changes to the principal that is available to the current thread by adding or removing roles to which the current user belongs.
2.1. Accessing the Claims

Now that you know the fundamental terminology, turn your focus to how the claims are used. One of the classes at the heart of claims-based authorization is the ServiceSecurityContext. Through this class, it is possible to gain access to the AuthorizationContext (which contains the claim sets), the authorization policies, and the identity claim.


Note: If no security is being applied by WCF, the ServiceSecurityContext object will be null. Even if the caller’s identity is anonymous, this does not guarantee that the ServiceSecurityContext will be instantiated and populated. However, an IsAnonymous property on the ServiceSecurityContext object can be used to determine whether an anonymous identity was used to build the security context object.

The ServiceSecurityContext is exposed through a static property on the OperationContext. You can retrieve the ServiceSecurityContext as shown here:

' VB
Dim securityContext As ServiceSecurityContext = _
OperationContext.Current.ServiceSecurityContext

// C#
ServiceSecurityContext securityContext =
OperationContext.Current.ServiceSecurityContext;

After the ServiceSecurityContext object is retrieved, you can examine it to find out the go/no go decision based on the claim sets. For example, the following code would check to see whether an e-mail address has been included in any of the claims.

' VB
Dim email As String = String.Empty

Dim claims as IEnumerable(Of Claim) = _
securityContext.AuthorizationContext.ClaimSets(0).FindClaims _
(ClaimTypes.Email, Rights.PossessProperty)

Dim c As Claim
For Each c in claims
email = TryCast(c.Resource, String)
Next

If String.IsNullOrEmpty(email) Then
Throw New SecurityException("Email address claim not found.")
End If

// C#
string email = String.Empty;

IEnumerable<Claim> claims =
securityContext.AuthorizationContext.ClaimSets[0].FindClaims
(ClaimTypes.Email,Rights.PossessProperty);

foreach (Claim c in claims)
{
email = c.Resource as string;
}
if (String.IsNullOrEmpty(email))
throw new SecurityException("Email address claim not found.");


The preceding code uses a couple of concepts that are worth consideration. First, notice that the claim sets associated with the request are exposed through the ClaimSets property in the AuthorizationContext object. This collection (the ClaimSets property) contains all the claims associated with the request. You can find the details about any of the claims generated either by the processing of the incoming security tokens or through administrator-configured settings in this collection.

The ClaimSet class has a method called FindClaims. This method retrieves an IEnumerable list of Claim objects that meet a specific set of criteria. The specified criteria include the type of claim and the rights of the claim. After the Claim objects have been retrieved, it becomes a simple matter to iterate over them to perform any authorization logic required.

Although you might get the impression from looking at the call to FindClaims that the type and rights are enumerated values, this is incorrect. Instead, these values are strings that represent a uniform resource indicator (URI) for the claim type and right. There is a ClaimTypes class and a Rights class that have a number of static properties containing URIs for common types and rights. Table 1 and Table 2 list the types and rights in these classes.

Table 1. Properties on the ClaimTypes Class Representing Claim URIs
NameDescription
AnonymousThe anonymous user claim.
AuthenticationIndicates whether an identity is authenticated.
AuthorizationDecisionSpecifies the authorization decision on an entity.
CountryIndicates the country region in which an entity resides.
DateOfBirthSpecifies the entity’s date of birth.
DenyOnlySidIndicates a deny-only security identifier (SID) for an entity.
DnsSpecifies the DNS name associated with the computer name or with the alternative name of either the subject or issuer of an X.509 certificate.
EmailIndicates the e-mail address of an entity.
GenderSpecifies the gender of an entity.
GivenNameRepresents the given name of an entity. This is typically the first name of the person represented by the entity.
HashSpecifies a hash value for the entity.
HomePhoneIndicates the home phone number of an entity.
LocalitySpecifies the locale in which an entity resides.
MobilePhoneIndicates the mobile phone number of an entity.
NameSpecifies the name of an entity.
NameIdentifierSpecifies an alternative name for an entity.
OtherPhoneIndicates the alternative phone number of an entity.
PostalCodeGets the URI for a claim that specifies the postal code of an entity.
PPIDGets the URI for a claim that specifies the private personal identifier (PPI) of an entity.
RsaGets the URI for a claim that specifies an RSA key.
SidGets the URI for a claim that specifies an SID.
SpnGets the URI for a claim that specifies a service principal name (SPN) claim.
StateOrProvinceGets the URI for a claim that specifies the state or province in which an entity resides.
StreetAddressGets the URI for a claim that specifies the street address of an entity.
SurnameGets the URI for a claim that specifies the surname of an entity. This would typically be the last name of a person represented by the entity.
SystemGets the URI for a claim that identifies the system entity.
ThumbprintGets the URI for a claim that specifies a thumbprint.
UpnGets the URI for a claim that specifies a user principal name (UPN).
UriGets the URI for a claim that specifies a URI.
WebpageGets the URI for a claim that specifies the Web page of an entity.
X509DistinguishedNameGets the string that contains the URI for a distinguished name claim of an X.509 certificate.

Table 2. Properties on the Rights Class Representing Right URIs
NameDescription
IdentityGets a string that specifies that the right represents an identity
PossessPropertyGets a string that specifies that the right represents a property that the entity associated with a claim possesses

The code from the previous example (which determined whether any claim represented an email address) is intended to be included in the logic for a service operation. However, that is not necessarily the best place to be performing this type of logic. In many cases, the desire to reuse authorization logic or to decouple the logic from the operations would lead to a more independent solution. In this case, the logic would be a custom authorization policy.

To implement a custom authorization policy, start by creating a class that implements the IAuthorizationPolicy interface. The interface itself is fairly straightforward. There are two properties: Id and Issuer. The Id property is a unique identifier for the authorization component (which, in this case, is the instance of the custom policy class). The Issuer property is a ClaimSet that represents the entity that issued this policy. In both cases, these are read-only properties. From an implementation perspective, this means that the backing values for these properties should be set in the constructor and returned in the property Get function. This is demonstrated in the following code. Please be aware that this example is not, by itself, a full implementation of the CustomPolicy class. Specifically, the Evaluate method that is part of the IAuthorizationPolicy interface is covered later in this section.

' VB
Public Class CustomPolicy
Implements IAuthorizationPolicy

Private _id As String
Private _issuer As ClaimSet

Public Sub New()
_id = Guid.NewGuid().ToString()
_issuer = ClaimSet.System
End Sub

Public ReadOnly Property Id() As String _
Implements IAuthorizationPolicy.Id
Get
Return _id
End Get
End Property

Public ReadOnly Property Issuer() As ClaimSet _
Implements IAuthorizationPolicy.Issuer
Get
Return _issuer
End Get
End Property

End Class

// C#
public class CustomPolicy : IAuthorizationPolicy
{
private string id;
private ClaimSet issuer;

public CustomPolicy()
{
id = Guid.NewGuid().ToString();
issuer = ClaimSet.System;
}

public string Id
{
get { return id; }
}

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


In the constructor, the value of the Issuer property is set to the ClaimSet.System value. This value is used if the current application is the issuer of the claim. Technically, it indicates an application-trusted issuer without needing to provide additional details about the issuer. However, conventionally, it is used when the current application (or something that has been configured within the current application’s configuration file) is issuing the claim.

The only method that appears in the IAuthorizationPolicy interface is called Evaluate. This is where the majority of the work associated with the custom authorization policy takes place. The signature of the method includes an EvaluationContext and a state object.

' VB
Public Function Evaluate(ByVal context As EvaluationContext, _
ByRef state As Object) As Boolean _
Implements IAuthorizationPolicy.Evaluate

// C#
public bool Evaluate(EvaluationContext context, ref object state)

The EvaluationContext object represents the results of an authorization policy doing its work. If claims are generated as part of an authorization policy, these claims are added to the evaluation context. The state object is simply an object that is passed into every invocation of the Evaluate method for a particular authorization policy. The actual method signature marks the state object as being passed by reference. This means that that method can create a new object and assign it to the state parameter. In practice, this parameter is frequently used as a cache for previously created claims or to ensure that claims are added only once.

After the authorization policy has been created, it must be associated with the WCF authorization process. You can do this either imperatively or declaratively. When you do so imperatively, there is a little more work than normal. Start by creating a List of IAuthorizationPolicy objects. After all the policies have been added to the list, a read-only version of the list is assigned to the ExternalAuthorizationPolicies property on the Authorization property for the service host. The following code demonstrates this technique:

' VB
Dim policies As List(Of IAuthorizationPolicy) = _
New List(Of IAuthorizationPolicy)()
policies.Add(New CustomPolicy())
Dim host As New ServiceHost(GetType(TestService))
host.Authorization.ExternalAuthorizationPolicies = _
policies.AsReadOnly()

// C#
List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>();
policies.Add(new CustomPolicy());
ServiceHost host = new ServiceHost(typeof(TestService));
host.Authorization.ExternalAuthorizationPolicies =
policies.AsReadOnly();

The declarative technique is a little easier. You can place an authorizationPolicies element within the serviceAuthorization element for a service’s behavior. The following segment from a configuration file demonstrates adding the CustomPolicy authorization policy to the service.

<behavior name="DemoBehavior">
<serviceAuthorization>
<authorizationPolicies>
<add policyType="DemoLibrary.CustomPolicy" />
</authorizationPolicies>
</serviceAuthorization>
</behavior>
Other -----------------
- Publisher Certificates
- Using LINQ To SQL
- Service Management API (part 2) - Making API Requests
- Service Management API (part 1)
- Windows Services : A Service Control Shell
- ASP.NET Applications and the Web Server
- Internet Information Services (IIS)
- Managing Websites with IIS Manager (part 7) - Confidentiality with SSL and Certificates
- Managing Websites with IIS Manager (part 6) - The Machine Key and Windows Authentication
- Managing Websites with IIS Manager (part 5) - The Default Page and Custom Error Pages
- Managing Websites with IIS Manager (part 4) - Configuration
- Managing Websites with IIS Manager (part 3) - The ASP.NET Account
- Managing Websites with IIS Manager (part 2) - Understanding Application Pools
- Managing Websites with IIS Manager (part 1) - Creating a Virtual Directory
- Deploying ASP.NET 4 Applications with Visual Studio (part 2) - Copying a Website and Publishing a Website
- Deploying ASP.NET 4 Applications with Visual Studio (part 1) - Creating a Virtual Directory for a New Project
- Writing Your First Service in Visual Basic 2008 (part 3)
- Writing Your First Service in Visual Basic 2008 (part 2)
- Writing Your First Service in Visual Basic 2008 (part 1)
 
 
Most View
- Windows 7: Troubleshooting Tools (part 2) - Running the Memory Diagnostics Tool
- SharePoint Server 2010 Business Intelligence Platform (part 6) - Reporting Services
- An OLAP Requirements Example: CompSales International (part 14) - Data Mining
- Troubleshooting and Optimizing SQL Server 2005 : Server Configuration Maintenance
- Windows Phone 7 : Browsing the Web - Searching the Web
- Windows Server 2008 : Perform a Full Server Recovery of a Domain Controller by Using the Command Line
- Performing Administrative Tasks Using Central Administration (part 27) - Search
- Mobile Game Networking Essentials : Network Programming and J2ME
- Implementing Windows Vista’s Internet Security and Privacy Features (part 5) - Encoding Addresses to Prevent IDN Spoofing
- Developing for Windows Phone and Xbox Live : Multiplayer Games (part 3) - Creating a Network Session
Top 10
- Implementing Edge Services for an Exchange Server 2007 Environment : Utilizing the Basic Sender and Recipient Connection Filters (part 3) - Configuring Recipient Filtering
- Implementing Edge Services for an Exchange Server 2007 Environment : Utilizing the Basic Sender and Recipient Connection Filters (part 2)
- Implementing Edge Services for an Exchange Server 2007 Environment : Utilizing the Basic Sender and Recipient Connection Filters (part 1)
- Implementing Edge Services for an Exchange Server 2007 Environment : Installing and Configuring the Edge Transport Server Components
- What's New in SharePoint 2013 (part 7) - BCS
- What's New in SharePoint 2013 (part 6) - SEARCH
- What's New in SharePoint 2013 (part 6) - WEB CONTENT MANAGEMENT
- What's New in SharePoint 2013 (part 5) - ENTERPRISE CONTENT MANAGEMENT
- What's New in SharePoint 2013 (part 4) - WORKFLOWS
- What's New in SharePoint 2013 (part 3) - REMOTE EVENTS