From e5eca688189978576a8e501da67bf9069e6f8dd1 Mon Sep 17 00:00:00 2001 From: brentschmaltz Date: Thu, 22 Sep 2022 10:21:07 -0700 Subject: [PATCH] Add Instance property bag for state that is cleared when Clone is called. --- .../TokenValidationParameters.cs | 18 +++++- .../TokenValidationParametersTests.cs | 56 ++++++++++++++++--- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs index d1f6dbcc06..77061bf75a 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs @@ -365,7 +365,10 @@ public TimeSpan ClockSkew /// This is a shallow Clone. public virtual TokenValidationParameters Clone() { - return new TokenValidationParameters(this); + return new(this) + { + IsClone = true + }; } /// @@ -451,6 +454,17 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken, /// public IssuerSigningKeyValidatorUsingConfiguration IssuerSigningKeyValidatorUsingConfiguration { get; set; } + /// + /// Gets a that is unique to this instance. + /// Calling will result in a new instance of this IDictionary. + /// + public IDictionary InstancePropertyBag { get; } = new Dictionary(); + + /// + /// Gets a value indicating if was called to obtain this instance. + /// + public bool IsClone { get; protected set; } = false; + /// /// Gets or sets the that is to be used for signature validation. /// @@ -565,7 +579,7 @@ public string NameClaimType /// /// Gets or sets the that contains a collection of custom key/value pairs. This allows addition of parameters that could be used in custom token validation scenarios. /// - public IDictionary PropertyBag { get; set; } + public IDictionary PropertyBag { get; set; } /// /// Gets or sets a boolean to control if configuration required to be refreshed before token validation. diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidationParametersTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidationParametersTests.cs index 98971edfd4..2ce625a6ff 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidationParametersTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidationParametersTests.cs @@ -10,6 +10,7 @@ using Microsoft.IdentityModel.Protocols.WsFederation; using Microsoft.IdentityModel.TestUtils; using Xunit; +using Xunit.Sdk; namespace Microsoft.IdentityModel.Tokens.Tests { @@ -21,8 +22,8 @@ public void Publics() TokenValidationParameters validationParameters = new TokenValidationParameters(); Type type = typeof(TokenValidationParameters); PropertyInfo[] properties = type.GetProperties(); - if (properties.Length != 54) - Assert.True(false, "Number of properties has changed from 54 to: " + properties.Length + ", adjust tests"); + if (properties.Length != 56) + Assert.True(false, "Number of properties has changed from 56 to: " + properties.Length + ", adjust tests"); TokenValidationParameters actorValidationParameters = new TokenValidationParameters(); SecurityKey issuerSigningKey = KeyingMaterial.DefaultX509Key_2048_Public; @@ -125,13 +126,27 @@ public void Publics() var compareContext = new CompareContext(); IdentityComparer.AreEqual(validationParametersInline, validationParametersSets, compareContext); - IdentityComparer.AreEqual(validationParametersInline.Clone() as TokenValidationParameters, validationParametersInline, compareContext); + + // only exlude 'IsClone' when comparing Clone vs. Original. + var instanceContext = new CompareContext(); + instanceContext.PropertiesToIgnoreWhenComparing.Add(typeof(TokenValidationParameters), new List { "IsClone" }); + TokenValidationParameters validationParametersInLineClone = validationParametersInline.Clone(); + IdentityComparer.AreEqual(validationParametersInLineClone, validationParametersInline, instanceContext); + if (!validationParametersInLineClone.IsClone) + instanceContext.AddDiff("!validationParametersInLineClone.IsClone)"); string id = Guid.NewGuid().ToString(); DerivedTokenValidationParameters derivedValidationParameters = new DerivedTokenValidationParameters(id, validationParametersInline); DerivedTokenValidationParameters derivedValidationParametersCloned = derivedValidationParameters.Clone() as DerivedTokenValidationParameters; - IdentityComparer.AreEqual(derivedValidationParameters, derivedValidationParametersCloned, compareContext); + IdentityComparer.AreEqual(derivedValidationParameters, derivedValidationParametersCloned, instanceContext); IdentityComparer.AreEqual(derivedValidationParameters.InternalString, derivedValidationParametersCloned.InternalString, compareContext); + if (!derivedValidationParametersCloned.IsClone) + instanceContext.AddDiff("!derivedValidationParametersCloned.IsClone)"); + + TokenValidationParameters tokenValidationParametersClone = validationParametersInline.Clone(); + IdentityComparer.AreEqual(tokenValidationParametersClone, tokenValidationParametersClone, instanceContext); + + compareContext.Merge(instanceContext); TestUtilities.AssertFailIfErrors(compareContext); } @@ -142,8 +157,8 @@ public void GetSets() TokenValidationParameters validationParameters = new TokenValidationParameters(); Type type = typeof(TokenValidationParameters); PropertyInfo[] properties = type.GetProperties(); - if (properties.Length != 54) - Assert.True(false, "Number of public fields has changed from 54 to: " + properties.Length + ", adjust tests"); + if (properties.Length != 56) + Assert.True(false, "Number of public fields has changed from 56 to: " + properties.Length + ", adjust tests"); GetSetContext context = new GetSetContext @@ -185,6 +200,31 @@ public void GetSets() Assert.Null(validationParameters.SignatureValidator); } + [Fact] + public void Clone() + { + object obj = new object(); + var compareContext = new CompareContext(); + + TokenValidationParameters validationParameters = new TokenValidationParameters(); + validationParameters.PropertyBag = new Dictionary { { "object", obj } }; + validationParameters.InstancePropertyBag["object"] = obj; + + compareContext.PropertiesToIgnoreWhenComparing.Add(typeof(TokenValidationParameters), new List { "InstancePropertyBag", "IsClone" }); + TokenValidationParameters validationParametersClone = validationParameters.Clone(); + IdentityComparer.AreEqual(validationParametersClone, validationParameters, compareContext); + if (validationParameters.IsClone) + compareContext.AddDiff("if (validationParameters.IsClone), IsCone should be false"); + + if (!validationParametersClone.IsClone) + compareContext.AddDiff("if (!validationParametersClone.IsClone), IsCone should be true"); + + if (validationParametersClone.InstancePropertyBag.Count != 0) + compareContext.AddDiff("validationParametersClone.InstancePropertyBag.Count != 0), should be empty."); + + TestUtilities.AssertFailIfErrors(compareContext); + } + class DerivedTokenValidationParameters : TokenValidationParameters { string _internalString; @@ -204,7 +244,9 @@ protected DerivedTokenValidationParameters(DerivedTokenValidationParameters othe public override TokenValidationParameters Clone() { - return new DerivedTokenValidationParameters(this); + DerivedTokenValidationParameters derivedTokenValidationParameters = new DerivedTokenValidationParameters(this); + derivedTokenValidationParameters.IsClone = true; + return derivedTokenValidationParameters; } } }