File: System\ServiceModel\Security\IdentityModelServiceAuthorizationManager.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IdentityModel;
using System.IdentityModel.Claims;
using System.IdentityModel.Policy;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Diagnostics;
using System.Xml;
using SysAuthorizationContext = System.IdentityModel.Policy.AuthorizationContext;
 
namespace System.ServiceModel.Security
{
    /// <summary>
    /// Custom ServiceAuthorizationManager implementation. This class substitues the WCF 
    /// generated IAuthorizationPolicies with 
    /// <see cref="System.IdentityModel.Tokens.AuthorizationPolicy"/>. These
    /// policies do not participate in the EvaluationContext and hence will render an 
    /// empty WCF AuthorizationConext. Once this AuthorizationManager is substitued to
    /// a ServiceHost, only <see cref="System.Security.Claims.ClaimsPrincipal"/>
    /// will be available for Authorization decisions.
    /// </summary>
    class IdentityModelServiceAuthorizationManager : ServiceAuthorizationManager
    {
        /// <summary>
        /// Authorization policy for anonymous authentication.
        /// </summary>
        protected static readonly ReadOnlyCollection<IAuthorizationPolicy> AnonymousAuthorizationPolicy
            = new ReadOnlyCollection<IAuthorizationPolicy>(
                new List<IAuthorizationPolicy>() { new AuthorizationPolicy(new ClaimsIdentity()) });
 
        /// <summary>
        /// Override of the base class method. Substitues WCF IAuthorizationPolicy with
        /// <see cref="System.IdentityModel.Tokens.AuthorizationPolicy"/>.
        /// </summary>
        /// <param name="operationContext">Current OperationContext that contains all the IAuthorizationPolicies.</param>
        /// <returns>Read-Only collection of <see cref="IAuthorizationPolicy"/> </returns>
        protected override ReadOnlyCollection<IAuthorizationPolicy> GetAuthorizationPolicies(OperationContext operationContext)
        {
            //
            // Make sure we always return at least one claims identity, if there are no auth policies
            // that contain any identities, then return an anonymous identity wrapped in an authorization policy.
            //
            // If we do not, then Thread.CurrentPrincipal may end up being null inside service operations after the
            // authorization polices are evaluated since ServiceCredentials.ConfigureServiceHost will
            // turn the PrincipalPermissionMode knob to Custom.
            //
 
            ReadOnlyCollection<IAuthorizationPolicy> baseAuthorizationPolicies = base.GetAuthorizationPolicies(operationContext);
            if (baseAuthorizationPolicies == null)
            {
                return AnonymousAuthorizationPolicy;
            }
            else
            {
                ServiceCredentials sc = GetServiceCredentials();
                AuthorizationPolicy transformedPolicy = TransformAuthorizationPolicies(baseAuthorizationPolicies,
                                                                                        sc.IdentityConfiguration.SecurityTokenHandlers,
                                                                                        true);
                if (transformedPolicy == null || transformedPolicy.IdentityCollection.Count == 0)
                {
                    return AnonymousAuthorizationPolicy;
                }
                return (new List<IAuthorizationPolicy>() { transformedPolicy }).AsReadOnly();
            }
        }
 
        internal static AuthorizationPolicy TransformAuthorizationPolicies(
            ReadOnlyCollection<IAuthorizationPolicy> baseAuthorizationPolicies,
            SecurityTokenHandlerCollection securityTokenHandlerCollection,
            bool includeTransportTokens)
        {
            List<ClaimsIdentity> identities = new List<ClaimsIdentity>();
            List<IAuthorizationPolicy> uncheckedAuthorizationPolicies = new List<IAuthorizationPolicy>();
 
            //
            // STEP 1: Filter out the IAuthorizationPolicy that WCF generated. These
            //         are generated as IDFx does not have a proper SecurityTokenHandler
            //         to handle these. For example, SSPI at message layer and all token
            //         types at the Transport layer.
            //
            foreach (IAuthorizationPolicy authPolicy in baseAuthorizationPolicies)
            {
                if ((authPolicy is SctAuthorizationPolicy) ||
                    (authPolicy is EndpointAuthorizationPolicy))
                {
                    //
                    // We ignore the SctAuthorizationPolicy if any found as they were created
                    // as wrapper policies to hold the primary identity claim during a token renewal path.
                    // WCF would otherwise fault thinking the token issuance and renewal identities are 
                    // different. This policy should be treated as a dummy policy and thereby should not be transformed.
                    //
                    // We ignore EndpointAuthorizationPolicy as well. This policy is used only to carry
                    // the endpoint Identity and there is no useful claims that this policy contributes.
                    //
                    continue;
                }
 
                AuthorizationPolicy idfxAuthPolicy = authPolicy as AuthorizationPolicy;
 
                if (idfxAuthPolicy != null)
                {
                    // Identities obtained from the Tokens in the message layer would 
                    identities.AddRange(idfxAuthPolicy.IdentityCollection);
                }
                else
                {
                    uncheckedAuthorizationPolicies.Add(authPolicy);
                }
            }
 
            //
            // STEP 2: Generate IDFx claims from the transport token
            //
            if (includeTransportTokens && (OperationContext.Current != null) &&
                (OperationContext.Current.IncomingMessageProperties != null) &&
                (OperationContext.Current.IncomingMessageProperties.Security != null) &&
                (OperationContext.Current.IncomingMessageProperties.Security.TransportToken != null))
            {
                SecurityToken transportToken =
                    OperationContext.Current.IncomingMessageProperties.Security.TransportToken.SecurityToken;
 
                ReadOnlyCollection<IAuthorizationPolicy> policyCollection =
                    OperationContext.Current.IncomingMessageProperties.Security.TransportToken.SecurityTokenPolicies;
                bool isWcfAuthPolicy = true;
 
                foreach (IAuthorizationPolicy policy in policyCollection)
                {
                    //
                    // Iterate over each of the policies in the policyCollection to make sure
                    // we don't have an idfx policy, if we do we will not consider this as
                    // a wcf auth policy: Such a case will be hit for the SslStreamSecurityBinding over net tcp
                    //
 
                    if (policy is AuthorizationPolicy)
                    {
                        isWcfAuthPolicy = false;
                        break;
                    }
                }
 
                if (isWcfAuthPolicy)
                {
                    ReadOnlyCollection<ClaimsIdentity> tranportTokenIdentities = GetTransportTokenIdentities(transportToken);
                    identities.AddRange(tranportTokenIdentities);
 
                    //
                    // NOTE: In the below code, we are trying to identify the IAuthorizationPolicy that WCF
                    // created for the Transport token and eliminate it. This assumes that any client Security  
                    // Token that came in the Security header would have been validated by the SecurityTokenHandler 
                    // and hence would have created a IDFx AuthorizationPolicy. 
                    // For example, if X.509 Certificate was used to authenticate the client at the transport layer 
                    // and then again at the Message security layer we depend on our TokenHandlers to have been in
                    // place to validate the X.509 Certificate at the message layer. This would clearly distinguish
                    // which policy was created for the Transport token by WCF. 
                    //
                    EliminateTransportTokenPolicy(transportToken, tranportTokenIdentities, uncheckedAuthorizationPolicies);
                }
            }
 
            //
            // STEP 3: Process any uncheckedAuthorizationPolicies here. Convert these to IDFx 
            //         Claims.
            //
            if (uncheckedAuthorizationPolicies.Count > 0)
            {
                identities.AddRange(ConvertToIDFxIdentities(uncheckedAuthorizationPolicies, securityTokenHandlerCollection));
            }
 
            //
            // STEP 4: Create an AuthorizationPolicy with all the ClaimsIdentities.
            //
            AuthorizationPolicy idfxAuthorizationPolicy = null;
            if (identities.Count == 0)
            {
                //
                // No IDFx ClaimsIdentity was found. Return AnonymousIdentity.
                //
                idfxAuthorizationPolicy = new AuthorizationPolicy(new ClaimsIdentity());
            }
            else
            {
                idfxAuthorizationPolicy = new AuthorizationPolicy(identities.AsReadOnly());
            }
 
            return idfxAuthorizationPolicy;
        }
 
        /// <summary>
        /// Creates ClaimsIdentityCollection for the given Transport SecurityToken.
        /// </summary>
        /// <param name="transportToken">Client SecurityToken provided at the Transport layer.</param>
        /// <returns>ClaimsIdentityCollection built from the Transport SecurityToken</returns>
        static ReadOnlyCollection<ClaimsIdentity> GetTransportTokenIdentities(SecurityToken transportToken)
        {
            if (transportToken == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("transportToken");
            }
 
            ServiceCredentials serviceCreds = GetServiceCredentials();
 
            List<ClaimsIdentity> transportTokenIdentityCollection = new List<ClaimsIdentity>();
 
            //////////////////////////////////////////////////////////////////////////////////////////
            // 
            // There are 5 well-known Client Authentication types at the transport layer. Each of these will
            // result either in a WindowsSecurityToken, X509SecurityToken or UserNameSecurityToken.
            // All other type of credentials (like OAuth token) result other token that will be passed trough regular validation process.
            //
            //      ClientCredential Type     ||        Transport Token Type
            // -------------------------------------------------------------------
            //          Basic                 ->        UserNameSecurityToken (In Self-hosted case)
            //          Basic                 ->        WindowsSecurityToken (In Web-Hosted case)
            //          NTLM                  ->        WindowsSecurityToken
            //          Negotiate             ->        WindowsSecurityToken
            //          Windows               ->        WindowsSecurityToken
            //          Certificate           ->        X509SecurityToken
            //
            //////////////////////////////////////////////////////////////////////////////////////////
            
            WindowsSecurityToken windowsSecurityToken = transportToken as WindowsSecurityToken;
            if ( windowsSecurityToken != null )
            {
                WindowsIdentity claimsIdentity = new WindowsIdentity( windowsSecurityToken.WindowsIdentity.Token,
                    AuthenticationTypes.Windows );
                AddAuthenticationMethod( claimsIdentity, AuthenticationMethods.Windows );
                AddAuthenticationInstantClaim(claimsIdentity, XmlConvert.ToString(DateTime.UtcNow, DateTimeFormats.Generated));
 
                // Just reflect on the wrapped WindowsIdentity and build the WindowsClaimsIdentity class.
                transportTokenIdentityCollection.Add(claimsIdentity);
            }
            else
            {
                // WCF does not call our SecurityTokenHandlers for the Transport token. So run the token through
                // the SecurityTokenHandler and generate claims for this token.
                transportTokenIdentityCollection.AddRange(serviceCreds.IdentityConfiguration.SecurityTokenHandlers.ValidateToken( transportToken ));
            }
 
            return transportTokenIdentityCollection.AsReadOnly();
        }
 
        /// <summary>
        /// Given a collection of IAuthorizationPolicies this method will eliminate the IAuthorizationPolicy
        /// that was created for the given transport Security Token. The method modifies the given collection
        /// of IAuthorizationPolicy.
        /// </summary>
        /// <param name="transportToken">Client's Security Token provided at the transport layer.</param>
        /// <param name="tranportTokenIdentities"></param>
        /// <param name="baseAuthorizationPolicies">Collection of IAuthorizationPolicies that were created by WCF.</param>
        static void EliminateTransportTokenPolicy(
            SecurityToken transportToken,
            IEnumerable<ClaimsIdentity> tranportTokenIdentities,
            List<IAuthorizationPolicy> baseAuthorizationPolicies)
        {
            if (transportToken == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("transportToken");
            }
 
            if (tranportTokenIdentities == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tranportTokenIdentities");
            }
 
            if (baseAuthorizationPolicies == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("baseAuthorizationPolicy");
            }
 
            if (baseAuthorizationPolicies.Count == 0)
            {
                // This should never happen in our current configuration. IDFx token handlers do not validate
                // client tokens present at the transport level. So we should atleast have one IAuthorizationPolicy
                // that WCF generated for the transport token.
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("baseAuthorizationPolicy", SR.GetString(SR.ID0020));
            }
 
            //
            // We will process one IAuthorizationPolicy at a time. Transport token will have been authenticated
            // by WCF and would have created a IAuthorizationPolicy for the same. If the transport token is a X.509
            // SecurityToken and 'mapToWindows' was set to true then the IAuthorizationPolicy that was created
            // by WCF will have two Claimsets, a X509ClaimSet and a WindowsClaimSet. We need to prune out this case
            // and ignore both these Claimsets as we have made a call to the token handler to authenticate this
            // token above. If we create a AuthorizationContext using all the IAuthorizationPolicies then all
            // the claimsets are merged and it becomes hard to identify this case. 
            //
            IAuthorizationPolicy policyToEliminate = null;
            foreach (IAuthorizationPolicy authPolicy in baseAuthorizationPolicies)
            {
                if (DoesPolicyMatchTransportToken(transportToken, tranportTokenIdentities, authPolicy))
                {
                    policyToEliminate = authPolicy;
                    break;
                }
            }
 
            if (policyToEliminate == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4271, transportToken));
            }
 
            baseAuthorizationPolicies.Remove(policyToEliminate);
        }
 
        /// <summary>
        /// Returns true if the IAuthorizationPolicy could have been created from the given Transport token.
        /// The method can handle only X509SecurityToken and WindowsSecurityToken.
        /// </summary>
        /// <param name="transportToken">Client's Security Token provided at the transport layer.</param>
        /// <param name="tranportTokenIdentities">A collection of <see cref="ClaimsIdentity"/> to match.</param>
        /// <param name="authPolicy">IAuthorizationPolicy to check.</param>
        /// <returns>True if the IAuthorizationPolicy could have been created from the given Transpor token.</returns>
        static bool DoesPolicyMatchTransportToken(
            SecurityToken transportToken,
            IEnumerable<ClaimsIdentity> tranportTokenIdentities,
            IAuthorizationPolicy authPolicy
            )
        {
            if (transportToken == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("transportToken");
            }
 
            if (tranportTokenIdentities == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tranportTokenIdentities");
            }
 
            if (authPolicy == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("authPolicy");
            }
 
            //////////////////////////////////////////////////////////////////////////////////////////
            // 
            // There are 5 Client Authentication types at the transport layer. Each of these will
            // result either in a WindowsSecurityToken, X509SecurityToken or UserNameSecurityToken.
            //
            //      ClientCredential Type     ||        Transport Token Type
            // -------------------------------------------------------------------
            //          Basic                 ->        UserNameSecurityToken (In Self-hosted case)
            //          Basic                 ->        WindowsSecurityToken (In Web-Hosted case)
            //          NTLM                  ->        WindowsSecurityToken
            //          Negotiate             ->        WindowsSecurityToken
            //          Windows               ->        WindowsSecurityToken
            //          Certificate           ->        X509SecurityToken
            //
            //////////////////////////////////////////////////////////////////////////////////////////
            X509SecurityToken x509SecurityToken = transportToken as X509SecurityToken;
 
            SysAuthorizationContext defaultAuthContext = SysAuthorizationContext.CreateDefaultAuthorizationContext(new List<IAuthorizationPolicy>() { authPolicy });
 
            foreach (System.IdentityModel.Claims.ClaimSet claimset in defaultAuthContext.ClaimSets)
            {
                if (x509SecurityToken != null)
                {
                    // Check if the claimset contains a claim that matches the X.509 certificate thumbprint.
                    if (claimset.ContainsClaim(new System.IdentityModel.Claims.Claim(
                            System.IdentityModel.Claims.ClaimTypes.Thumbprint,
                            x509SecurityToken.Certificate.GetCertHash(),
                            System.IdentityModel.Claims.Rights.PossessProperty)))
                    {
                        return true;
                    }
                }
                else
                {
                    // For WindowsSecurityToken and UserNameSecurityToken check that IClaimsdentity.Name 
                    // matches the Name Claim in the ClaimSet.
                    // In most cases, we will have only one Identity in the ClaimsIdentityCollection 
                    // generated from transport token. 
                    foreach (ClaimsIdentity transportTokenIdentity in tranportTokenIdentities)
                    {
                        if (claimset.ContainsClaim(new System.IdentityModel.Claims.Claim(
                                System.IdentityModel.Claims.ClaimTypes.Name,
                                transportTokenIdentity.Name,
                                System.IdentityModel.Claims.Rights.PossessProperty), new ClaimStringValueComparer()))
                        {
                            return true;
                        }
                    }
                }
            }
 
            return false;
        }
 
        /// <summary>
        /// Converts a given set of WCF IAuthorizationPolicy to WIF ClaimIdentities.
        /// </summary>
        /// <param name="authorizationPolicies">Set of AuthorizationPolicies to convert to IDFx.</param>
        /// <param name="securityTokenHandlerCollection">The SecurityTokenHandlerCollection to use.</param>
        /// <returns>ClaimsIdentityCollection</returns>
        static ReadOnlyCollection<ClaimsIdentity> ConvertToIDFxIdentities(IList<IAuthorizationPolicy> authorizationPolicies,
                                                                 SecurityTokenHandlerCollection securityTokenHandlerCollection)
        {
            if (authorizationPolicies == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("authorizationPolicies");
            }
 
            if (securityTokenHandlerCollection == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityTokenHandlerCollection");
            }
 
            List<ClaimsIdentity> identities = new List<ClaimsIdentity>();
 
            SecurityTokenSpecification kerberosTokenSpecification = null;
            SysAuthorizationContext kerberosAuthContext = null;
            if ((OperationContext.Current != null) &&
                 (OperationContext.Current.IncomingMessageProperties != null) &&
                 (OperationContext.Current.IncomingMessageProperties.Security != null))
            {
                SecurityMessageProperty securityMessageProperty = OperationContext.Current.IncomingMessageProperties.Security;
                foreach (SecurityTokenSpecification tokenSpecification in new SecurityTokenSpecificationEnumerable(securityMessageProperty))
                {
                    if (tokenSpecification.SecurityToken is KerberosReceiverSecurityToken)
                    {
                        kerberosTokenSpecification = tokenSpecification;
                        kerberosAuthContext = SysAuthorizationContext.CreateDefaultAuthorizationContext(kerberosTokenSpecification.SecurityTokenPolicies);
                        break;
                    }
                }
            }
 
            bool hasKerberosTokenPolicyMatched = false;
 
            foreach (IAuthorizationPolicy policy in authorizationPolicies)
            {
                bool authPolicyHandled = false;
 
                if ((kerberosTokenSpecification != null) && !hasKerberosTokenPolicyMatched)
                {
                    if (kerberosTokenSpecification.SecurityTokenPolicies.Contains(policy))
                    {
                        hasKerberosTokenPolicyMatched = true;
                    }
                    else
                    {
                        SysAuthorizationContext authContext = SysAuthorizationContext.CreateDefaultAuthorizationContext(new List<IAuthorizationPolicy>() { policy });                        
                        // Kerberos creates only one ClaimSet. So any more ClaimSet would mean that this is not a Policy created from Kerberos.
                        if (authContext.ClaimSets.Count == 1)
                        {
                            bool allClaimsMatched = true;
                            foreach (System.IdentityModel.Claims.Claim c in authContext.ClaimSets[0])
                            {
                                if (!kerberosAuthContext.ClaimSets[0].ContainsClaim(c))
                                {
                                    allClaimsMatched = false;
                                    break;
                                }
                            }
                            hasKerberosTokenPolicyMatched = allClaimsMatched;
                        }
                    }
 
                    if (hasKerberosTokenPolicyMatched)
                    {
                        SecurityTokenHandler tokenHandler = securityTokenHandlerCollection[kerberosTokenSpecification.SecurityToken];
                        if ((tokenHandler != null) && tokenHandler.CanValidateToken)
                        {
                            identities.AddRange(tokenHandler.ValidateToken(kerberosTokenSpecification.SecurityToken));
                            authPolicyHandled = true;
                        }
                    }
                }
 
                if (!authPolicyHandled)
                {
 
                    SysAuthorizationContext defaultAuthContext = SysAuthorizationContext.CreateDefaultAuthorizationContext(new List<IAuthorizationPolicy>() { policy });
                    //
                    // Merge all ClaimSets to IClaimsIdentity.
                    //
 
                    identities.Add(ConvertToIDFxIdentity(defaultAuthContext.ClaimSets, securityTokenHandlerCollection.Configuration));
                }
 
            }
 
            return identities.AsReadOnly();
        }
 
        /// <summary>
        /// Converts a given set of WCF ClaimSets to IDFx ClaimsIdentity.
        /// </summary>
        /// <param name="claimSets">Collection of <see cref="ClaimSet"/> to convert to IDFx.</param>
        /// <param name="securityTokenHandlerConfiguration">The SecurityTokenHandlerConfiguration to use.</param>
        /// <returns>ClaimsIdentity</returns>
        static ClaimsIdentity ConvertToIDFxIdentity(IList<ClaimSet> claimSets, SecurityTokenHandlerConfiguration securityTokenHandlerConfiguration)
        {
            if (claimSets == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claimSets");
            }
 
            ClaimsIdentity claimsIdentity = null;
            foreach (System.IdentityModel.Claims.ClaimSet claimSet in claimSets)
            {
                WindowsClaimSet windowsClaimSet = claimSet as WindowsClaimSet;
                if (windowsClaimSet != null)
                {
                    // 
                    // The ClaimSet in the authorizationContext is simply a reflection of the NT Token.
                    // The WindowsClaimsIdentity will generate that information properly. So ignore the ClaimSets.
                    //
                    //
                    // WCF does not propogate the WindowsIdentity.AuthenticationType properly.
                    // To avoid WindowsClaimsIdentity.AuthenticationType from throwing, specify
                    // this authenticationType value. Since we only have to handle SPNEGO specify Negotiate.
                    // 
                    claimsIdentity = MergeClaims(claimsIdentity, new WindowsIdentity(windowsClaimSet.WindowsIdentity.Token,
                                                                                             AuthenticationTypes.Negotiate));
 
                    AddAuthenticationMethod(claimsIdentity, AuthenticationMethods.Windows);
                    AddAuthenticationInstantClaim(claimsIdentity, XmlConvert.ToString(DateTime.UtcNow, DateTimeFormats.Generated));
                }
                else
                {
                    claimsIdentity = MergeClaims(claimsIdentity, ClaimsConversionHelper.CreateClaimsIdentityFromClaimSet(claimSet));
                    AddAuthenticationInstantClaim(claimsIdentity, XmlConvert.ToString(DateTime.UtcNow, DateTimeFormats.Generated));
                }
 
            }
 
            return claimsIdentity;
        }
 
        /// <summary>
        /// Gets the ServiceCredentials from the OperationContext.
        /// </summary>
        /// <returns>ServiceCredentials</returns>
        static ServiceCredentials GetServiceCredentials()
        {
            ServiceCredentials serviceCredentials = null;
 
            if (OperationContext.Current != null &&
                OperationContext.Current.Host != null &&
                OperationContext.Current.Host.Description != null &&
                OperationContext.Current.Host.Description.Behaviors != null)
            {
                serviceCredentials = OperationContext.Current.Host.Description.Behaviors.Find<ServiceCredentials>();
            }
 
            return serviceCredentials;
        }
 
        // Adds an Authentication Method claims to the given ClaimsIdentity if one is not already present.
        static void AddAuthenticationMethod(ClaimsIdentity claimsIdentity, string authenticationMethod)
        {
            System.Security.Claims.Claim authenticationMethodClaim =
                        claimsIdentity.Claims.FirstOrDefault(claim => claim.Type == System.Security.Claims.ClaimTypes.AuthenticationMethod);
 
            if (authenticationMethodClaim == null)
            {
                // AuthenticationMethod claims does not exist. Add one.
                claimsIdentity.AddClaim(
                    new System.Security.Claims.Claim(
                        System.Security.Claims.ClaimTypes.AuthenticationMethod, authenticationMethod));
            }
        }
 
        // Adds an Authentication Method claims to the given ClaimsIdentity if one is not already present.
        static void AddAuthenticationInstantClaim(ClaimsIdentity claimsIdentity, string authenticationInstant)
        {
            // the issuer for this claim should always be the default issuer. 
            string issuerName = ClaimsIdentity.DefaultIssuer;
            System.Security.Claims.Claim authenticationInstantClaim =
                    claimsIdentity.Claims.FirstOrDefault(claim => claim.Type == System.Security.Claims.ClaimTypes.AuthenticationInstant);
 
            if (authenticationInstantClaim == null)
            {
                // AuthenticationInstance claims does not exist. Add one.
                claimsIdentity.AddClaim(
                    new System.Security.Claims.Claim(
                        System.Security.Claims.ClaimTypes.AuthenticationInstant, authenticationInstant, ClaimValueTypes.DateTime,
                        issuerName));
            }
        }
 
        // When a token creates more than one Identity we have to merge these identities. 
        // The below method takes two Identities and will return a single identity. If one of the 
        // Identities is a WindowsIdentity then all claims from the other identity are 
        // merged into the WindowsIdentity. If neither are WindowsIdentity then it
        // selects 'identity1' and merges all the claims from 'identity2' into 'identity1'.
        //
        // It is not clear how we can handler duplicate name claim types and delegates.
        // So, we are just cloning the claims from one identity and adding it to another. 
        internal static ClaimsIdentity MergeClaims(ClaimsIdentity identity1, ClaimsIdentity identity2)
        {
            if ((identity1 == null) && (identity2 == null))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4268));
            }
 
            if (identity1 == null)
            {
                return identity2;
            }
 
            if (identity2 == null)
            {
                return identity1;
            }
 
            WindowsIdentity windowsIdentity = identity1 as WindowsIdentity;
            if (windowsIdentity != null)
            {
                windowsIdentity.AddClaims(identity2.Claims);
                return windowsIdentity;
            }
 
            windowsIdentity = identity2 as WindowsIdentity;
            if (windowsIdentity != null)
            {
                windowsIdentity.AddClaims(identity1.Claims);
                return windowsIdentity;
            }
 
            identity1.AddClaims(identity2.Claims);
 
            return identity1;
        }
 
        /// <summary>
        /// Checks authorization for the given operation context based on policy evaluation.
        /// </summary>
        /// <param name="operationContext">The OperationContext for the current authorization request.</param>
        /// <returns>true if authorized, false otherwise</returns>
        protected override bool CheckAccessCore(OperationContext operationContext)
        {
            if (operationContext == null)
            {
                return false;
            }
 
            string action = string.Empty;
 
            // WebRequests will not always have an action specified in the operation context.
            // If action is null or empty, check the httpRequest.
            if (!string.IsNullOrEmpty(operationContext.IncomingMessageHeaders.Action))
            {
                action = operationContext.IncomingMessageHeaders.Action;
            }
            else
            {
                HttpRequestMessageProperty request = operationContext.IncomingMessageProperties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
                if (request != null)
                {
                    action = request.Method;
                }
            }
 
            System.Uri resource = operationContext.IncomingMessageHeaders.To;
            ServiceCredentials credentials = GetServiceCredentials();
 
            if ((credentials == null) || string.IsNullOrEmpty(action) || (resource == null))
            {
                return false;
            }
 
            //
            // CheckAccess is called prior to impersonation in WCF, so we need to pull
            // the ClaimsPrincipal from the OperationContext.ServiceSecurityContext.AuthorizationContext.Properties[ "Principal" ].            
            //
            ClaimsPrincipal claimsPrincipal = operationContext.ServiceSecurityContext.AuthorizationContext.Properties[AuthorizationPolicy.ClaimsPrincipalKey] as ClaimsPrincipal;
            
            claimsPrincipal = credentials.IdentityConfiguration.ClaimsAuthenticationManager.Authenticate(resource.AbsoluteUri, claimsPrincipal);
            operationContext.ServiceSecurityContext.AuthorizationContext.Properties[AuthorizationPolicy.ClaimsPrincipalKey] = claimsPrincipal;
 
            if ((claimsPrincipal == null) || (claimsPrincipal.Identities == null))
            {
                return false;
            }
 
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                TraceUtility.TraceEvent(
                    TraceEventType.Information,
                    TraceCode.Security,
                    SR.GetString(SR.TraceAuthorize),
                    new System.IdentityModel.Diagnostics.AuthorizeTraceRecord(claimsPrincipal, resource.AbsoluteUri, action));
            }
 
            bool authorized = credentials.IdentityConfiguration.ClaimsAuthorizationManager.CheckAccess(
                new System.Security.Claims.AuthorizationContext(
                    claimsPrincipal, resource.AbsoluteUri, action
                    )
                );
 
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                if (authorized)
                {
                    System.IdentityModel.Diagnostics.TraceUtility.TraceString(
                        TraceEventType.Information,
                        SR.GetString(SR.TraceOnAuthorizeRequestSucceed));
                }
                else
                {
                    System.IdentityModel.Diagnostics.TraceUtility.TraceString(
                        TraceEventType.Information,
                        SR.GetString(SR.TraceOnAuthorizeRequestFailed));
                }
            }
 
            return authorized;
        }
    }
 
    class ClaimStringValueComparer : IEqualityComparer<System.IdentityModel.Claims.Claim>
    {
        #region IEqualityComparer<System.IdentityModel.Claims.Claim> Members
 
        public bool Equals(System.IdentityModel.Claims.Claim claim1, System.IdentityModel.Claims.Claim claim2)
        {
            if (ReferenceEquals(claim1, claim2))
            {
                return true;
            }
 
            if (claim1 == null || claim2 == null)
            {
                return false;
            }
 
            if (claim1.ClaimType != claim2.ClaimType || claim1.Right != claim2.Right)
            {
                return false;
            }
 
            return StringComparer.OrdinalIgnoreCase.Equals(claim1.Resource, claim2.Resource);
        }
 
        public int GetHashCode(System.IdentityModel.Claims.Claim claim)
        {
            if (claim == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claim");
            }
 
            return claim.ClaimType.GetHashCode() ^ claim.Right.GetHashCode()
                ^ ((claim.Resource == null) ? 0 : claim.Resource.GetHashCode());
        }
 
        #endregion
    }
 
    class SecurityTokenSpecificationEnumerable : IEnumerable<SecurityTokenSpecification>
    {
        SecurityMessageProperty _securityMessageProperty;
 
        public SecurityTokenSpecificationEnumerable(SecurityMessageProperty securityMessageProperty)
        {
            if (securityMessageProperty == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityMessageProperty");
            }
 
            _securityMessageProperty = securityMessageProperty;
        }
 
        public IEnumerator<SecurityTokenSpecification> GetEnumerator()
        {
            if (_securityMessageProperty.InitiatorToken != null)
            {
                yield return _securityMessageProperty.InitiatorToken;
            }
 
            if (_securityMessageProperty.ProtectionToken != null)
            {
                yield return _securityMessageProperty.ProtectionToken;
            }
 
            if (_securityMessageProperty.HasIncomingSupportingTokens)
            {
                foreach (SecurityTokenSpecification tokenSpecification in _securityMessageProperty.IncomingSupportingTokens)
                {
                    if (tokenSpecification != null)
                    {
                        yield return tokenSpecification;
                    }
                }
            }
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
        }
 
    }
 
}