Add AuthenticationProperties.Parameters (#1008)
Add a `Parameters` bag to the authentication properties that allow passing arbitrary parameters to an authentication handler. These values are not intended for serialization of persistence, only for flowing data between call sites. Also make existing `Items` collection helpers protected to allow them to be reused in subclasses, make string-based helpers public as a public way to work with the collection, and add helper methods to interact with the `Parameters` dictionary.
This commit is contained in:
parent
f91db5b794
commit
72210e4078
|
|
@ -20,20 +20,29 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
internal const string UtcDateTimeFormat = "r";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuthenticationProperties"/> class
|
||||
/// Initializes a new instance of the <see cref="AuthenticationProperties"/> class.
|
||||
/// </summary>
|
||||
public AuthenticationProperties()
|
||||
: this(items: null)
|
||||
{
|
||||
}
|
||||
: this(items: null, parameters: null)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuthenticationProperties"/> class
|
||||
/// Initializes a new instance of the <see cref="AuthenticationProperties"/> class.
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
/// <param name="items">State values dictionary to use.</param>
|
||||
public AuthenticationProperties(IDictionary<string, string> items)
|
||||
: this(items, parameters: null)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuthenticationProperties"/> class.
|
||||
/// </summary>
|
||||
/// <param name="items">State values dictionary to use.</param>
|
||||
/// <param name="parameters">Parameters dictionary to use.</param>
|
||||
public AuthenticationProperties(IDictionary<string, string> items, IDictionary<string, object> parameters)
|
||||
{
|
||||
Items = items ?? new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
Parameters = parameters ?? new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -41,6 +50,12 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
/// </summary>
|
||||
public IDictionary<string, string> Items { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Collection of parameters that are passed to the authentication handler. These are not intended for
|
||||
/// serialization or persistence, only for flowing data between call sites.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> Parameters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the authentication session is persisted across multiple requests.
|
||||
/// </summary>
|
||||
|
|
@ -86,12 +101,22 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
set => SetBool(RefreshKey, value);
|
||||
}
|
||||
|
||||
private string GetString(string key)
|
||||
/// <summary>
|
||||
/// Get a string value from the <see cref="Items"/> collection.
|
||||
/// </summary>
|
||||
/// <param name="key">Property key.</param>
|
||||
/// <returns>Retrieved value or <c>null</c> if the property is not set.</returns>
|
||||
public string GetString(string key)
|
||||
{
|
||||
return Items.TryGetValue(key, out string value) ? value : null;
|
||||
}
|
||||
|
||||
private void SetString(string key, string value)
|
||||
/// <summary>
|
||||
/// Set a string value in the <see cref="Items"/> collection.
|
||||
/// </summary>
|
||||
/// <param name="key">Property key.</param>
|
||||
/// <param name="value">Value to set or <c>null</c> to remove the property.</param>
|
||||
public void SetString(string key, string value)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
|
|
@ -103,16 +128,44 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
}
|
||||
}
|
||||
|
||||
private bool? GetBool(string key)
|
||||
/// <summary>
|
||||
/// Get a parameter from the <see cref="Parameters"/> collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Parameter type.</typeparam>
|
||||
/// <param name="key">Parameter key.</param>
|
||||
/// <returns>Retrieved value or the default value if the property is not set.</returns>
|
||||
public T GetParameter<T>(string key)
|
||||
=> Parameters.TryGetValue(key, out var obj) && obj is T value ? value : default;
|
||||
|
||||
/// <summary>
|
||||
/// Set a parameter value in the <see cref="Parameters"/> collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Parameter type.</typeparam>
|
||||
/// <param name="key">Parameter key.</param>
|
||||
/// <param name="value">Value to set.</param>
|
||||
public void SetParameter<T>(string key, T value)
|
||||
=> Parameters[key] = value;
|
||||
|
||||
/// <summary>
|
||||
/// Get a bool value from the <see cref="Items"/> collection.
|
||||
/// </summary>
|
||||
/// <param name="key">Property key.</param>
|
||||
/// <returns>Retrieved value or <c>null</c> if the property is not set.</returns>
|
||||
protected bool? GetBool(string key)
|
||||
{
|
||||
if (Items.TryGetValue(key, out string value) && bool.TryParse(value, out bool refresh))
|
||||
if (Items.TryGetValue(key, out string value) && bool.TryParse(value, out bool boolValue))
|
||||
{
|
||||
return refresh;
|
||||
return boolValue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SetBool(string key, bool? value)
|
||||
/// <summary>
|
||||
/// Set a bool value in the <see cref="Items"/> collection.
|
||||
/// </summary>
|
||||
/// <param name="key">Property key.</param>
|
||||
/// <param name="value">Value to set or <c>null</c> to remove the property.</param>
|
||||
protected void SetBool(string key, bool? value)
|
||||
{
|
||||
if (value.HasValue)
|
||||
{
|
||||
|
|
@ -124,7 +177,12 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
}
|
||||
}
|
||||
|
||||
private DateTimeOffset? GetDateTimeOffset(string key)
|
||||
/// <summary>
|
||||
/// Get a DateTimeOffset value from the <see cref="Items"/> collection.
|
||||
/// </summary>
|
||||
/// <param name="key">Property key.</param>
|
||||
/// <returns>Retrieved value or <c>null</c> if the property is not set.</returns>
|
||||
protected DateTimeOffset? GetDateTimeOffset(string key)
|
||||
{
|
||||
if (Items.TryGetValue(key, out string value)
|
||||
&& DateTimeOffset.TryParseExact(value, UtcDateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTimeOffset dateTimeOffset))
|
||||
|
|
@ -134,7 +192,12 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
return null;
|
||||
}
|
||||
|
||||
private void SetDateTimeOffset(string key, DateTimeOffset? value)
|
||||
/// <summary>
|
||||
/// Set a DateTimeOffset value in the <see cref="Items"/> collection.
|
||||
/// </summary>
|
||||
/// <param name="key">Property key.</param>
|
||||
/// <param name="value">Value to set or <c>null</c> to remove the property.</param>
|
||||
protected void SetDateTimeOffset(string key, DateTimeOffset? value)
|
||||
{
|
||||
if (value.HasValue)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,207 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication.Core.Test
|
||||
{
|
||||
public class AuthenticationPropertiesTests
|
||||
{
|
||||
[Fact]
|
||||
public void DefaultConstructor_EmptyCollections()
|
||||
{
|
||||
var props = new AuthenticationProperties();
|
||||
Assert.Empty(props.Items);
|
||||
Assert.Empty(props.Parameters);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItemsConstructor_ReusesItemsDictionary()
|
||||
{
|
||||
var items = new Dictionary<string, string>
|
||||
{
|
||||
["foo"] = "bar",
|
||||
};
|
||||
var props = new AuthenticationProperties(items);
|
||||
Assert.Same(items, props.Items);
|
||||
Assert.Empty(props.Parameters);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FullConstructor_ReusesDictionaries()
|
||||
{
|
||||
var items = new Dictionary<string, string>
|
||||
{
|
||||
["foo"] = "bar",
|
||||
};
|
||||
var parameters = new Dictionary<string, object>
|
||||
{
|
||||
["number"] = 1234,
|
||||
["list"] = new List<string> { "a", "b", "c" },
|
||||
};
|
||||
var props = new AuthenticationProperties(items, parameters);
|
||||
Assert.Same(items, props.Items);
|
||||
Assert.Same(parameters, props.Parameters);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetSetString()
|
||||
{
|
||||
var props = new AuthenticationProperties();
|
||||
Assert.Null(props.GetString("foo"));
|
||||
Assert.Equal(0, props.Items.Count);
|
||||
|
||||
props.SetString("foo", "foo bar");
|
||||
Assert.Equal("foo bar", props.GetString("foo"));
|
||||
Assert.Equal("foo bar", props.Items["foo"]);
|
||||
Assert.Equal(1, props.Items.Count);
|
||||
|
||||
props.SetString("foo", "foo baz");
|
||||
Assert.Equal("foo baz", props.GetString("foo"));
|
||||
Assert.Equal("foo baz", props.Items["foo"]);
|
||||
Assert.Equal(1, props.Items.Count);
|
||||
|
||||
props.SetString("bar", "xy");
|
||||
Assert.Equal("xy", props.GetString("bar"));
|
||||
Assert.Equal("xy", props.Items["bar"]);
|
||||
Assert.Equal(2, props.Items.Count);
|
||||
|
||||
props.SetString("bar", string.Empty);
|
||||
Assert.Equal(string.Empty, props.GetString("bar"));
|
||||
Assert.Equal(string.Empty, props.Items["bar"]);
|
||||
|
||||
props.SetString("foo", null);
|
||||
Assert.Null(props.GetString("foo"));
|
||||
Assert.Equal(1, props.Items.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetSetParameter_String()
|
||||
{
|
||||
var props = new AuthenticationProperties();
|
||||
Assert.Null(props.GetParameter<string>("foo"));
|
||||
Assert.Equal(0, props.Parameters.Count);
|
||||
|
||||
props.SetParameter<string>("foo", "foo bar");
|
||||
Assert.Equal("foo bar", props.GetParameter<string>("foo"));
|
||||
Assert.Equal("foo bar", props.Parameters["foo"]);
|
||||
Assert.Equal(1, props.Parameters.Count);
|
||||
|
||||
props.SetParameter<string>("foo", null);
|
||||
Assert.Null(props.GetParameter<string>("foo"));
|
||||
Assert.Null(props.Parameters["foo"]);
|
||||
Assert.Equal(1, props.Parameters.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetSetParameter_Int()
|
||||
{
|
||||
var props = new AuthenticationProperties();
|
||||
Assert.Null(props.GetParameter<int?>("foo"));
|
||||
Assert.Equal(0, props.Parameters.Count);
|
||||
|
||||
props.SetParameter<int?>("foo", 123);
|
||||
Assert.Equal(123, props.GetParameter<int?>("foo"));
|
||||
Assert.Equal(123, props.Parameters["foo"]);
|
||||
Assert.Equal(1, props.Parameters.Count);
|
||||
|
||||
props.SetParameter<int?>("foo", null);
|
||||
Assert.Null(props.GetParameter<int?>("foo"));
|
||||
Assert.Null(props.Parameters["foo"]);
|
||||
Assert.Equal(1, props.Parameters.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetSetParameter_Collection()
|
||||
{
|
||||
var props = new AuthenticationProperties();
|
||||
Assert.Null(props.GetParameter<int?>("foo"));
|
||||
Assert.Equal(0, props.Parameters.Count);
|
||||
|
||||
var list = new string[] { "a", "b", "c" };
|
||||
props.SetParameter<ICollection<string>>("foo", list);
|
||||
Assert.Equal(new string[] { "a", "b", "c" }, props.GetParameter<ICollection<string>>("foo"));
|
||||
Assert.Same(list, props.Parameters["foo"]);
|
||||
Assert.Equal(1, props.Parameters.Count);
|
||||
|
||||
props.SetParameter<ICollection<string>>("foo", null);
|
||||
Assert.Null(props.GetParameter<ICollection<string>>("foo"));
|
||||
Assert.Null(props.Parameters["foo"]);
|
||||
Assert.Equal(1, props.Parameters.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsPersistent_Test()
|
||||
{
|
||||
var props = new AuthenticationProperties();
|
||||
Assert.False(props.IsPersistent);
|
||||
|
||||
props.IsPersistent = true;
|
||||
Assert.True(props.IsPersistent);
|
||||
Assert.Equal(string.Empty, props.Items.First().Value);
|
||||
|
||||
props.Items.Clear();
|
||||
Assert.False(props.IsPersistent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RedirectUri_Test()
|
||||
{
|
||||
var props = new AuthenticationProperties();
|
||||
Assert.Null(props.RedirectUri);
|
||||
|
||||
props.RedirectUri = "http://example.com";
|
||||
Assert.Equal("http://example.com", props.RedirectUri);
|
||||
Assert.Equal("http://example.com", props.Items.First().Value);
|
||||
|
||||
props.Items.Clear();
|
||||
Assert.Null(props.RedirectUri);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IssuedUtc_Test()
|
||||
{
|
||||
var props = new AuthenticationProperties();
|
||||
Assert.Null(props.IssuedUtc);
|
||||
|
||||
props.IssuedUtc = new DateTimeOffset(new DateTime(2018, 03, 21, 0, 0, 0, DateTimeKind.Utc));
|
||||
Assert.Equal(new DateTimeOffset(new DateTime(2018, 03, 21, 0, 0, 0, DateTimeKind.Utc)), props.IssuedUtc);
|
||||
Assert.Equal("Wed, 21 Mar 2018 00:00:00 GMT", props.Items.First().Value);
|
||||
|
||||
props.Items.Clear();
|
||||
Assert.Null(props.IssuedUtc);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpiresUtc_Test()
|
||||
{
|
||||
var props = new AuthenticationProperties();
|
||||
Assert.Null(props.ExpiresUtc);
|
||||
|
||||
props.ExpiresUtc = new DateTimeOffset(new DateTime(2018, 03, 19, 12, 34, 56, DateTimeKind.Utc));
|
||||
Assert.Equal(new DateTimeOffset(new DateTime(2018, 03, 19, 12, 34, 56, DateTimeKind.Utc)), props.ExpiresUtc);
|
||||
Assert.Equal("Mon, 19 Mar 2018 12:34:56 GMT", props.Items.First().Value);
|
||||
|
||||
props.Items.Clear();
|
||||
Assert.Null(props.ExpiresUtc);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllowRefresh_Test()
|
||||
{
|
||||
var props = new AuthenticationProperties();
|
||||
Assert.Null(props.AllowRefresh);
|
||||
|
||||
props.AllowRefresh = true;
|
||||
Assert.True(props.AllowRefresh);
|
||||
Assert.Equal("True", props.Items.First().Value);
|
||||
|
||||
props.AllowRefresh = false;
|
||||
Assert.False(props.AllowRefresh);
|
||||
Assert.Equal("False", props.Items.First().Value);
|
||||
|
||||
props.Items.Clear();
|
||||
Assert.Null(props.AllowRefresh);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue