| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806 |
- //------------------------------------------------------------
- // 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());
- }
- }
- }
|