MSDN Magazine - October 2008 - (Page 67) Writing a custom principal is straightforward. Simply implement the IPrincipal interface and add your own custom functionality. To integrate the principal with WCF, you have to set the PrincipalPermissionMode attribute in the ServiceAuthorization element to “custom” and provide an authorization policy that is responsible for creating the principal and giving it back to WCF. An authorization policy is simply a class that implements the System.IdentityModel.Policy.IAuthorizationPolicy interface. Implementations of this interface must employ a method called Evaluate, which gets called on every request. Here you can reach into the WCF service security context and set the custom principal. Figure 3 shows a custom principal as well as the authorization policy and its corresponding configuration entries. WCF will create the Access Denied fault message and send that back to the client. A return value of true will grant access to the service operation. You can find the unique identifier of the operation that the client tries to call in the WS-Addressing action header. This value can be obtained from IncomingMessageHeader.Action property on the operation context that is passed into CheckAccess. The format of the action value is: ServiceNamespace/ContractName/OperationName For example, you might see something like this: urn:msdnmag/ServiceContract/GetRoles Centralizing Authorization Logic So far you have seen how you can access the role information that is provided by the WCF security system from within your service operations. Sometimes, however, it is also useful to have centralized logic that can inspect every incoming request and make authorization decisions without having to spread that logic over all your service operations. Enter the service authorization manager. The service authorization manager is a class that derives from System.ServiceModel.ServiceAuthorizationManager. You can override the CheckAccessCore method to run custom authorization code for each request. In CheckAccess, you have access to the current security context as well as the incoming message, including the headers. When you return false from CheckAccess, Figure 3 A Custom Principal and Authorization Policy Principal class CustomPrincipal : IPrincipal { IIdentity _identity; string[] _roles; Cache _cache = HttpRuntime.Cache; public CustomPrincipal(IIdentity identity) { _identity = identity; } // helper method for easy access (without casting) public static CustomPrincipal Current { get { return Thread.CurrentPrincipal as CustomPrincipal; } } public IIdentity Identity { get { return _identity; } } // return all roles (custom property) public string[] Roles { get { EnsureRoles(); return _roles; } } // IPrincipal role check public bool IsInRole(string role) { EnsureRoles(); } return _roles.Contains(role); WCF passes in the complete request message as a ref parameter. This allows inspecting and even changing the message before it reaches the service operation (or gets bounced back because of a negative authorization outcome). Be aware that messages in WCF are always read-once. That means you first have to create a copy of the message before you inspect the body. This can be done by creating a message buffer using the following code (you need to be careful with large or streamed messages): MessageBuffer buffer = operationContext.RequestContext.RequestMessage.CreateBufferedCopy( int.MaxValue); Once you have created a copy of the message, you can use the standard APIs to get access to its content. In Figure 4, you can see a sample implementation of a service authorization manager that moves the authorization logic from the service operation to a central place. } // cache roles for subsequent requests protected virtual void EnsureRoles() { // caching logic omitted – see the sample download } Authorization Policy class AuthorizationPolicy : IAuthorizationPolicy { // called after the authentication stage public bool Evaluate(EvaluationContext evaluationContext, ref object state) { // get the authenticated client identity from the evaluation context IIdentity client = GetClientIdentity(evaluationContext); // set the custom principal evaluationContext.Properties[‘Principal’] = new CustomPrincipal(client); } } return true; // rest omitted Configuration msdnmagazine.com October 2008 67 http://www.msdnmagazine.com
For optimal viewing of this digital publication, please enable JavaScript and then refresh the page. If you would like to try to load the digital publication without using Flash Player detection, please click here.