diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt index db8578df07..6e9837ef35 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt @@ -8,12 +8,12 @@ Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions.ValidatedConditions(string ValidatedAudience, Microsoft.IdentityModel.Tokens.ValidatedLifetime? ValidatedLifetime) -> void Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions.ValidatedLifetime.get -> Microsoft.IdentityModel.Tokens.ValidatedLifetime? Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions.ValidatedLifetime.set -> void -Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.Saml.SamlSecurityToken samlToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> +Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateTokenAsync(string token, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> Microsoft.IdentityModel.Tokens.Saml.SamlValidationError Microsoft.IdentityModel.Tokens.Saml.SamlValidationError.SamlValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Exception innerException = null) -> void Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrames -Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken samlToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> +Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateTokenAsync(string token, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> Microsoft.IdentityModel.Tokens.Saml2.Saml2ValidationError Microsoft.IdentityModel.Tokens.Saml2.Saml2ValidationError.Saml2ValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Exception innerException = null) -> void diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/InternalsVisibleTo.cs b/src/Microsoft.IdentityModel.Tokens.Saml/InternalsVisibleTo.cs new file mode 100644 index 0000000000..6918991a28 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens.Saml/InternalsVisibleTo.cs @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.TestUtils, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] + diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs index c6b14409c2..23b4444503 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs @@ -35,21 +35,34 @@ internal async Task> ValidateTokenAsync( } internal async Task> ValidateTokenAsync( - SamlSecurityToken samlToken, + SecurityToken securityToken, ValidationParameters validationParameters, CallContext callContext, #pragma warning disable CA1801 // Review unused parameters CancellationToken cancellationToken) #pragma warning restore CA1801 // Review unused parameters { - if (samlToken is null) + if (securityToken is null) { StackFrames.TokenNull ??= new StackFrame(true); return ValidationError.NullParameter( - nameof(samlToken), + nameof(securityToken), StackFrames.TokenNull); } + if (securityToken is not SamlSecurityToken samlToken) + { + return new ValidationError( + new MessageDetail( + LogMessages.IDX11400, + this, + typeof(SamlSecurityToken), + securityToken.GetType()), + ValidationFailureType.InvalidSecurityToken, + typeof(SecurityTokenArgumentException), + ValidationError.GetCurrentStackFrame()); + } + if (validationParameters is null) { StackFrames.TokenValidationParametersNull ??= new StackFrame(true); diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs index 8da07673ef..81ae5762f8 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs @@ -36,19 +36,32 @@ internal async Task> ValidateTokenAsync( } internal async Task> ValidateTokenAsync( - Saml2SecurityToken samlToken, + SecurityToken securityToken, ValidationParameters validationParameters, CallContext callContext, CancellationToken cancellationToken) { - if (samlToken is null) + if (securityToken is null) { StackFrames.TokenNull ??= new StackFrame(true); return ValidationError.NullParameter( - nameof(samlToken), + nameof(securityToken), StackFrames.TokenNull); } + if (securityToken is not Saml2SecurityToken samlToken) + { + return new ValidationError( + new MessageDetail( + Tokens.Saml.LogMessages.IDX11400, + this, + typeof(Saml2SecurityToken), + securityToken.GetType()), + ValidationFailureType.InvalidSecurityToken, + typeof(SecurityTokenArgumentException), + ValidationError.GetCurrentStackFrame()); + } + if (validationParameters is null) { StackFrames.TokenValidationParametersNull ??= new StackFrame(true); diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Issuer.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Issuer.cs index 7f703bb951..37bf08994d 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Issuer.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Issuer.cs @@ -1,15 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; -using System.Diagnostics; -using System.Threading; using System.Threading.Tasks; -using Microsoft.IdentityModel.JsonWebTokens.Tests; -using Microsoft.IdentityModel.Logging; -using Microsoft.IdentityModel.TestUtils; -using Microsoft.IdentityModel.Tokens; -using Microsoft.IdentityModel.Tokens.Json.Tests; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; using Xunit; #nullable enable @@ -17,257 +10,27 @@ namespace Microsoft.IdentityModel.JsonWebTokens.Extensibility.Tests { public partial class JsonWebTokenHandlerValidateTokenAsyncTests { - [Theory, MemberData(nameof(Issuer_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)] - public async Task ValidateTokenAsync_IssuerValidator_Extensibility(IssuerExtensibilityTheoryData theoryData) + [Theory, MemberData( + nameof(GenerateIssuerExtensibilityTestCases), + parameters: ["JWT", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_IssuerValidator_Extensibility( + IssuerExtensibilityTheoryData theoryData) { - var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_IssuerValidator_Extensibility)}", theoryData); - context.IgnoreType = false; - for (int i = 0; i < theoryData.ExtraStackFrames; i++) - theoryData.IssuerValidationError!.AddStackFrame(new StackFrame(false)); - - try - { - ValidationResult validationResult = await theoryData.JsonWebTokenHandler.ValidateTokenAsync( - theoryData.JsonWebToken!, - theoryData.ValidationParameters!, - theoryData.CallContext, - CancellationToken.None); - - if (validationResult.IsValid) - { - ValidatedToken validatedToken = validationResult.UnwrapResult(); - if (validatedToken.ValidatedIssuer.HasValue) - IdentityComparer.AreValidatedIssuersEqual(validatedToken.ValidatedIssuer.Value, theoryData.ValidatedIssuer, context); - } - else - { - ValidationError validationError = validationResult.UnwrapError(); - IdentityComparer.AreValidationErrorsEqual(validationError, theoryData.IssuerValidationError, context); - theoryData.ExpectedException.ProcessException(validationError.GetException(), context); - } - } - catch (Exception ex) - { - theoryData.ExpectedException.ProcessException(ex, context); - } - - TestUtilities.AssertFailIfErrors(context); - } - - public static TheoryData Issuer_ExtensibilityTestCases - { - get - { - var theoryData = new TheoryData(); - CallContext callContext = new CallContext(); - string issuerGuid = Guid.NewGuid().ToString(); - - #region return CustomIssuerValidationError - // Test cases where delegate is overridden and return an CustomIssuerValidationError - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync, - extraStackFrames: 2) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(SecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 88), - issuerGuid) - }); - - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorCustomExceptionDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync, - extraStackFrames: 2) - { - ExpectedException = new ExpectedException( - typeof(CustomSecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(CustomSecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 107), - issuerGuid), - }); - - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorUnknownExceptionDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync, - extraStackFrames: 2) - { - // CustomIssuerValidationError does not handle the exception type 'NotSupportedException' - ExpectedException = ExpectedException.SecurityTokenException( - LogHelper.FormatInvariant( - Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; - typeof(NotSupportedException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync))), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(NotSupportedException), - new StackFrame("CustomIssuerValidationDelegates.cs", 139), - issuerGuid), - }); - - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomIssuerValidationFailureType - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync, - extraStackFrames: 2) - { - ExpectedException = new ExpectedException( - typeof(CustomSecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync), null), - CustomIssuerValidationError.CustomIssuerValidationFailureType, - typeof(CustomSecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 123), - issuerGuid, - null), - }); - #endregion - - #region return IssuerValidationError - // Test cases where delegate is overridden and return an IssuerValidationError - // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorDelegate", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync, - extraStackFrames: 2) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync)), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(SecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 169), - issuerGuid) - }); - - // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorCustomIssuerExceptionTypeDelegate", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync, - extraStackFrames: 2) - { - // IssuerValidationError does not handle the exception type 'CustomSecurityTokenInvalidIssuerException' - ExpectedException = ExpectedException.SecurityTokenException( - LogHelper.FormatInvariant( - Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; - typeof(CustomSecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync))), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(CustomSecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 196), - issuerGuid) - }); - - // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorCustomExceptionTypeDelegate", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync, - extraStackFrames: 2) - { - // IssuerValidationError does not handle the exception type 'CustomSecurityTokenException' - ExpectedException = ExpectedException.SecurityTokenException( - LogHelper.FormatInvariant( - Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; - typeof(CustomSecurityTokenException), - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync))), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(CustomSecurityTokenException), - new StackFrame("CustomIssuerValidationDelegates.cs", 210), - issuerGuid) - }); - - // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException, inner: CustomSecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorThrows", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorThrows, - extraStackFrames: 1) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenInvalidIssuerException), - string.Format(Tokens.LogMessages.IDX10269), - typeof(CustomSecurityTokenInvalidIssuerException)), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - string.Format(Tokens.LogMessages.IDX10269), null), - ValidationFailureType.IssuerValidatorThrew, - typeof(SecurityTokenInvalidIssuerException), - new StackFrame("JsonWebTokenHandler.ValidateToken.Internal.cs", 300), - issuerGuid, - new SecurityTokenInvalidIssuerException(nameof(CustomIssuerValidationDelegates.IssuerValidatorThrows)) - ) - }); - #endregion - - return theoryData; - } + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_IssuerValidator_Extensibility)); } - public class IssuerExtensibilityTheoryData : ValidateTokenAsyncBaseTheoryData + public static TheoryData GenerateIssuerExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) { - internal IssuerExtensibilityTheoryData(string testId, string issuer, IssuerValidationDelegateAsync issuerValidator, int extraStackFrames) : base(testId) - { - JsonWebToken = JsonUtilities.CreateUnsignedJsonWebToken("iss", issuer); - ValidationParameters = new ValidationParameters - { - AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation, - AudienceValidator = SkipValidationDelegates.SkipAudienceValidation, - IssuerValidatorAsync = issuerValidator, - IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation, - LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation, - SignatureValidator = SkipValidationDelegates.SkipSignatureValidation, - TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation, - TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation - }; - - ExtraStackFrames = extraStackFrames; - } - - public JsonWebToken JsonWebToken { get; } - - public JsonWebTokenHandler JsonWebTokenHandler { get; } = new JsonWebTokenHandler(); - - public bool IsValid { get; set; } - - internal ValidatedIssuer ValidatedIssuer { get; set; } - - internal IssuerValidationError? IssuerValidationError { get; set; } - - internal int ExtraStackFrames { get; } + return ExtensibilityTesting.GenerateIssuerExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "JsonWebTokenHandler.ValidateToken.Internal.cs"); } } } diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ExtensibilityTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ExtensibilityTheoryData.cs new file mode 100644 index 0000000000..c358e7ad45 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ExtensibilityTheoryData.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Security.Claims; +using System; +using Microsoft.IdentityModel.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public class ExtensibilityTheoryData : TheoryDataBase + { + string _tokenHandlerType; + SecurityTokenDescriptor? _securityTokenDescriptor; + + internal ExtensibilityTheoryData( + string testId, + string tokenHandlerType, + int extraStackFrames) : base(testId) + { + ExtraStackFrames = extraStackFrames; + TokenHandler = CreateSecurityTokenHandlerForType(tokenHandlerType); + _tokenHandlerType = tokenHandlerType; + + ValidationParameters = CreateValidationParametersSkippingValidations(); + } + + private IExtensibilityTestingTokenHandler CreateSecurityTokenHandlerForType(string tokenHandlerType) + { + return tokenHandlerType switch + { + "JWT" => new JsonWebTokenHandlerWithResult(), + "SAML" => new SamlSecurityTokenHandlerWithResult(), + "SAML2" => new Saml2SecurityTokenHandlerWithResult(), + _ => throw new NotImplementedException(tokenHandlerType) + }; + } + + private SecurityTokenDescriptor PopulateSubjectForSecurityTokenDescriptor( + SecurityTokenDescriptor securityTokenDescriptor, + string tokenHandlerType) + { + ClaimsIdentity subject = tokenHandlerType switch + { + "JWT" => Default.ClaimsIdentity, + "SAML" or "SAML2" => Default.SamlClaimsIdentity, + _ => throw new NotImplementedException(tokenHandlerType) + }; + + securityTokenDescriptor.Subject = subject; + + return securityTokenDescriptor; + } + + private ValidationParameters CreateValidationParametersSkippingValidations() + { + var validationParameters = new ValidationParameters(); + + validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation; + validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; + validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; + validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; + validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation; + validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation; + validationParameters.TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation; + validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation; + + return validationParameters; + } + + public SecurityTokenDescriptor SecurityTokenDescriptor + { + get => _securityTokenDescriptor!; + set => _securityTokenDescriptor = PopulateSubjectForSecurityTokenDescriptor(value, _tokenHandlerType); + } + + internal IExtensibilityTestingTokenHandler TokenHandler { get; } + + public bool IsValid { get; set; } + + internal ValidationParameters ValidationParameters { get; } + + internal ValidationError? ValidationError { get; set; } + + internal int ExtraStackFrames { get; } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IExtensibilityTestingTokenHandler.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IExtensibilityTestingTokenHandler.cs new file mode 100644 index 0000000000..62902868cd --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IExtensibilityTestingTokenHandler.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Tokens.Saml; +using Microsoft.IdentityModel.Tokens.Saml2; + +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + // This interface is used to test the extensibility of the ValidateTokenAsync method + // in the JsonWebTokenHandler, SamlSecurityTokenHandler, and Saml2SecurityTokenHandler classes, + // since the ValidateTokenAsync method with ValidationParameters is not part of any shared interface. + internal interface IExtensibilityTestingTokenHandler + { + Task> ValidateTokenAsync( + SecurityToken token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken); + + SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor); + } + + // Because the ValidateTokenAsync method in the token handler subclasses is internal, we need + // to create classes that implement the IValidateTokenAsyncResult interface to use in tests. + internal class JsonWebTokenHandlerWithResult : IExtensibilityTestingTokenHandler + { + private readonly JsonWebTokenHandler _handler = new JsonWebTokenHandler(); + + public JsonWebTokenHandlerWithResult() + { + } + + public async Task> ValidateTokenAsync( + SecurityToken token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + return await _handler.ValidateTokenAsync(token, validationParameters, callContext, cancellationToken); + } + + public SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor) + { + return _handler.ReadToken(_handler.CreateToken(tokenDescriptor)); + } + } + + internal class SamlSecurityTokenHandlerWithResult : IExtensibilityTestingTokenHandler + { + private readonly SamlSecurityTokenHandler _handler = new SamlSecurityTokenHandler(); + + public async Task> ValidateTokenAsync( + SecurityToken token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + return await _handler.ValidateTokenAsync(token, validationParameters, callContext, cancellationToken); + } + + public SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor) + { + SamlSecurityToken token = (SamlSecurityToken)_handler.CreateToken(tokenDescriptor); + // SamlSecurityTokenHandler.CreateToken does not set correctly the signature on the token. + // Reading the token from the CanonicalString will set the signature correctly. + return _handler.ReadToken(token.Assertion.CanonicalString); + } + } + + internal class Saml2SecurityTokenHandlerWithResult : IExtensibilityTestingTokenHandler + { + private readonly Saml2SecurityTokenHandler _handler = new Saml2SecurityTokenHandler(); + + public async Task> ValidateTokenAsync( + SecurityToken token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + return await _handler.ValidateTokenAsync(token, validationParameters, callContext, cancellationToken); + } + + public SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor) + { + Saml2SecurityToken token = (Saml2SecurityToken)_handler.CreateToken(tokenDescriptor); + // SamlSecurityTokenHandler.CreateToken does not set correctly the signature on the token. + // Reading the token from the CanonicalString will set the signature correctly. + return _handler.ReadToken(token.Assertion.CanonicalString); + } + } +} diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerExtensibilityTestCases.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerExtensibilityTestCases.cs new file mode 100644 index 0000000000..9a6a669aa9 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerExtensibilityTestCases.cs @@ -0,0 +1,205 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using Xunit; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Logging; + +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public partial class ExtensibilityTesting + { + public static TheoryData GenerateIssuerExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames, + string stackFrameFileName) + { + var theoryData = new TheoryData(); + CallContext callContext = new CallContext(); + string issuerGuid = Guid.NewGuid().ToString(); + + #region return CustomIssuerValidationError + // Test cases where delegate is overridden and return an CustomIssuerValidationError + // CustomIssuerValidationError : IssuerValidationError, ExceptionType: SecurityTokenInvalidIssuerException + theoryData.Add(new IssuerExtensibilityTheoryData( + "CustomIssuerValidatorDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync, + extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidIssuerException), + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync)), + ValidationError = new CustomIssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync), null), + ValidationFailureType.IssuerValidationFailed, + typeof(SecurityTokenInvalidIssuerException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid) + }); + + // CustomIssuerValidationError : IssuerValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException + theoryData.Add(new IssuerExtensibilityTheoryData( + "CustomIssuerValidatorCustomExceptionDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync, + extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidIssuerException), + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync)), + ValidationError = new CustomIssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync), null), + ValidationFailureType.IssuerValidationFailed, + typeof(CustomSecurityTokenInvalidIssuerException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid), + }); + + // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException + theoryData.Add(new IssuerExtensibilityTheoryData( + "CustomIssuerValidatorUnknownExceptionDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync, + extraStackFrames) + { + // CustomIssuerValidationError does not handle the exception type 'NotSupportedException' + ExpectedException = ExpectedException.SecurityTokenException( + LogHelper.FormatInvariant( + Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; + typeof(NotSupportedException), + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync))), + ValidationError = new CustomIssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync), null), + ValidationFailureType.IssuerValidationFailed, + typeof(NotSupportedException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid), + }); + + // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomIssuerValidationFailureType + theoryData.Add(new IssuerExtensibilityTheoryData( + "CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync, + extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidIssuerException), + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync)), + ValidationError = new CustomIssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync), null), + CustomIssuerValidationError.CustomIssuerValidationFailureType, + typeof(CustomSecurityTokenInvalidIssuerException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid, + null), + }); + #endregion + + #region return IssuerValidationError + // Test cases where delegate is overridden and return an IssuerValidationError + // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException + theoryData.Add(new IssuerExtensibilityTheoryData( + "IssuerValidatorDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync, + extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidIssuerException), + nameof(CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync)), + ValidationError = new IssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync), null), + ValidationFailureType.IssuerValidationFailed, + typeof(SecurityTokenInvalidIssuerException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid) + }); + + // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException + theoryData.Add(new IssuerExtensibilityTheoryData( + "IssuerValidatorCustomIssuerExceptionTypeDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync, + extraStackFrames) + { + // IssuerValidationError does not handle the exception type 'CustomSecurityTokenInvalidIssuerException' + ExpectedException = ExpectedException.SecurityTokenException( + LogHelper.FormatInvariant( + Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; + typeof(CustomSecurityTokenInvalidIssuerException), + nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync))), + ValidationError = new IssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync), null), + ValidationFailureType.IssuerValidationFailed, + typeof(CustomSecurityTokenInvalidIssuerException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid) + }); + + // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException + theoryData.Add(new IssuerExtensibilityTheoryData( + "IssuerValidatorCustomExceptionTypeDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync, + extraStackFrames) + { + // IssuerValidationError does not handle the exception type 'CustomSecurityTokenException' + ExpectedException = ExpectedException.SecurityTokenException( + LogHelper.FormatInvariant( + Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; + typeof(CustomSecurityTokenException), + nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync))), + ValidationError = new IssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync), null), + ValidationFailureType.IssuerValidationFailed, + typeof(CustomSecurityTokenException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid) + }); + + // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException, inner: CustomSecurityTokenInvalidIssuerException + theoryData.Add(new IssuerExtensibilityTheoryData( + "IssuerValidatorThrows", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.IssuerValidatorThrows, + extraStackFrames - 1) // when throwing an exception, the stack trace contains one less frame + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidIssuerException), + string.Format(Tokens.LogMessages.IDX10269), + typeof(CustomSecurityTokenInvalidIssuerException)), + ValidationError = new IssuerValidationError( + new MessageDetail( + string.Format(Tokens.LogMessages.IDX10269), null), + ValidationFailureType.IssuerValidatorThrew, + typeof(SecurityTokenInvalidIssuerException), + new StackFrame(stackFrameFileName, 0), + issuerGuid, + new SecurityTokenInvalidIssuerException(nameof(CustomIssuerValidationDelegates.IssuerValidatorThrows)) + ) + }); + #endregion + + return theoryData; + } + } +} diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerExtensibilityTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerExtensibilityTheoryData.cs new file mode 100644 index 0000000000..12b4c53c28 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerExtensibilityTheoryData.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.IdentityModel.Tokens; + +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public class IssuerExtensibilityTheoryData : ExtensibilityTheoryData + { + internal IssuerExtensibilityTheoryData( + string testId, + string tokenHandlerType, + string issuer, + IssuerValidationDelegateAsync issuerValidationDelegate, + int extraStackFrames) : base(testId, tokenHandlerType, extraStackFrames) + { + SecurityTokenDescriptor = new() + { + Issuer = issuer, + }; + + ValidationParameters.IssuerValidatorAsync = issuerValidationDelegate; + } + } +} diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ValidateTokenAsyncExtensibility.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ValidateTokenAsyncExtensibility.cs new file mode 100644 index 0000000000..f5cc14145e --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ValidateTokenAsyncExtensibility.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.Tokens; + +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public partial class ExtensibilityTesting + { + public static async Task ValidateTokenAsync_Extensibility(ExtensibilityTheoryData theoryData, object testInstance, string methodName) + { + var context = TestUtilities.WriteHeader($"{testInstance}.{methodName}", theoryData); + context.IgnoreType = false; + for (int i = 0; i < theoryData.ExtraStackFrames; i++) + theoryData.ValidationError!.AddStackFrame(new StackFrame(false)); + + SecurityToken securityToken = theoryData.TokenHandler.CreateToken(theoryData.SecurityTokenDescriptor!); + + try + { + ValidationResult validationResult = await theoryData.TokenHandler.ValidateTokenAsync( + securityToken, + theoryData.ValidationParameters!, + theoryData.CallContext, + CancellationToken.None); + + if (validationResult.IsValid) + { + context.AddDiff("validationResult.IsValid == true, expected false"); + } + else + { + ValidationError validationError = validationResult.UnwrapError(); + IdentityComparer.AreValidationErrorsEqual(validationError, theoryData.ValidationError, context); + theoryData.ExpectedException.ProcessException(validationError.GetException(), context); + } + } + catch (Exception ex) + { + context.AddDiff($"ValidateTokenAsync threw an unexpected exception: {ex}."); + } + + TestUtilities.AssertFailIfErrors(context); + } + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Issuer.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Issuer.cs index 4f5afae851..e5ce7bffc0 100644 --- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Issuer.cs +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Issuer.cs @@ -1,13 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; -using System.Diagnostics; -using System.Threading; using System.Threading.Tasks; -using Microsoft.IdentityModel.Logging; -using Microsoft.IdentityModel.TestUtils; - +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; using Xunit; #nullable enable @@ -15,263 +10,27 @@ namespace Microsoft.IdentityModel.Tokens.Saml2.Extensibility.Tests { public partial class Saml2SecurityTokenHandlerValidateTokenAsyncTests { - [Theory, MemberData(nameof(Issuer_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)] - public async Task ValidateTokenAsync_IssuerValidator_Extensibility(IssuerExtensibilityTheoryData theoryData) + [Theory, MemberData( + nameof(GenerateIssuerExtensibilityTestCases), + parameters: ["SAML2", 1], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_IssuerValidator_Extensibility( + IssuerExtensibilityTheoryData theoryData) { - var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_IssuerValidator_Extensibility)}", theoryData); - context.IgnoreType = false; - for (int i = 0; i < theoryData.ExtraStackFrames; i++) - theoryData.IssuerValidationError!.AddStackFrame(new StackFrame(false)); - - try - { - ValidationResult validationResult = await theoryData.SamlSecurityTokenHandler.ValidateTokenAsync( - theoryData.SamlToken!, - theoryData.ValidationParameters!, - theoryData.CallContext, - CancellationToken.None); - - if (validationResult.IsValid) - { - ValidatedToken validatedToken = validationResult.UnwrapResult(); - if (validatedToken.ValidatedIssuer.HasValue) - IdentityComparer.AreValidatedIssuersEqual(validatedToken.ValidatedIssuer.Value, theoryData.ValidatedIssuer, context); - } - else - { - ValidationError validationError = validationResult.UnwrapError(); - IdentityComparer.AreValidationErrorsEqual(validationError, theoryData.IssuerValidationError, context); - theoryData.ExpectedException.ProcessException(validationError.GetException(), context); - } - } - catch (Exception ex) - { - theoryData.ExpectedException.ProcessException(ex, context); - } - - TestUtilities.AssertFailIfErrors(context); + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_IssuerValidator_Extensibility)); } - public static TheoryData Issuer_ExtensibilityTestCases + public static TheoryData GenerateIssuerExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) { - get - { - var theoryData = new TheoryData(); - CallContext callContext = new CallContext(); - string issuerGuid = Guid.NewGuid().ToString(); - - #region return CustomIssuerValidationError - // Test cases where delegate is overridden and return an CustomIssuerValidationError - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync, - extraStackFrames: 1) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(SecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 88), - issuerGuid) - }); - - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorCustomExceptionDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync, - extraStackFrames: 1) - { - ExpectedException = new ExpectedException( - typeof(CustomSecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(CustomSecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 107), - issuerGuid), - }); - - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorUnknownExceptionDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync, - extraStackFrames: 1) - { - // CustomIssuerValidationError does not handle the exception type 'NotSupportedException' - ExpectedException = ExpectedException.SecurityTokenException( - LogHelper.FormatInvariant( - Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; - typeof(NotSupportedException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync))), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(NotSupportedException), - new StackFrame("CustomIssuerValidationDelegates.cs", 139), - issuerGuid), - }); - - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomIssuerValidationFailureType - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync, - extraStackFrames: 1) - { - ExpectedException = new ExpectedException( - typeof(CustomSecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync), null), - CustomIssuerValidationError.CustomIssuerValidationFailureType, - typeof(CustomSecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 123), - issuerGuid), - }); - #endregion - - #region return IssuerValidationError - // Test cases where delegate is overridden and return an IssuerValidationError - // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorDelegate", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync, - extraStackFrames: 1) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync)), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(SecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 169), - issuerGuid) - }); - - // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorCustomIssuerExceptionTypeDelegate", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync, - extraStackFrames: 1) - { - // IssuerValidationError does not handle the exception type 'CustomSecurityTokenInvalidIssuerException' - ExpectedException = ExpectedException.SecurityTokenException( - LogHelper.FormatInvariant( - Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; - typeof(CustomSecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync))), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(CustomSecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 196), - issuerGuid) - }); - - // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorCustomExceptionTypeDelegate", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync, - extraStackFrames: 1) - { - // IssuerValidationError does not handle the exception type 'CustomSecurityTokenException' - ExpectedException = ExpectedException.SecurityTokenException( - LogHelper.FormatInvariant( - Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; - typeof(CustomSecurityTokenException), - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync))), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(CustomSecurityTokenException), - new StackFrame("CustomIssuerValidationDelegates.cs", 210), - issuerGuid) - }); - - // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException, inner: CustomSecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorThrows", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorThrows, - extraStackFrames: 0) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenInvalidIssuerException), - string.Format(Tokens.LogMessages.IDX10269), - typeof(CustomSecurityTokenInvalidIssuerException)), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - string.Format(Tokens.LogMessages.IDX10269), null), - ValidationFailureType.IssuerValidatorThrew, - typeof(SecurityTokenInvalidIssuerException), - new StackFrame("Saml2SecurityTokenHandler.ValidateToken.Internal.cs", 95), - issuerGuid, - new SecurityTokenInvalidIssuerException(nameof(CustomIssuerValidationDelegates.IssuerValidatorThrows)) - ) - }); - #endregion - - return theoryData; - } - } - - public class IssuerExtensibilityTheoryData : TheoryDataBase - { - internal IssuerExtensibilityTheoryData(string testId, string issuer, IssuerValidationDelegateAsync issuerValidator, int extraStackFrames) : base(testId) - { - SamlToken = (Saml2SecurityToken)SamlSecurityTokenHandler.CreateToken(new() - { - Subject = Default.SamlClaimsIdentity, - Issuer = issuer, - }); - - ValidationParameters = new ValidationParameters - { - AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation, - AudienceValidator = SkipValidationDelegates.SkipAudienceValidation, - IssuerValidatorAsync = issuerValidator, - IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation, - LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation, - SignatureValidator = SkipValidationDelegates.SkipSignatureValidation, - TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation, - TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation - }; - - ExtraStackFrames = extraStackFrames; - } - - public Saml2SecurityToken SamlToken { get; } - - public Saml2SecurityTokenHandler SamlSecurityTokenHandler { get; } = new Saml2SecurityTokenHandler(); - - public bool IsValid { get; set; } - - internal ValidatedIssuer ValidatedIssuer { get; set; } - - internal ValidationParameters? ValidationParameters { get; set; } - - internal IssuerValidationError? IssuerValidationError { get; set; } - - internal int ExtraStackFrames { get; } + return ExtensibilityTesting.GenerateIssuerExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "Saml2SecurityTokenHandler.ValidateToken.Internal.cs"); } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Issuer.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Issuer.cs index af2e6822d9..b0c95b0952 100644 --- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Issuer.cs +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Issuer.cs @@ -1,13 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; -using System.Diagnostics; -using System.Threading; using System.Threading.Tasks; -using Microsoft.IdentityModel.Logging; -using Microsoft.IdentityModel.TestUtils; - +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; using Xunit; #nullable enable @@ -15,263 +10,27 @@ namespace Microsoft.IdentityModel.Tokens.Saml.Extensibility.Tests { public partial class SamlSecurityTokenHandlerValidateTokenAsyncTests { - [Theory, MemberData(nameof(Issuer_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)] - public async Task ValidateTokenAsync_IssuerValidator_Extensibility(IssuerExtensibilityTheoryData theoryData) + [Theory, MemberData( + nameof(GenerateIssuerExtensibilityTestCases), + parameters: ["SAML", 1], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_IssuerValidator_Extensibility( + IssuerExtensibilityTheoryData theoryData) { - var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_IssuerValidator_Extensibility)}", theoryData); - context.IgnoreType = false; - for (int i = 0; i < theoryData.ExtraStackFrames; i++) - theoryData.IssuerValidationError!.AddStackFrame(new StackFrame(false)); - - try - { - ValidationResult validationResult = await theoryData.SamlSecurityTokenHandler.ValidateTokenAsync( - theoryData.SamlToken!, - theoryData.ValidationParameters!, - theoryData.CallContext, - CancellationToken.None); - - if (validationResult.IsValid) - { - ValidatedToken validatedToken = validationResult.UnwrapResult(); - if (validatedToken.ValidatedIssuer.HasValue) - IdentityComparer.AreValidatedIssuersEqual(validatedToken.ValidatedIssuer.Value, theoryData.ValidatedIssuer, context); - } - else - { - ValidationError validationError = validationResult.UnwrapError(); - IdentityComparer.AreValidationErrorsEqual(validationError, theoryData.IssuerValidationError, context); - theoryData.ExpectedException.ProcessException(validationError.GetException(), context); - } - } - catch (Exception ex) - { - theoryData.ExpectedException.ProcessException(ex, context); - } - - TestUtilities.AssertFailIfErrors(context); + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_IssuerValidator_Extensibility)); } - public static TheoryData Issuer_ExtensibilityTestCases + public static TheoryData GenerateIssuerExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) { - get - { - var theoryData = new TheoryData(); - CallContext callContext = new CallContext(); - string issuerGuid = Guid.NewGuid().ToString(); - - #region return CustomIssuerValidationError - // Test cases where delegate is overridden and return an CustomIssuerValidationError - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync, - extraStackFrames: 1) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(SecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 88), - issuerGuid) - }); - - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorCustomExceptionDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync, - extraStackFrames: 1) - { - ExpectedException = new ExpectedException( - typeof(CustomSecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(CustomSecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 107), - issuerGuid), - }); - - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorUnknownExceptionDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync, - extraStackFrames: 1) - { - // CustomIssuerValidationError does not handle the exception type 'NotSupportedException' - ExpectedException = ExpectedException.SecurityTokenException( - LogHelper.FormatInvariant( - Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; - typeof(NotSupportedException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync))), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(NotSupportedException), - new StackFrame("CustomIssuerValidationDelegates.cs", 139), - issuerGuid), - }); - - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomIssuerValidationFailureType - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync, - extraStackFrames: 1) - { - ExpectedException = new ExpectedException( - typeof(CustomSecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync), null), - CustomIssuerValidationError.CustomIssuerValidationFailureType, - typeof(CustomSecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 123), - issuerGuid), - }); - #endregion - - #region return IssuerValidationError - // Test cases where delegate is overridden and return an IssuerValidationError - // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorDelegate", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync, - extraStackFrames: 1) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync)), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(SecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 169), - issuerGuid) - }); - - // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorCustomIssuerExceptionTypeDelegate", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync, - extraStackFrames: 1) - { - // IssuerValidationError does not handle the exception type 'CustomSecurityTokenInvalidIssuerException' - ExpectedException = ExpectedException.SecurityTokenException( - LogHelper.FormatInvariant( - Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; - typeof(CustomSecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync))), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(CustomSecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates.cs", 196), - issuerGuid) - }); - - // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorCustomExceptionTypeDelegate", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync, - extraStackFrames: 1) - { - // IssuerValidationError does not handle the exception type 'CustomSecurityTokenException' - ExpectedException = ExpectedException.SecurityTokenException( - LogHelper.FormatInvariant( - Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; - typeof(CustomSecurityTokenException), - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync))), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(CustomSecurityTokenException), - new StackFrame("CustomIssuerValidationDelegates.cs", 210), - issuerGuid) - }); - - // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException, inner: CustomSecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorThrows", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorThrows, - extraStackFrames: 0) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenInvalidIssuerException), - string.Format(Tokens.LogMessages.IDX10269), - typeof(CustomSecurityTokenInvalidIssuerException)), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - string.Format(Tokens.LogMessages.IDX10269), null), - ValidationFailureType.IssuerValidatorThrew, - typeof(SecurityTokenInvalidIssuerException), - new StackFrame("SamlSecurityTokenHandler.ValidateToken.Internal.cs", 92), - issuerGuid, - new SecurityTokenInvalidIssuerException(nameof(CustomIssuerValidationDelegates.IssuerValidatorThrows)) - ) - }); - #endregion - - return theoryData; - } - } - - public class IssuerExtensibilityTheoryData : TheoryDataBase - { - internal IssuerExtensibilityTheoryData(string testId, string issuer, IssuerValidationDelegateAsync issuerValidator, int extraStackFrames) : base(testId) - { - SamlToken = (SamlSecurityToken)SamlSecurityTokenHandler.CreateToken(new() - { - Subject = Default.SamlClaimsIdentity, - Issuer = issuer, - }); - - ValidationParameters = new ValidationParameters - { - AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation, - AudienceValidator = SkipValidationDelegates.SkipAudienceValidation, - IssuerValidatorAsync = issuerValidator, - IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation, - LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation, - SignatureValidator = SkipValidationDelegates.SkipSignatureValidation, - TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation, - TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation - }; - - ExtraStackFrames = extraStackFrames; - } - - public SamlSecurityToken SamlToken { get; } - - public SamlSecurityTokenHandler SamlSecurityTokenHandler { get; } = new SamlSecurityTokenHandler(); - - public bool IsValid { get; set; } - - internal ValidatedIssuer ValidatedIssuer { get; set; } - - internal ValidationParameters? ValidationParameters { get; set; } - - internal IssuerValidationError? IssuerValidationError { get; set; } - - internal int ExtraStackFrames { get; } + return ExtensibilityTesting.GenerateIssuerExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "SamlSecurityTokenHandler.ValidateToken.Internal.cs"); } } }