< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.IdentityModel.Tokens.Saml2SecurityTokenHandler
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/Saml2SecurityTokenHandler.cs
Line coverage
23%
Covered lines: 23
Uncovered lines: 73
Coverable lines: 96
Total lines: 322
Line coverage: 23.9%
Branch coverage
16%
Covered branches: 8
Total branches: 48
Branch coverage: 16.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
.ctor(...)50%22100%
.ctor()100%11100%
.ctor(...)100%110%
.ctor(...)100%110%
ValidateToken(...)70%101042.85%
CanReadToken(...)100%11100%
ReadToken(...)100%1127.27%
TryResolveIssuerToken(...)0%660%
ResolveSecurityKeys(...)0%16160%
WriteToken(...)0%660%
GetTokenTypeIdentifiers()100%11100%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/Saml2SecurityTokenHandler.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Collections.ObjectModel;
 7using System.Linq;
 8using System.Security.Claims;
 9using System.Xml;
 10using CoreWCF.IdentityModel.Selectors;
 11using CoreWCF.Runtime;
 12using CoreWCF.Security;
 13using Microsoft.IdentityModel.Tokens.Saml2;
 14using MSIdentityTokens = Microsoft.IdentityModel.Tokens;
 15
 16namespace CoreWCF.IdentityModel.Tokens
 17{
 18    /// <summary>
 19    /// Creates SAML2 assertion-based security tokens
 20    /// </summary>
 21    public class Saml2SecurityTokenHandler : SecurityTokenHandler
 22    {
 23        /// <summary>
 24        /// The key identifier value type for SAML 2.0 assertion IDs, as defined
 25        /// by the OASIS Web Services Security SAML Token Profile 1.1.
 26        /// </summary>
 27        public const string TokenProfile11ValueType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#S
 128        private static readonly string[] s_tokenTypeIdentifiers = new string[] { SecurityTokenTypes.Saml2TokenProfile11,
 29        private SecurityTokenSerializer _keyInfoSerializer;
 30        private readonly MSIdentityTokens.Saml2.Saml2SecurityTokenHandler _internalSaml2SecurityTokenHandler;
 831        private readonly object _syncObject = new object();
 32        private readonly SamlSecurityTokenRequirement _samlSecurityTokenRequirement;
 33
 34        /// <summary>
 35        /// Creates an instance of <see cref="Saml2SecurityTokenHandler"/>
 36        /// </summary>
 37        public Saml2SecurityTokenHandler()
 838            : this(new SamlSecurityTokenRequirement())
 39        {
 840            _internalSaml2SecurityTokenHandler = new MSIdentityTokens.Saml2.Saml2SecurityTokenHandler();
 841        }
 42
 43        /// <summary>
 44        /// Creates an instance of <see cref="Saml2SecurityTokenHandler"/>
 45        /// </summary>
 46        /// <param name="samlSecurityTokenRequirement">The SamlSecurityTokenRequirement to be used by the Saml2SecurityT
 847        public Saml2SecurityTokenHandler(SamlSecurityTokenRequirement samlSecurityTokenRequirement)
 48        {
 849            _samlSecurityTokenRequirement = samlSecurityTokenRequirement ?? throw DiagnosticUtility.ExceptionUtility.Thr
 850        }
 51
 52        /// <summary>
 53        /// Method exposed for extensibility
 54        /// </summary>
 55        /// <param name="saml2SecurityTokenHandler"></param>
 56        public Saml2SecurityTokenHandler(MSIdentityTokens.Saml2.Saml2SecurityTokenHandler saml2SecurityTokenHandler)
 057            : this(saml2SecurityTokenHandler, new SamlSecurityTokenRequirement())
 58        {
 059        }
 60
 061        public Saml2SecurityTokenHandler(MSIdentityTokens.Saml2.Saml2SecurityTokenHandler saml2SecurityTokenHandler, Sam
 62        {
 063            _internalSaml2SecurityTokenHandler = saml2SecurityTokenHandler;
 064            _samlSecurityTokenRequirement = samlSecurityTokenRequirement;
 065        }
 66
 67        #region TokenValidation
 68        /// <summary>
 69        /// Returns value indicates if this handler can validate tokens of type
 70        /// Saml2SecurityToken.
 71        /// </summary>
 072        public override bool CanValidateToken => _internalSaml2SecurityTokenHandler.CanValidateToken;
 73
 74        /// <summary>
 75        /// Validates a <see cref="Saml2SecurityToken"/>.
 76        /// </summary>
 77        /// <param name="token">The <see cref="Saml2SecurityToken"/> to validate.</param>
 78        /// <returns>The <see cref="ReadOnlyCollection{T}"/> of <see cref="ClaimsIdentity"/> representing the identities
 79        /// <exception cref="ArgumentNullException">The parameter 'token' is null.</exception>
 80        /// <exception cref="ArgumentException">The token is not assignable from <see cref="Saml2SecurityToken"/>.</exce
 81        /// <exception cref="InvalidOperationException">Configuration <see cref="SecurityTokenHandlerConfiguration"/>is 
 82        /// <exception cref="ArgumentException">Saml2SecurityToken.Assertion is null.</exception>
 83        /// <exception cref="SecurityTokenValidationException">Thrown if Saml2SecurityToken.Assertion.SigningToken is nu
 84        /// <exception cref="SecurityTokenValidationException">Thrown if the certificate associated with the token issue
 85        public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
 86        {
 487            if (token == null)
 88            {
 189                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(token));
 90            }
 91
 392            Saml2SecurityToken samlToken = token as Saml2SecurityToken;
 393            if (samlToken == null)
 94            {
 195                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(token), SR.Format(SR.ID4151));
 96            }
 97
 298            if (Configuration == null)
 99            {
 1100                throw DiagnosticUtility.ExceptionUtility.ThrowHelperInvalidOperation(SR.Format(SR.ID4274));
 101            }
 102
 1103            if (samlToken.Assertion == null)
 104            {
 1105                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(token), SR.Format(SR.ID1034));
 106            }
 107
 0108            string assertionXML = samlToken.AssertionXML;
 0109            SamlTokenValidationParameters tokenValidation = new SamlTokenValidationParameters();
 0110            ClaimsPrincipal claim = _internalSaml2SecurityTokenHandler.ValidateToken(assertionXML,
 0111                tokenValidation.ConvertToTokenValidationParameters(Configuration, samlToken, _samlSecurityTokenRequireme
 0112            ClaimsIdentity claimsIdentity = (ClaimsIdentity)claim.Identity;
 0113            if (Configuration.SaveBootstrapContext)
 114            {
 0115                claimsIdentity.BootstrapContext = new BootstrapContext(token, this);
 116            }
 0117            List<ClaimsIdentity> identities = new List<ClaimsIdentity>(1)
 0118            {
 0119                claimsIdentity
 0120            };
 0121            return identities.AsReadOnly();
 122        }
 123        #endregion
 124
 125        #region TokenSerialization
 126
 127        /// <summary>
 128        /// Indicates whether the current XML element can be read as a token
 129        /// of the type handled by this instance.
 130        /// </summary>
 131        /// <param name="reader">An XML reader positioned at a start
 132        /// element. The reader should not be advanced.</param>
 133        /// <returns>'True' if the ReadToken method can the element.</returns>
 1134        public override bool CanReadToken(XmlReader reader) => _internalSaml2SecurityTokenHandler.CanReadToken(reader);
 135
 136        /// <summary>
 137        /// Deserializes from XML a token of the type handled by this instance.
 138        /// </summary>
 139        /// <param name="reader">An XML reader positioned at the token's start
 140        /// element.</param>
 141        /// <returns>An instance of <see cref="Saml2SecurityToken"/>.</returns>
 142        /// <exception cref="InvalidOperationException">Is thrown if 'Configuration' or 'Configruation.IssuerTokenResolv
 143        public override SecurityToken ReadToken(XmlReader reader)
 144        {
 1145            XmlDocument doc = new XmlDocument();
 1146            XmlElement rstXml = (doc.ReadNode(reader) as XmlElement);
 1147            MSIdentityTokens.Saml2.Saml2SecurityToken internalSecurityToken = _internalSaml2SecurityTokenHandler.ReadSam
 0148            var securityKeys = ResolveSecurityKeys(internalSecurityToken.Assertion, Configuration.IssuerTokenResolver);
 0149            TryResolveIssuerToken(internalSecurityToken.Assertion, Configuration.IssuerTokenResolver, out SecurityToken 
 0150            Saml2SecurityToken saml2SecurityToken = new Saml2SecurityToken(internalSecurityToken, securityKeys)
 0151            {
 0152                SigningToken = token,
 0153                AssertionXML = rstXml.OuterXml
 0154            };
 0155            return saml2SecurityToken;
 156        }
 157
 158        protected virtual bool TryResolveIssuerToken(Saml2Assertion assertion, SecurityTokenResolver issuerResolver, out
 159        {
 0160            if (null == assertion)
 161            {
 0162                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(assertion));
 163            }
 164
 0165            var keyIdentifier = CoreWCF.Security.SecurityUtils.CreateSecurityKeyIdentifier(assertion.Signature.KeyInfo);
 166
 0167            if (keyIdentifier != null
 0168               && issuerResolver != null)
 169            {
 170                Fx.Assert(keyIdentifier.Count == 1, "There should only be one key identifier clause");
 0171                return issuerResolver.TryResolveToken(keyIdentifier, out token);
 172            }
 173            else
 174            {
 0175                token = null;
 0176                return false;
 177            }
 178        }
 179
 180        /// <summary>
 181        /// Resolves the collection of <see cref="SecurityKey"/> referenced in a <see cref="Saml2Assertion"/>.
 182        /// </summary>
 183        /// <param name="assertion"><see cref="Saml2Assertion"/> to process.</param>
 184        /// <param name="resolver"><see cref="SecurityTokenResolver"/> to use in resolving the <see cref="SecurityKey"/>
 185        /// <returns>A read only collection of <see cref="SecurityKey"/> contained in the assertion.</returns>
 186        protected virtual ReadOnlyCollection<SecurityKey> ResolveSecurityKeys(Saml2Assertion assertion, SecurityTokenRes
 187        {
 0188            if (null == assertion)
 189            {
 0190                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(assertion));
 191            }
 192
 193            // Must have Subject
 0194            Saml2Subject subject = assertion.Subject;
 0195            if (null == subject)
 196            {
 197                // No Subject
 0198                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.Format(SR.ID4130
 199            }
 200
 201            // Must have one SubjectConfirmation
 0202            if (0 == subject.SubjectConfirmations.Count)
 203            {
 204                // No SubjectConfirmation
 0205                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.Format(SR.ID4131
 206            }
 207
 0208            if (subject.SubjectConfirmations.Count > 1)
 209            {
 210                // More than one SubjectConfirmation
 0211                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.Format(SR.ID4132
 212            }
 213
 214            // Extract the keys for the given method
 215            ReadOnlyCollection<SecurityKey> securityKeys;
 216
 0217            Saml2SubjectConfirmation subjectConfirmation = subject.SubjectConfirmations.First();
 218
 219            // For bearer, ensure there are no keys, set the collection to empty
 220            // For HolderOfKey, ensure there is at least one key, resolve and create collection
 0221            if (Saml2Constants.ConfirmationMethods.Bearer == subjectConfirmation.Method)
 222            {
 0223                if (null != subjectConfirmation.SubjectConfirmationData
 0224                    && 0 != subjectConfirmation.SubjectConfirmationData.KeyInfos.Count)
 225                {
 226                    // Bearer but has keys
 0227                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.Format(SR.ID
 228                }
 229
 0230                securityKeys = EmptyReadOnlyCollection<SecurityKey>.Instance;
 231            }
 0232            else if (Saml2Constants.ConfirmationMethods.HolderOfKey == subjectConfirmation.Method)
 233            {
 0234                throw new NotSupportedException();
 235            }
 236            else
 237            {
 238                // SenderVouches, as well as other random things, aren't accepted
 0239                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.Format(SR.ID4136
 240            }
 241
 0242            return securityKeys;
 243        }
 244
 245        /// <summary>
 246        /// Gets a boolean indicating if the SecurityTokenHandler can Serialize Tokens. Return true by default.
 247        /// </summary>
 0248        public override bool CanWriteToken => true;
 249
 250        /// <summary>
 251        /// Serializes the given SecurityToken to the XmlWriter.
 252        /// </summary>
 253        /// <param name="writer">XmlWriter into which the token is serialized.</param>
 254        /// <param name="token">SecurityToken to be serialized.</param>
 255        /// <exception cref="ArgumentNullException">Input parameter 'writer' or 'token' is null.</exception>
 256        /// <exception cref="SecurityTokenException">The given 'token' is not a Saml2SecurityToken.</exception>
 257        public override void WriteToken(XmlWriter writer, SecurityToken token)
 258        {
 0259            if (writer == null)
 260            {
 0261                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writer));
 262            }
 263
 0264            if (token == null)
 265            {
 0266                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(token));
 267            }
 268
 0269            Saml2SecurityToken samlToken = token as Saml2SecurityToken;
 0270            var wrappedSaml2SecurityToken = samlToken.WrappedSaml2SecurityToken;
 271
 0272            if (null != wrappedSaml2SecurityToken)
 273            {
 0274                _internalSaml2SecurityTokenHandler.WriteToken(writer, wrappedSaml2SecurityToken);
 275            }
 276            else
 277            {
 0278                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(token), SR.Format(SR.ID4160));
 279            }
 280        }
 281        #endregion
 282
 283        /// <summary>
 284        /// Returns the saml token's token type that is supported by this handler.
 285        /// </summary>
 8286        public override string[] GetTokenTypeIdentifiers() => s_tokenTypeIdentifiers;
 287
 288        /// <summary>
 289        /// Gets or Sets a SecurityTokenSerializers that will be used to serialize and deserializer
 290        /// SecurtyKeyIdentifier. For example, SamlSubject SecurityKeyIdentifier or Signature
 291        /// SecurityKeyIdentifier.
 292        /// </summary>
 293        public SecurityTokenSerializer KeyInfoSerializer
 294        {
 295            get
 296            {
 0297                if (_keyInfoSerializer == null)
 298                {
 0299                    lock (_syncObject)
 300                    {
 0301                        if (_keyInfoSerializer == null)
 302                        {
 0303                            SecurityTokenHandlerCollection sthc = ContainingCollection ?? throw new NotSupportedExceptio
 0304                            _keyInfoSerializer = new SecurityTokenSerializerAdapter(sthc);
 305                        }
 0306                    }
 307                }
 308
 0309                return _keyInfoSerializer;
 310            }
 311            set
 312            {
 0313                _keyInfoSerializer = value ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(va
 0314            }
 315        }
 316
 317        /// <summary>
 318        /// Gets the System.Type of the SecurityToken is supported by ththis handler.
 319        /// </summary>
 14320        public override Type TokenType => typeof(Saml2SecurityToken);
 321    }
 322}