Switch to IOptionsMonitor (#1295)

This commit is contained in:
Hao Kung 2017-07-06 13:41:47 -07:00 committed by GitHub
parent fd502195a4
commit 23da476176
11 changed files with 76 additions and 22 deletions

View File

@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
private string _sessionKey; private string _sessionKey;
private Task<AuthenticateResult> _readCookieTask; private Task<AuthenticateResult> _readCookieTask;
public CookieAuthenticationHandler(IOptionsSnapshot<CookieAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) public CookieAuthenticationHandler(IOptionsMonitor<CookieAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) : base(options, logger, encoder, clock)
{ } { }

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
{ {
internal class FacebookHandler : OAuthHandler<FacebookOptions> internal class FacebookHandler : OAuthHandler<FacebookOptions>
{ {
public FacebookHandler(IOptionsSnapshot<FacebookOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) public FacebookHandler(IOptionsMonitor<FacebookOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) : base(options, logger, encoder, clock)
{ } { }

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Authentication.Google
{ {
internal class GoogleHandler : OAuthHandler<GoogleOptions> internal class GoogleHandler : OAuthHandler<GoogleOptions>
{ {
public GoogleHandler(IOptionsSnapshot<GoogleOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) public GoogleHandler(IOptionsMonitor<GoogleOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) : base(options, logger, encoder, clock)
{ } { }

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
private OpenIdConnectConfiguration _configuration; private OpenIdConnectConfiguration _configuration;
public JwtBearerHandler(IOptionsSnapshot<JwtBearerOptions> options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock) public JwtBearerHandler(IOptionsMonitor<JwtBearerOptions> options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock)
: base(options, logger, encoder, clock) : base(options, logger, encoder, clock)
{ } { }

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount
{ {
internal class MicrosoftAccountHandler : OAuthHandler<MicrosoftAccountOptions> internal class MicrosoftAccountHandler : OAuthHandler<MicrosoftAccountOptions>
{ {
public MicrosoftAccountHandler(IOptionsSnapshot<MicrosoftAccountOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) public MicrosoftAccountHandler(IOptionsMonitor<MicrosoftAccountOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) : base(options, logger, encoder, clock)
{ } { }

View File

@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
set { base.Events = value; } set { base.Events = value; }
} }
public OAuthHandler(IOptionsSnapshot<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) public OAuthHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) : base(options, logger, encoder, clock)
{ } { }

View File

@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
protected HtmlEncoder HtmlEncoder { get; } protected HtmlEncoder HtmlEncoder { get; }
public OpenIdConnectHandler(IOptionsSnapshot<OpenIdConnectOptions> options, ILoggerFactory logger, HtmlEncoder htmlEncoder, UrlEncoder encoder, ISystemClock clock) public OpenIdConnectHandler(IOptionsMonitor<OpenIdConnectOptions> options, ILoggerFactory logger, HtmlEncoder htmlEncoder, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) : base(options, logger, encoder, clock)
{ {
HtmlEncoder = htmlEncoder; HtmlEncoder = htmlEncoder;

View File

@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
set { base.Events = value; } set { base.Events = value; }
} }
public TwitterHandler(IOptionsSnapshot<TwitterOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) public TwitterHandler(IOptionsMonitor<TwitterOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) : base(options, logger, encoder, clock)
{ } { }

View File

@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Authentication
protected ISystemClock Clock { get; } protected ISystemClock Clock { get; }
protected IOptionsSnapshot<TOptions> OptionsSnapshot { get; } protected IOptionsMonitor<TOptions> OptionsMonitor { get; }
/// <summary> /// <summary>
/// The handler calls methods on the events which give the application control at certain points where processing is occurring. /// The handler calls methods on the events which give the application control at certain points where processing is occurring.
@ -58,12 +58,12 @@ namespace Microsoft.AspNetCore.Authentication
} }
} }
protected AuthenticationHandler(IOptionsSnapshot<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) protected AuthenticationHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
{ {
Logger = logger.CreateLogger(this.GetType().FullName); Logger = logger.CreateLogger(this.GetType().FullName);
UrlEncoder = encoder; UrlEncoder = encoder;
Clock = clock; Clock = clock;
OptionsSnapshot = options; OptionsMonitor = options;
} }
/// <summary> /// <summary>
@ -86,7 +86,7 @@ namespace Microsoft.AspNetCore.Authentication
Scheme = scheme; Scheme = scheme;
Context = context; Context = context;
Options = OptionsSnapshot.Get(Scheme.Name) ?? new TOptions(); Options = OptionsMonitor.Get(Scheme.Name) ?? new TOptions();
Options.Validate(); Options.Validate();
await InitializeEventsAsync(); await InitializeEventsAsync();

View File

@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Authentication
set { base.Events = value; } set { base.Events = value; }
} }
protected RemoteAuthenticationHandler(IOptionsSnapshot<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) protected RemoteAuthenticationHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) { } : base(options, logger, encoder, clock) { }
protected override Task<object> CreateEventsAsync() protected override Task<object> CreateEventsAsync()

View File

@ -18,6 +18,42 @@ namespace Microsoft.AspNetCore.Authentication
{ {
public class DynamicSchemeTests public class DynamicSchemeTests
{ {
[Fact]
public async Task OptionsAreConfiguredOnce()
{
var server = CreateServer(s =>
{
s.Configure<TestOptions>("One", o => o.Instance = new Singleton());
s.Configure<TestOptions>("Two", o => o.Instance = new Singleton());
});
// Add One scheme
var response = await server.CreateClient().GetAsync("http://example.com/add/One");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var transaction = await server.SendAsync("http://example.com/auth/One");
Assert.Equal("One", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "One"));
Assert.Equal("1", transaction.FindClaimValue("Count"));
// Verify option is not recreated
transaction = await server.SendAsync("http://example.com/auth/One");
Assert.Equal("One", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "One"));
Assert.Equal("1", transaction.FindClaimValue("Count"));
// Add Two scheme
response = await server.CreateClient().GetAsync("http://example.com/add/Two");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
transaction = await server.SendAsync("http://example.com/auth/Two");
Assert.Equal("Two", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "Two"));
Assert.Equal("2", transaction.FindClaimValue("Count"));
// Verify options are not recreated
transaction = await server.SendAsync("http://example.com/auth/One");
Assert.Equal("One", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "One"));
Assert.Equal("1", transaction.FindClaimValue("Count"));
transaction = await server.SendAsync("http://example.com/auth/Two");
Assert.Equal("Two", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "Two"));
Assert.Equal("2", transaction.FindClaimValue("Count"));
}
[Fact] [Fact]
public async Task CanAddAndRemoveSchemes() public async Task CanAddAndRemoveSchemes()
{ {
@ -48,7 +84,6 @@ namespace Microsoft.AspNetCore.Authentication
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
await Assert.ThrowsAsync<InvalidOperationException>(() => server.SendAsync("http://example.com/auth/Two")); await Assert.ThrowsAsync<InvalidOperationException>(() => server.SendAsync("http://example.com/auth/Two"));
await Assert.ThrowsAsync<InvalidOperationException>(() => server.SendAsync("http://example.com/auth/One")); await Assert.ThrowsAsync<InvalidOperationException>(() => server.SendAsync("http://example.com/auth/One"));
} }
[Fact] [Fact]
@ -69,9 +104,27 @@ namespace Microsoft.AspNetCore.Authentication
await Assert.ThrowsAsync<InvalidOperationException>(() => server.SendAsync("http://example.com/auth")); await Assert.ThrowsAsync<InvalidOperationException>(() => server.SendAsync("http://example.com/auth"));
} }
private class TestHandler : AuthenticationHandler<AuthenticationSchemeOptions> public class TestOptions : AuthenticationSchemeOptions
{ {
public TestHandler(IOptionsSnapshot<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) public Singleton Instance { get; set; }
}
public class Singleton
{
public static int _count;
public Singleton()
{
_count++;
Count = _count;
}
public int Count { get; }
}
private class TestHandler : AuthenticationHandler<TestOptions>
{
public TestHandler(IOptionsMonitor<TestOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{ {
} }
@ -80,12 +133,16 @@ namespace Microsoft.AspNetCore.Authentication
var principal = new ClaimsPrincipal(); var principal = new ClaimsPrincipal();
var id = new ClaimsIdentity(); var id = new ClaimsIdentity();
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, Scheme.Name, ClaimValueTypes.String, Scheme.Name)); id.AddClaim(new Claim(ClaimTypes.NameIdentifier, Scheme.Name, ClaimValueTypes.String, Scheme.Name));
if (Options.Instance != null)
{
id.AddClaim(new Claim("Count", Options.Instance.Count.ToString()));
}
principal.AddIdentity(id); principal.AddIdentity(id);
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name))); return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name)));
} }
} }
private static TestServer CreateServer(Action<AuthenticationOptions> configureAuth = null) private static TestServer CreateServer(Action<IServiceCollection> configureServices = null)
{ {
var builder = new WebHostBuilder() var builder = new WebHostBuilder()
.Configure(app => .Configure(app =>
@ -122,11 +179,8 @@ namespace Microsoft.AspNetCore.Authentication
}) })
.ConfigureServices(services => .ConfigureServices(services =>
{ {
if (configureAuth == null) configureServices?.Invoke(services);
{ services.AddAuthentication();
configureAuth = o => { };
}
services.AddAuthentication(configureAuth);
}); });
return new TestServer(builder); return new TestServer(builder);
} }