Added protected ctor to AuthenticationSchemeProvider (#990)

This commit is contained in:
Kristian Hellang 2018-02-05 22:43:55 +01:00 committed by Hao Kung
parent 51a7e83a23
commit 450900d14c
2 changed files with 70 additions and 12 deletions

View File

@ -15,13 +15,28 @@ namespace Microsoft.AspNetCore.Authentication
public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider
{ {
/// <summary> /// <summary>
/// Constructor. /// Creates an instance of <see cref="AuthenticationSchemeProvider"/>
/// using the specified <paramref name="options"/>,
/// </summary> /// </summary>
/// <param name="options">The <see cref="AuthenticationOptions"/> options.</param> /// <param name="options">The <see cref="AuthenticationOptions"/> options.</param>
public AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options) public AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options)
: this(options, new Dictionary<string, AuthenticationScheme>(StringComparer.Ordinal))
{
}
/// <summary>
/// Creates an instance of <see cref="AuthenticationSchemeProvider"/>
/// using the specified <paramref name="options"/> and <paramref name="schemes"/>.
/// </summary>
/// <param name="options">The <see cref="AuthenticationOptions"/> options.</param>
/// <param name="schemes">The dictionary used to store authentication schemes.</param>
protected AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options, IDictionary<string, AuthenticationScheme> schemes)
{ {
_options = options.Value; _options = options.Value;
_schemes = schemes ?? throw new ArgumentNullException(nameof(schemes));
_requestHandlers = new List<AuthenticationScheme>();
foreach (var builder in _options.Schemes) foreach (var builder in _options.Schemes)
{ {
var scheme = builder.Build(); var scheme = builder.Build();
@ -32,8 +47,8 @@ namespace Microsoft.AspNetCore.Authentication
private readonly AuthenticationOptions _options; private readonly AuthenticationOptions _options;
private readonly object _lock = new object(); private readonly object _lock = new object();
private IDictionary<string, AuthenticationScheme> _map = new Dictionary<string, AuthenticationScheme>(StringComparer.Ordinal); private readonly IDictionary<string, AuthenticationScheme> _schemes;
private List<AuthenticationScheme> _requestHandlers = new List<AuthenticationScheme>(); private readonly List<AuthenticationScheme> _requestHandlers;
private Task<AuthenticationScheme> GetDefaultSchemeAsync() private Task<AuthenticationScheme> GetDefaultSchemeAsync()
=> _options.DefaultScheme != null => _options.DefaultScheme != null
@ -101,7 +116,7 @@ namespace Microsoft.AspNetCore.Authentication
/// <param name="name">The name of the authenticationScheme.</param> /// <param name="name">The name of the authenticationScheme.</param>
/// <returns>The scheme or null if not found.</returns> /// <returns>The scheme or null if not found.</returns>
public virtual Task<AuthenticationScheme> GetSchemeAsync(string name) public virtual Task<AuthenticationScheme> GetSchemeAsync(string name)
=> Task.FromResult(_map.ContainsKey(name) ? _map[name] : null); => Task.FromResult(_schemes.ContainsKey(name) ? _schemes[name] : null);
/// <summary> /// <summary>
/// Returns the schemes in priority order for request handling. /// Returns the schemes in priority order for request handling.
@ -116,13 +131,13 @@ namespace Microsoft.AspNetCore.Authentication
/// <param name="scheme">The scheme.</param> /// <param name="scheme">The scheme.</param>
public virtual void AddScheme(AuthenticationScheme scheme) public virtual void AddScheme(AuthenticationScheme scheme)
{ {
if (_map.ContainsKey(scheme.Name)) if (_schemes.ContainsKey(scheme.Name))
{ {
throw new InvalidOperationException("Scheme already exists: " + scheme.Name); throw new InvalidOperationException("Scheme already exists: " + scheme.Name);
} }
lock (_lock) lock (_lock)
{ {
if (_map.ContainsKey(scheme.Name)) if (_schemes.ContainsKey(scheme.Name))
{ {
throw new InvalidOperationException("Scheme already exists: " + scheme.Name); throw new InvalidOperationException("Scheme already exists: " + scheme.Name);
} }
@ -130,7 +145,7 @@ namespace Microsoft.AspNetCore.Authentication
{ {
_requestHandlers.Add(scheme); _requestHandlers.Add(scheme);
} }
_map[scheme.Name] = scheme; _schemes[scheme.Name] = scheme;
} }
} }
@ -140,22 +155,22 @@ namespace Microsoft.AspNetCore.Authentication
/// <param name="name">The name of the authenticationScheme being removed.</param> /// <param name="name">The name of the authenticationScheme being removed.</param>
public virtual void RemoveScheme(string name) public virtual void RemoveScheme(string name)
{ {
if (!_map.ContainsKey(name)) if (!_schemes.ContainsKey(name))
{ {
return; return;
} }
lock (_lock) lock (_lock)
{ {
if (_map.ContainsKey(name)) if (_schemes.ContainsKey(name))
{ {
var scheme = _map[name]; var scheme = _schemes[name];
_requestHandlers.Remove(scheme); _requestHandlers.Remove(scheme);
_map.Remove(name); _schemes.Remove(name);
} }
} }
} }
public virtual Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync() public virtual Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync()
=> Task.FromResult<IEnumerable<AuthenticationScheme>>(_map.Values); => Task.FromResult<IEnumerable<AuthenticationScheme>>(_schemes.Values);
} }
} }

View File

@ -3,10 +3,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Authentication namespace Microsoft.AspNetCore.Authentication
@ -117,6 +119,39 @@ namespace Microsoft.AspNetCore.Authentication
Assert.NotNull(await provider.GetDefaultSignOutSchemeAsync()); Assert.NotNull(await provider.GetDefaultSignOutSchemeAsync());
} }
[Fact]
public void SchemeRegistrationIsCaseSensitive()
{
var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o =>
{
o.AddScheme<Handler>("signin", "whatever");
o.AddScheme<Handler>("signin", "whatever");
}).BuildServiceProvider();
var error = Assert.Throws<InvalidOperationException>(() => services.GetRequiredService<IAuthenticationSchemeProvider>());
Assert.Contains("Scheme already exists: signin", error.Message);
}
[Fact]
public async Task LookupUsesProvidedStringComparer()
{
var services = new ServiceCollection().AddOptions()
.AddSingleton<IAuthenticationSchemeProvider, IgnoreCaseSchemeProvider>()
.AddAuthenticationCore(o => o.AddScheme<Handler>("signin", "whatever"))
.BuildServiceProvider();
var provider = services.GetRequiredService<IAuthenticationSchemeProvider>();
var a = await provider.GetSchemeAsync("signin");
var b = await provider.GetSchemeAsync("SignIn");
var c = await provider.GetSchemeAsync("SIGNIN");
Assert.NotNull(a);
Assert.Same(a, b);
Assert.Same(b, c);
}
private class Handler : IAuthenticationHandler private class Handler : IAuthenticationHandler
{ {
public Task<AuthenticateResult> AuthenticateAsync() public Task<AuthenticateResult> AuthenticateAsync()
@ -160,5 +195,13 @@ namespace Microsoft.AspNetCore.Authentication
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
private class IgnoreCaseSchemeProvider : AuthenticationSchemeProvider
{
public IgnoreCaseSchemeProvider(IOptions<AuthenticationOptions> options)
: base(options, new Dictionary<string, AuthenticationScheme>(StringComparer.OrdinalIgnoreCase))
{
}
}
} }
} }