Skip to content

Commit

Permalink
Add Instance property bag for state that is cleared when Clone is cal…
Browse files Browse the repository at this point in the history
…led.
  • Loading branch information
brentschmaltz committed Oct 27, 2022
1 parent b7a4fdd commit e5eca68
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 9 deletions.
18 changes: 16 additions & 2 deletions src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,10 @@ public TimeSpan ClockSkew
/// <remarks>This is a shallow Clone.</remarks>
public virtual TokenValidationParameters Clone()
{
return new TokenValidationParameters(this);
return new(this)
{
IsClone = true
};
}

/// <summary>
Expand Down Expand Up @@ -451,6 +454,17 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken,
/// </remarks>
public IssuerSigningKeyValidatorUsingConfiguration IssuerSigningKeyValidatorUsingConfiguration { get; set; }

/// <summary>
/// Gets a <see cref="IDictionary{String, Object}"/> that is unique to this instance.
/// Calling <see cref="Clone"/> will result in a new instance of this IDictionary.
/// </summary>
public IDictionary<string, object> InstancePropertyBag { get; } = new Dictionary<string, object>();

/// <summary>
/// Gets a value indicating if <see cref="Clone"/> was called to obtain this instance.
/// </summary>
public bool IsClone { get; protected set; } = false;

/// <summary>
/// Gets or sets the <see cref="SecurityKey"/> that is to be used for signature validation.
/// </summary>
Expand Down Expand Up @@ -565,7 +579,7 @@ public string NameClaimType
/// <summary>
/// Gets or sets the <see cref="IDictionary{String, Object}"/> that contains a collection of custom key/value pairs. This allows addition of parameters that could be used in custom token validation scenarios.
/// </summary>
public IDictionary<string, Object> PropertyBag { get; set; }
public IDictionary<string, object> PropertyBag { get; set; }

/// <summary>
/// Gets or sets a boolean to control if configuration required to be refreshed before token validation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.IdentityModel.Protocols.WsFederation;
using Microsoft.IdentityModel.TestUtils;
using Xunit;
using Xunit.Sdk;

namespace Microsoft.IdentityModel.Tokens.Tests
{
Expand All @@ -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;
Expand Down Expand Up @@ -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<string> { "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);
}
Expand All @@ -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
Expand Down Expand Up @@ -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<string, object> { { "object", obj } };
validationParameters.InstancePropertyBag["object"] = obj;

compareContext.PropertiesToIgnoreWhenComparing.Add(typeof(TokenValidationParameters), new List<string> { "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;
Expand All @@ -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;
}
}
}
Expand Down

0 comments on commit e5eca68

Please sign in to comment.