From 23da47617624cfed065cd1cdd552d34e5ea5b821 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Thu, 6 Jul 2017 13:41:47 -0700 Subject: [PATCH] Switch to IOptionsMonitor (#1295) --- .../CookieAuthenticationHandler.cs | 2 +- .../FacebookHandler.cs | 2 +- .../GoogleHandler.cs | 2 +- .../JwtBearerHandler.cs | 2 +- .../MicrosoftAccountHandler.cs | 2 +- .../OAuthHandler.cs | 2 +- .../OpenIdConnectHandler.cs | 2 +- .../TwitterHandler.cs | 2 +- .../AuthenticationHandler.cs | 8 +-- .../RemoteAuthenticationHandler.cs | 2 +- .../DynamicSchemeTests.cs | 72 ++++++++++++++++--- 11 files changed, 76 insertions(+), 22 deletions(-) diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs index f411997520..996c334a73 100644 --- a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs @@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies private string _sessionKey; private Task _readCookieTask; - public CookieAuthenticationHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + public CookieAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookHandler.cs b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookHandler.cs index 8a1f29bbe5..9004fc09cd 100644 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookHandler.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Authentication.Facebook { internal class FacebookHandler : OAuthHandler { - public FacebookHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + public FacebookHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } diff --git a/src/Microsoft.AspNetCore.Authentication.Google/GoogleHandler.cs b/src/Microsoft.AspNetCore.Authentication.Google/GoogleHandler.cs index aa5e596494..7a2e1a2d14 100644 --- a/src/Microsoft.AspNetCore.Authentication.Google/GoogleHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Google/GoogleHandler.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Authentication.Google { internal class GoogleHandler : OAuthHandler { - public GoogleHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + public GoogleHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs index 1cb1ea4496..9cf73182ba 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs @@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer { private OpenIdConnectConfiguration _configuration; - public JwtBearerHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock) + public JwtBearerHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock) : base(options, logger, encoder, clock) { } diff --git a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountHandler.cs b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountHandler.cs index 45fae3d0ea..8204bf07b9 100644 --- a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountHandler.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount { internal class MicrosoftAccountHandler : OAuthHandler { - public MicrosoftAccountHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + public MicrosoftAccountHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs index b61d575375..007d7dbefd 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs @@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth set { base.Events = value; } } - public OAuthHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + public OAuthHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs index 068593837b..65e1e1951e 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs @@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect protected HtmlEncoder HtmlEncoder { get; } - public OpenIdConnectHandler(IOptionsSnapshot options, ILoggerFactory logger, HtmlEncoder htmlEncoder, UrlEncoder encoder, ISystemClock clock) + public OpenIdConnectHandler(IOptionsMonitor options, ILoggerFactory logger, HtmlEncoder htmlEncoder, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { HtmlEncoder = htmlEncoder; diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs index ddcd095d5b..1e1dd08d87 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs @@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Authentication.Twitter set { base.Events = value; } } - public TwitterHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + public TwitterHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } diff --git a/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs index a258d0acaf..d1d09f0746 100644 --- a/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs @@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Authentication protected ISystemClock Clock { get; } - protected IOptionsSnapshot OptionsSnapshot { get; } + protected IOptionsMonitor OptionsMonitor { get; } /// /// 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 options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + protected AuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) { Logger = logger.CreateLogger(this.GetType().FullName); UrlEncoder = encoder; Clock = clock; - OptionsSnapshot = options; + OptionsMonitor = options; } /// @@ -86,7 +86,7 @@ namespace Microsoft.AspNetCore.Authentication Scheme = scheme; Context = context; - Options = OptionsSnapshot.Get(Scheme.Name) ?? new TOptions(); + Options = OptionsMonitor.Get(Scheme.Name) ?? new TOptions(); Options.Validate(); await InitializeEventsAsync(); diff --git a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs index 7bd3b07731..bcd5983642 100644 --- a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs @@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Authentication set { base.Events = value; } } - protected RemoteAuthenticationHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + protected RemoteAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override Task CreateEventsAsync() diff --git a/test/Microsoft.AspNetCore.Authentication.Test/DynamicSchemeTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/DynamicSchemeTests.cs index d239d85f81..fe7443679c 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/DynamicSchemeTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/DynamicSchemeTests.cs @@ -18,6 +18,42 @@ namespace Microsoft.AspNetCore.Authentication { public class DynamicSchemeTests { + [Fact] + public async Task OptionsAreConfiguredOnce() + { + var server = CreateServer(s => + { + s.Configure("One", o => o.Instance = new Singleton()); + s.Configure("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] public async Task CanAddAndRemoveSchemes() { @@ -48,7 +84,6 @@ namespace Microsoft.AspNetCore.Authentication Assert.Equal(HttpStatusCode.OK, response.StatusCode); await Assert.ThrowsAsync(() => server.SendAsync("http://example.com/auth/Two")); await Assert.ThrowsAsync(() => server.SendAsync("http://example.com/auth/One")); - } [Fact] @@ -69,9 +104,27 @@ namespace Microsoft.AspNetCore.Authentication await Assert.ThrowsAsync(() => server.SendAsync("http://example.com/auth")); } - private class TestHandler : AuthenticationHandler + public class TestOptions : AuthenticationSchemeOptions { - public TestHandler(IOptionsSnapshot 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 + { + public TestHandler(IOptionsMonitor 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 id = new ClaimsIdentity(); 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); return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name))); } } - private static TestServer CreateServer(Action configureAuth = null) + private static TestServer CreateServer(Action configureServices = null) { var builder = new WebHostBuilder() .Configure(app => @@ -122,11 +179,8 @@ namespace Microsoft.AspNetCore.Authentication }) .ConfigureServices(services => { - if (configureAuth == null) - { - configureAuth = o => { }; - } - services.AddAuthentication(configureAuth); + configureServices?.Invoke(services); + services.AddAuthentication(); }); return new TestServer(builder); }