//------------------------------------------------------------ // 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 { /// /// Custom ServiceAuthorizationManager implementation. This class substitues the WCF /// generated IAuthorizationPolicies with /// . 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 /// will be available for Authorization decisions. /// class IdentityModelServiceAuthorizationManager : ServiceAuthorizationManager { /// /// Authorization policy for anonymous authentication. /// protected static readonly ReadOnlyCollection AnonymousAuthorizationPolicy = new ReadOnlyCollection( new List() { new AuthorizationPolicy(new ClaimsIdentity()) }); /// /// Override of the base class method. Substitues WCF IAuthorizationPolicy with /// . /// /// Current OperationContext that contains all the IAuthorizationPolicies. /// Read-Only collection of protected override ReadOnlyCollection 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 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() { transformedPolicy }).AsReadOnly(); } } internal static AuthorizationPolicy TransformAuthorizationPolicies( ReadOnlyCollection baseAuthorizationPolicies, SecurityTokenHandlerCollection securityTokenHandlerCollection, bool includeTransportTokens) { List identities = new List(); List uncheckedAuthorizationPolicies = new List(); // // 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 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 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; } /// /// Creates ClaimsIdentityCollection for the given Transport SecurityToken. /// /// Client SecurityToken provided at the Transport layer. /// ClaimsIdentityCollection built from the Transport SecurityToken static ReadOnlyCollection GetTransportTokenIdentities(SecurityToken transportToken) { if (transportToken == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("transportToken"); } ServiceCredentials serviceCreds = GetServiceCredentials(); List transportTokenIdentityCollection = new List(); ////////////////////////////////////////////////////////////////////////////////////////// // // 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(); } /// /// 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. /// /// Client's Security Token provided at the transport layer. /// /// Collection of IAuthorizationPolicies that were created by WCF. static void EliminateTransportTokenPolicy( SecurityToken transportToken, IEnumerable tranportTokenIdentities, List 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); } /// /// Returns true if the IAuthorizationPolicy could have been created from the given Transport token. /// The method can handle only X509SecurityToken and WindowsSecurityToken. /// /// Client's Security Token provided at the transport layer. /// A collection of to match. /// IAuthorizationPolicy to check. /// True if the IAuthorizationPolicy could have been created from the given Transpor token. static bool DoesPolicyMatchTransportToken( SecurityToken transportToken, IEnumerable 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() { 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; } /// /// Converts a given set of WCF IAuthorizationPolicy to WIF ClaimIdentities. /// /// Set of AuthorizationPolicies to convert to IDFx. /// The SecurityTokenHandlerCollection to use. /// ClaimsIdentityCollection static ReadOnlyCollection ConvertToIDFxIdentities(IList authorizationPolicies, SecurityTokenHandlerCollection securityTokenHandlerCollection) { if (authorizationPolicies == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("authorizationPolicies"); } if (securityTokenHandlerCollection == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityTokenHandlerCollection"); } List identities = new List(); 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() { 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() { policy }); // // Merge all ClaimSets to IClaimsIdentity. // identities.Add(ConvertToIDFxIdentity(defaultAuthContext.ClaimSets, securityTokenHandlerCollection.Configuration)); } } return identities.AsReadOnly(); } /// /// Converts a given set of WCF ClaimSets to IDFx ClaimsIdentity. /// /// Collection of to convert to IDFx. /// The SecurityTokenHandlerConfiguration to use. /// ClaimsIdentity static ClaimsIdentity ConvertToIDFxIdentity(IList 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; } /// /// Gets the ServiceCredentials from the OperationContext. /// /// ServiceCredentials 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(); } 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; } /// /// Checks authorization for the given operation context based on policy evaluation. /// /// The OperationContext for the current authorization request. /// true if authorized, false otherwise 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 { #region IEqualityComparer 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 { SecurityMessageProperty _securityMessageProperty; public SecurityTokenSpecificationEnumerable(SecurityMessageProperty securityMessageProperty) { if (securityMessageProperty == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityMessageProperty"); } _securityMessageProperty = securityMessageProperty; } public IEnumerator 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()); } } }