Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ArgumentNullException type with ValidationError #2818

Merged
merged 6 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Diagnostics;
#if !NET8_0_OR_GREATER
using System.Text;
#endif

#nullable enable

namespace Microsoft.IdentityModel.Tokens
{
internal class SecurityTokenArgumentNullException : ArgumentNullException, ISecurityTokenException
{
private string? _stackTrace;
private ValidationError? _validationError;

/// <summary>
/// Initializes a new instance of the <see cref="SecurityTokenArgumentNullException"/> class.
/// </summary>
public SecurityTokenArgumentNullException()
: base()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="SecurityTokenArgumentNullException"/> class with a specified null parameter.
/// </summary>
/// <param name="paramName">The name of the null parameter that triggered the exception.</param>
public SecurityTokenArgumentNullException(string? paramName)
: base(paramName)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="SecurityTokenArgumentNullException"/> class with a specified error message
/// and a reference to the inner exception that is the cause of this exception.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The <see cref="Exception"/> that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
public SecurityTokenArgumentNullException(string? message, Exception? innerException)
: base(message, innerException)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="SecurityTokenArgumentNullException"/> class with a specified null parameter and an error message.
/// </summary>
/// <param name="paramName">The name of the null parameter that triggered the exception.</param>
/// <param name="message">The error message that explains the reason for the exception.</param>
public SecurityTokenArgumentNullException(string? paramName, string? message)
: base(paramName, message)
{
}

/// <summary>
/// Sets the <see cref="ValidationError"/> that is associated with the exception.
/// </summary>
/// <param name="validationError">The validation error to associate with the exception.</param>
public void SetValidationError(ValidationError validationError)
{
_validationError = validationError;
}


/// <summary>
/// Gets the stack trace that is captured when the exception is created.
/// </summary>
public override string? StackTrace
{
get
{
if (_stackTrace == null)
{
if (_validationError == null)
return base.StackTrace;
#if NET8_0_OR_GREATER
_stackTrace = new StackTrace(_validationError.StackFrames).ToString();
#else
StringBuilder sb = new();
foreach (StackFrame frame in _validationError.StackFrames)
{
sb.Append(frame.ToString());
sb.Append(Environment.NewLine);
}

_stackTrace = sb.ToString();
#endif
}

return _stackTrace;
}
}
}
}
#nullable restore
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ namespace Microsoft.IdentityModel.Tokens
/// Represents a security token exception.
/// </summary>
[Serializable]
public class SecurityTokenException : Exception
public class SecurityTokenException : Exception, ISecurityTokenException
{
[NonSerialized]
private string _stackTrace;

private ValidationError _validationError;

/// <summary>
/// Initializes a new instance of the <see cref="SecurityTokenException"/> class.
/// </summary>
Expand Down Expand Up @@ -66,6 +68,15 @@ protected SecurityTokenException(SerializationInfo info, StreamingContext contex
{
}

/// <summary>
iNinja marked this conversation as resolved.
Show resolved Hide resolved
///
/// </summary>
/// <param name="validationError"></param>
public void SetValidationError(ValidationError validationError)
{
_validationError = validationError;
}

/// <summary>
/// Gets the stack trace that is captured when the exception is created.
/// </summary>
Expand All @@ -75,13 +86,13 @@ public override string StackTrace
{
if (_stackTrace == null)
{
if (ValidationError == null)
if (_validationError == null)
return base.StackTrace;
#if NET8_0_OR_GREATER
_stackTrace = new StackTrace(ValidationError.StackFrames).ToString();
_stackTrace = new StackTrace(_validationError.StackFrames).ToString();
#else
StringBuilder sb = new();
foreach (StackFrame frame in ValidationError.StackFrames)
foreach (StackFrame frame in _validationError.StackFrames)
{
sb.Append(frame.ToString());
sb.Append(Environment.NewLine);
Expand All @@ -104,11 +115,6 @@ public override string Source
set => base.Source = value;
}

internal ValidationError ValidationError
{
get; set;
}

#if NET472 || NETSTANDARD2_0 || NET6_0_OR_GREATER
/// <summary>
/// When overridden in a derived class, sets the System.Runtime.Serialization.SerializationInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public LifetimeValidationError(
_additionalInformation = additionalInformation.Value;
}

protected override void AddAdditionalInformation(Exception exception)
internal override void AddAdditionalInformation(ISecurityTokenException exception)
{
if (exception is SecurityTokenExpiredException expiredException &&
_additionalInformation.ExpirationDate.HasValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@

namespace Microsoft.IdentityModel.Tokens
{
internal interface ISecurityTokenException
{
void SetValidationError(ValidationError validationError);
}

/// <summary>
/// Contains information so that Exceptions can be logged or thrown written as required.
/// </summary>
internal class ValidationError
public class ValidationError
iNinja marked this conversation as resolved.
Show resolved Hide resolved
{
private Type _exceptionType;

Expand All @@ -21,7 +26,7 @@ internal class ValidationError
/// <param name="exceptionType"/> is the type of exception that occurred.
/// <param name="failureType"/> is the type of validation failure that occurred.
/// <param name="stackFrame"/> is the stack frame where the exception occurred.
public ValidationError(
internal ValidationError(
MessageDetail MessageDetail,
ValidationFailureType failureType,
Type exceptionType,
Expand All @@ -38,7 +43,7 @@ public ValidationError(
/// <param name="failureType"/> is the type of validation failure that occurred.
/// <param name="stackFrame"/> is the stack frame where the exception occurred.
/// <param name="innerException"/> is the inner exception that occurred.
public ValidationError(
internal ValidationError(
MessageDetail messageDetail,
ValidationFailureType failureType,
Type exceptionType,
Expand All @@ -55,7 +60,7 @@ public ValidationError(
};
}

public ValidationError(
internal ValidationError(
MessageDetail messageDetail,
ValidationFailureType failureType,
Type exceptionType,
Expand All @@ -79,10 +84,12 @@ public ValidationError(
public Exception GetException()
{
Exception exception = GetException(ExceptionType, InnerException);
if (exception is SecurityTokenException securityTokenException)
securityTokenException.ValidationError = this;

AddAdditionalInformation(exception);
if (exception is ISecurityTokenException securityTokenException)
{
securityTokenException.SetValidationError(this);
AddAdditionalInformation(securityTokenException);
}

return exception;
}
Expand Down Expand Up @@ -127,8 +134,8 @@ private Exception GetException(Type exceptionType, Exception innerException)
exception = new SecurityTokenMalformedException(MessageDetail.Message);
else if (exceptionType == typeof(SecurityTokenInvalidSignatureException))
exception = new SecurityTokenInvalidSignatureException(MessageDetail.Message);
else if (exceptionType == typeof(ArgumentNullException))
exception = new ArgumentNullException(MessageDetail.Message);
else if (exceptionType == typeof(SecurityTokenArgumentNullException))
exception = new SecurityTokenArgumentNullException(MessageDetail.Message);
else if (exceptionType == typeof(SecurityTokenInvalidAlgorithmException))
exception = new SecurityTokenInvalidAlgorithmException(MessageDetail.Message);
else if (exceptionType == typeof(SecurityTokenInvalidAlgorithmException))
Expand Down Expand Up @@ -174,8 +181,8 @@ private Exception GetException(Type exceptionType, Exception innerException)
exception = new SecurityTokenMalformedException(MessageDetail.Message, actualException);
else if (exceptionType == typeof(SecurityTokenInvalidSignatureException))
exception = new SecurityTokenInvalidSignatureException(MessageDetail.Message, actualException);
else if (exceptionType == typeof(ArgumentNullException))
exception = new ArgumentNullException(MessageDetail.Message, actualException);
else if (exceptionType == typeof(SecurityTokenArgumentNullException))
exception = new SecurityTokenArgumentNullException(MessageDetail.Message, actualException);
else if (exceptionType == typeof(SecurityTokenInvalidAlgorithmException))
exception = new SecurityTokenInvalidAlgorithmException(MessageDetail.Message, actualException);
else if (exceptionType == typeof(SecurityTokenInvalidAlgorithmException))
Expand All @@ -187,21 +194,21 @@ private Exception GetException(Type exceptionType, Exception innerException)
return exception;
}

protected virtual void AddAdditionalInformation(Exception exception)
internal virtual void AddAdditionalInformation(ISecurityTokenException exception)
{
// base implementation is no-op. Derived classes can override to add additional information to the exception.
}

internal static ValidationError NullParameter(string parameterName, StackFrame stackFrame) => new ValidationError(
internal static ValidationError NullParameter(string parameterName, StackFrame stackFrame) => new(
MessageDetail.NullParameter(parameterName),
ValidationFailureType.NullArgument,
typeof(ArgumentNullException),
typeof(SecurityTokenArgumentNullException),
stackFrame);

/// <summary>
/// Gets the type of validation failure that occurred.
/// </summary>
public ValidationFailureType FailureType { get; }
internal ValidationFailureType FailureType { get; }

/// <summary>
/// Gets the type of exception that occurred.
Expand All @@ -221,7 +228,7 @@ protected virtual void AddAdditionalInformation(Exception exception)
/// <summary>
/// Gets the message details that are used to generate the exception message.
/// </summary>
public MessageDetail MessageDetail { get; }
internal MessageDetail MessageDetail { get; }

/// <summary>
/// Gets the stack frames where the exception occurred.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,19 @@ public void Log()

#region Validated Properties
public ValidatedToken? ActorValidationResult { get; internal set; }

public string? ValidatedAudience { get; internal set; }

public ValidatedIssuer? ValidatedIssuer { get; internal set; }

public ValidatedLifetime? ValidatedLifetime { get; internal set; }

public DateTime? ValidatedTokenReplayExpirationTime { get; internal set; }

public ValidatedTokenType? ValidatedTokenType { get; internal set; }

public SecurityKey? ValidatedSigningKey { get; internal set; }

public ValidatedSigningKeyLifetime? ValidatedSigningKeyLifetime { get; internal set; }
#endregion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ public static partial class Validators
/// <param name="validationParameters">The <see cref="ValidationParameters"/> to be used for validating the token.</param>
/// <param name="configuration">The <see cref="BaseConfiguration"/> to be used for validation.</param>
/// <param name="callContext">The <see cref="CallContext"/> to be used for logging.</param>
/// <exception cref="ArgumentNullException"> if 'securityKey' is null and ValidateIssuerSigningKey is true.</exception>
/// <exception cref="ArgumentNullException"> if 'securityToken' is null and ValidateIssuerSigningKey is true.</exception>
/// <exception cref="ArgumentNullException"> if 'validationParameters' is null.</exception>
/// <exception cref="SecurityTokenArgumentNullException"> if 'securityKey' is null and ValidateIssuerSigningKey is true.</exception>
/// <exception cref="SecurityTokenArgumentNullException"> if 'securityToken' is null and ValidateIssuerSigningKey is true.</exception>
/// <exception cref="SecurityTokenArgumentNullException"> if 'validationParameters' is null.</exception>
internal static ValidationResult<ValidatedSigningKeyLifetime> ValidateIssuerSigningKey(
SecurityKey securityKey,
SecurityToken securityToken,
Expand All @@ -63,7 +63,7 @@ internal static ValidationResult<ValidatedSigningKeyLifetime> ValidateIssuerSign
return new ValidationError(
new MessageDetail(LogMessages.IDX10253, nameof(securityKey)),
ValidationFailureType.SigningKeyValidationFailed,
typeof(ArgumentNullException),
typeof(SecurityTokenArgumentNullException),
new StackFrame(true));

if (securityToken == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,23 +120,23 @@ public static TheoryData<TokenDecryptingTheoryData> JsonWebTokenHandlerDecryptTo
TestId = "Invalid_SecurityTokenIsNull",
Token = null,
ValidationParameters = new ValidationParameters(),
ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"),
ExpectedException = ExpectedException.SecurityTokenArgumentNullException("IDX10000:"),
Result = new ValidationError(
new MessageDetail(TokenLogMessages.IDX10000, "jwtToken"),
ValidationFailureType.NullArgument,
typeof(ArgumentNullException),
typeof(SecurityTokenArgumentNullException),
null),
},
new TokenDecryptingTheoryData
{
TestId = "Invalid_ValidationParametersIsNull",
Token = token,
ValidationParameters = null,
ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"),
ExpectedException = ExpectedException.SecurityTokenArgumentNullException("IDX10000:"),
Result = new ValidationError(
new MessageDetail(TokenLogMessages.IDX10000, "validationParameters"),
ValidationFailureType.NullArgument,
typeof(ArgumentNullException),
typeof(SecurityTokenArgumentNullException),
null),
},
new TokenDecryptingTheoryData
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,26 +71,26 @@ public static TheoryData<TokenReadingTheoryData> JsonWebTokenHandlerReadTokenTes
{
TestId = "Invalid_NullToken",
Token = null,
ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"),
ExpectedException = ExpectedException.SecurityTokenArgumentNullException("IDX10000:"),
Result = new ValidationError(
new MessageDetail(
TokenLogMessages.IDX10000,
LogHelper.MarkAsNonPII("token")),
ValidationFailureType.NullArgument,
typeof(ArgumentNullException),
typeof(SecurityTokenArgumentNullException),
null)
},
new TokenReadingTheoryData
{
TestId = "Invalid_EmptyToken",
Token = string.Empty,
ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"),
ExpectedException = ExpectedException.SecurityTokenArgumentNullException("IDX10000:"),
Result = new ValidationError(
new MessageDetail(
TokenLogMessages.IDX10000,
LogHelper.MarkAsNonPII("token")),
ValidationFailureType.NullArgument,
typeof(ArgumentNullException),
typeof(SecurityTokenArgumentNullException),
null)
},
new TokenReadingTheoryData
Expand Down
Loading
Loading