Rename VirtualScheme => PolicyScheme (#1665)

* VirtualScheme => PolicyScheme

* Use SignInHandler base for cookies

* PolicySchemeHandlers throw NotImplemented by default

* Remove redundant interface
This commit is contained in:
Hao Kung 2018-03-13 11:09:43 -07:00 committed by GitHub
parent 4103d47f09
commit bee77ebf91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 166 additions and 198 deletions

View File

@ -14,10 +14,7 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Authentication.Cookies namespace Microsoft.AspNetCore.Authentication.Cookies
{ {
public class CookieAuthenticationHandler : public class CookieAuthenticationHandler : SignInAuthenticationHandler<CookieAuthenticationOptions>
AuthenticationHandler<CookieAuthenticationOptions>,
IAuthenticationSignInHandler,
IAuthenticationSignOutHandler
{ {
private const string HeaderValueNoCache = "no-cache"; private const string HeaderValueNoCache = "no-cache";
private const string HeaderValueEpocDate = "Thu, 01 Jan 1970 00:00:00 GMT"; private const string HeaderValueEpocDate = "Thu, 01 Jan 1970 00:00:00 GMT";
@ -252,20 +249,13 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
} }
} }
public async virtual Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) protected async override Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
{ {
if (user == null) if (user == null)
{ {
throw new ArgumentNullException(nameof(user)); throw new ArgumentNullException(nameof(user));
} }
var target = ResolveTarget(Options.ForwardSignIn);
if (target != null)
{
await Context.SignInAsync(target, user, properties);
return;
}
properties = properties ?? new AuthenticationProperties(); properties = properties ?? new AuthenticationProperties();
_signInCalled = true; _signInCalled = true;
@ -346,15 +336,8 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
Logger.SignedIn(Scheme.Name); Logger.SignedIn(Scheme.Name);
} }
public async virtual Task SignOutAsync(AuthenticationProperties properties) protected async override Task HandleSignOutAsync(AuthenticationProperties properties)
{ {
var target = ResolveTarget(Options.ForwardSignOut);
if (target != null)
{
await Context.SignOutAsync(target, properties);
return;
}
properties = properties ?? new AuthenticationProperties(); properties = properties ?? new AuthenticationProperties();
_signOutCalled = true; _signOutCalled = true;

View File

@ -0,0 +1,6 @@
[
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler : Microsoft.AspNetCore.Authentication.AuthenticationHandler<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>, Microsoft.AspNetCore.Authentication.IAuthenticationSignInHandler",
"Kind": "Removal"
}
]

View File

@ -91,15 +91,15 @@ namespace Microsoft.AspNetCore.Authentication
} }
/// <summary> /// <summary>
/// Adds a <see cref="VirtualAuthenticationHandler"/> based authentication handler which can be used to /// Adds a <see cref="PolicySchemeHandler"/> based authentication handler which can be used to
/// redirect to other authentication schemes. /// redirect to other authentication schemes.
/// </summary> /// </summary>
/// <param name="authenticationScheme">The name of this scheme.</param> /// <param name="authenticationScheme">The name of this scheme.</param>
/// <param name="displayName">The display name of this scheme.</param> /// <param name="displayName">The display name of this scheme.</param>
/// <param name="configureOptions">Used to configure the scheme options.</param> /// <param name="configureOptions">Used to configure the scheme options.</param>
/// <returns>The builder.</returns> /// <returns>The builder.</returns>
public virtual AuthenticationBuilder AddVirtualScheme(string authenticationScheme, string displayName, Action<VirtualSchemeOptions> configureOptions) public virtual AuthenticationBuilder AddPolicyScheme(string authenticationScheme, string displayName, Action<PolicySchemeOptions> configureOptions)
=> AddSchemeHelper<VirtualSchemeOptions, VirtualAuthenticationHandler>(authenticationScheme, displayName, configureOptions); => AddSchemeHelper<PolicySchemeOptions, PolicySchemeHandler>(authenticationScheme, displayName, configureOptions);
// Used to ensure that there's always a default sign in scheme that's not itself // Used to ensure that there's always a default sign in scheme that's not itself
private class EnsureSignInScheme<TOptions> : IPostConfigureOptions<TOptions> where TOptions : RemoteAuthenticationOptions private class EnsureSignInScheme<TOptions> : IPostConfigureOptions<TOptions> where TOptions : RemoteAuthenticationOptions

View File

@ -2,7 +2,6 @@
// 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.Security.Claims;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;

View File

@ -0,0 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// PolicySchemes are used to redirect authentication methods to another scheme.
/// </summary>
public class PolicySchemeHandler : SignInAuthenticationHandler<PolicySchemeOptions>
{
public PolicySchemeHandler(IOptionsMonitor<PolicySchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{ }
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
=> throw new NotImplementedException();
protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
=> throw new NotImplementedException();
protected override Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
=> throw new NotImplementedException();
protected override Task HandleSignOutAsync(AuthenticationProperties properties)
=> throw new NotImplementedException();
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
=> throw new NotImplementedException();
}
}

View File

@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Contains the options used by the <see cref="PolicySchemeHandler"/>.
/// </summary>
public class PolicySchemeOptions : AuthenticationSchemeOptions
{ }
}

View File

@ -0,0 +1,39 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Adds support for SignInAsync
/// </summary>
public abstract class SignInAuthenticationHandler<TOptions> : SignOutAuthenticationHandler<TOptions>, IAuthenticationSignInHandler
where TOptions : AuthenticationSchemeOptions, new()
{
public SignInAuthenticationHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{ }
public virtual Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
{
var target = ResolveTarget(Options.ForwardSignIn);
return (target != null)
? Context.SignInAsync(target, user, properties)
: HandleSignInAsync(user, properties ?? new AuthenticationProperties());
}
/// <summary>
/// Override this method to handle SignIn.
/// </summary>
/// <param name="user"></param>
/// <param name="properties"></param>
/// <returns>A Task.</returns>
protected abstract Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties);
}
}

View File

@ -0,0 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Adds support for SignOutAsync
/// </summary>
public abstract class SignOutAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions>, IAuthenticationSignOutHandler
where TOptions : AuthenticationSchemeOptions, new()
{
public SignOutAuthenticationHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{ }
public virtual Task SignOutAsync(AuthenticationProperties properties)
{
var target = ResolveTarget(Options.ForwardSignOut);
return (target != null)
? Context.SignOutAsync(target, properties)
: HandleSignOutAsync(properties ?? new AuthenticationProperties());
}
/// <summary>
/// Override this method to handle SignOut.
/// </summary>
/// <param name="properties"></param>
/// <returns>A Task.</returns>
protected abstract Task HandleSignOutAsync(AuthenticationProperties properties);
}
}

View File

@ -1,71 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Forwards calls to another authentication scheme.
/// </summary>
public class VirtualAuthenticationHandler : IAuthenticationHandler, IAuthenticationSignInHandler
{
protected IOptionsMonitor<VirtualSchemeOptions> OptionsMonitor { get; }
public AuthenticationScheme Scheme { get; private set; }
public VirtualSchemeOptions Options { get; private set; }
protected HttpContext Context { get; private set; }
public VirtualAuthenticationHandler(IOptionsMonitor<VirtualSchemeOptions> options)
{
OptionsMonitor = options;
}
/// <summary>
/// Initialize the handler, resolve the options and validate them.
/// </summary>
/// <param name="scheme"></param>
/// <param name="context"></param>
/// <returns>A Task.</returns>
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
{
if (scheme == null)
{
throw new ArgumentNullException(nameof(scheme));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
Scheme = scheme;
Context = context;
Options = OptionsMonitor.Get(Scheme.Name) ?? new VirtualSchemeOptions();
Options.Validate();
return Task.CompletedTask;
}
protected virtual string ResolveTarget(string scheme)
=> scheme ?? Options.DefaultSelector?.Invoke(Context) ?? Options.Default;
public virtual Task<AuthenticateResult> AuthenticateAsync()
=> Context.AuthenticateAsync(ResolveTarget(Options.Authenticate));
public virtual Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
=> Context.SignInAsync(ResolveTarget(Options.SignIn), user, properties);
public virtual Task SignOutAsync(AuthenticationProperties properties)
=> Context.SignOutAsync(ResolveTarget(Options.SignOut), properties);
public virtual Task ChallengeAsync(AuthenticationProperties properties)
=> Context.ChallengeAsync(ResolveTarget(Options.Challenge), properties);
public virtual Task ForbidAsync(AuthenticationProperties properties)
=> Context.ForbidAsync(ResolveTarget(Options.Forbid), properties);
}
}

View File

@ -1,33 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Used to redirect authentication methods to another scheme
/// </summary>
public class VirtualSchemeOptions
{
public string Default { get; set; }
public string Authenticate { get; set; }
public string Challenge { get; set; }
public string Forbid { get; set; }
public string SignIn { get; set; }
public string SignOut { get; set; }
/// <summary>
/// Used to select a default scheme to target based on the request.
/// </summary>
public Func<HttpContext, string> DefaultSelector { get; set; }
/// <summary>
/// Check that the options are valid. Should throw an exception if things are not ok.
/// </summary>
public virtual void Validate() { }
}
}

View File

@ -12,26 +12,26 @@ using Xunit;
namespace Microsoft.AspNetCore.Authentication namespace Microsoft.AspNetCore.Authentication
{ {
public class VirtualHandlerTests public class PolicyTests
{ {
[Fact] [Fact]
public async Task CanDispatch() public async Task CanDispatch()
{ {
var server = CreateServer(services => var server = CreateServer(services =>
{ {
services.AddAuthentication(o => services.AddLogging().AddAuthentication(o =>
{ {
o.AddScheme<TestHandler>("auth1", "auth1"); o.AddScheme<TestHandler>("auth1", "auth1");
o.AddScheme<TestHandler>("auth2", "auth2"); o.AddScheme<TestHandler>("auth2", "auth2");
o.AddScheme<TestHandler>("auth3", "auth3"); o.AddScheme<TestHandler>("auth3", "auth3");
}) })
.AddVirtualScheme("policy1", "policy1", p => .AddPolicyScheme("policy1", "policy1", p =>
{ {
p.Default = "auth1"; p.ForwardDefault = "auth1";
}) })
.AddVirtualScheme("policy2", "policy2", p => .AddPolicyScheme("policy2", "policy2", p =>
{ {
p.Authenticate = "auth2"; p.ForwardAuthenticate = "auth2";
}); });
}); });
@ -54,16 +54,15 @@ namespace Microsoft.AspNetCore.Authentication
[Fact] [Fact]
public async Task DefaultTargetSelectorWinsOverDefaultTarget() public async Task DefaultTargetSelectorWinsOverDefaultTarget()
{ {
var services = new ServiceCollection().AddOptions(); var services = new ServiceCollection().AddOptions().AddLogging();
services.AddAuthentication(o => services.AddAuthentication(o =>
{ {
o.AddScheme<TestHandler>("auth1", "auth1"); o.AddScheme<TestHandler>("auth1", "auth1");
o.AddScheme<TestHandler2>("auth2", "auth2"); o.AddScheme<TestHandler2>("auth2", "auth2");
}) })
.AddVirtualScheme("forward", "forward", p => { .AddPolicyScheme("forward", "forward", p => {
p.Default = "auth2"; p.ForwardDefault= "auth2";
p.DefaultSelector = ctx => "auth1"; p.ForwardDefaultSelector = ctx => "auth1";
}); });
var handler1 = new TestHandler(); var handler1 = new TestHandler();
@ -110,16 +109,15 @@ namespace Microsoft.AspNetCore.Authentication
[Fact] [Fact]
public async Task NullDefaultTargetSelectorFallsBacktoDefaultTarget() public async Task NullDefaultTargetSelectorFallsBacktoDefaultTarget()
{ {
var services = new ServiceCollection().AddOptions(); var services = new ServiceCollection().AddOptions().AddLogging();
services.AddAuthentication(o => services.AddAuthentication(o =>
{ {
o.AddScheme<TestHandler>("auth1", "auth1"); o.AddScheme<TestHandler>("auth1", "auth1");
o.AddScheme<TestHandler2>("auth2", "auth2"); o.AddScheme<TestHandler2>("auth2", "auth2");
}) })
.AddVirtualScheme("forward", "forward", p => { .AddPolicyScheme("forward", "forward", p => {
p.Default = "auth1"; p.ForwardDefault= "auth1";
p.DefaultSelector = ctx => null; p.ForwardDefaultSelector = ctx => null;
}); });
var handler1 = new TestHandler(); var handler1 = new TestHandler();
@ -166,21 +164,20 @@ namespace Microsoft.AspNetCore.Authentication
[Fact] [Fact]
public async Task SpecificTargetAlwaysWinsOverDefaultTarget() public async Task SpecificTargetAlwaysWinsOverDefaultTarget()
{ {
var services = new ServiceCollection().AddOptions(); var services = new ServiceCollection().AddOptions().AddLogging();
services.AddAuthentication(o => services.AddAuthentication(o =>
{ {
o.AddScheme<TestHandler>("auth1", "auth1"); o.AddScheme<TestHandler>("auth1", "auth1");
o.AddScheme<TestHandler2>("auth2", "auth2"); o.AddScheme<TestHandler2>("auth2", "auth2");
}) })
.AddVirtualScheme("forward", "forward", p => { .AddPolicyScheme("forward", "forward", p => {
p.Default = "auth2"; p.ForwardDefault= "auth2";
p.DefaultSelector = ctx => "auth2"; p.ForwardDefaultSelector = ctx => "auth2";
p.Authenticate = "auth1"; p.ForwardAuthenticate = "auth1";
p.SignIn = "auth1"; p.ForwardSignIn = "auth1";
p.SignOut = "auth1"; p.ForwardSignOut = "auth1";
p.Forbid = "auth1"; p.ForwardForbid = "auth1";
p.Challenge = "auth1"; p.ForwardChallenge = "auth1";
}); });
var handler1 = new TestHandler(); var handler1 = new TestHandler();
@ -227,14 +224,13 @@ namespace Microsoft.AspNetCore.Authentication
[Fact] [Fact]
public async Task VirtualSchemeTargetsForwardWithDefaultTarget() public async Task VirtualSchemeTargetsForwardWithDefaultTarget()
{ {
var services = new ServiceCollection().AddOptions(); var services = new ServiceCollection().AddOptions().AddLogging();
services.AddAuthentication(o => services.AddAuthentication(o =>
{ {
o.AddScheme<TestHandler>("auth1", "auth1"); o.AddScheme<TestHandler>("auth1", "auth1");
o.AddScheme<TestHandler2>("auth2", "auth2"); o.AddScheme<TestHandler2>("auth2", "auth2");
}) })
.AddVirtualScheme("forward", "forward", p => p.Default = "auth1"); .AddPolicyScheme("forward", "forward", p => p.ForwardDefault= "auth1");
var handler1 = new TestHandler(); var handler1 = new TestHandler();
services.AddSingleton(handler1); services.AddSingleton(handler1);
@ -280,18 +276,17 @@ namespace Microsoft.AspNetCore.Authentication
[Fact] [Fact]
public async Task VirtualSchemeTargetsOverrideDefaultTarget() public async Task VirtualSchemeTargetsOverrideDefaultTarget()
{ {
var services = new ServiceCollection().AddOptions(); var services = new ServiceCollection().AddOptions().AddLogging();
services.AddAuthentication(o => services.AddAuthentication(o =>
{ {
o.AddScheme<TestHandler>("auth1", "auth1"); o.AddScheme<TestHandler>("auth1", "auth1");
o.AddScheme<TestHandler2>("auth2", "auth2"); o.AddScheme<TestHandler2>("auth2", "auth2");
}) })
.AddVirtualScheme("forward", "forward", p => .AddPolicyScheme("forward", "forward", p =>
{ {
p.Default = "auth1"; p.ForwardDefault= "auth1";
p.Challenge = "auth2"; p.ForwardChallenge = "auth2";
p.SignIn = "auth2"; p.ForwardSignIn = "auth2";
}); });
var handler1 = new TestHandler(); var handler1 = new TestHandler();
@ -346,9 +341,9 @@ namespace Microsoft.AspNetCore.Authentication
o.AddScheme<TestHandler>("auth2", "auth2"); o.AddScheme<TestHandler>("auth2", "auth2");
o.AddScheme<TestHandler>("auth3", "auth3"); o.AddScheme<TestHandler>("auth3", "auth3");
}) })
.AddVirtualScheme("dynamic", "dynamic", p => .AddPolicyScheme("dynamic", "dynamic", p =>
{ {
p.DefaultSelector = c => c.Request.QueryString.Value.Substring(1); p.ForwardDefaultSelector = c => c.Request.QueryString.Value.Substring(1);
}); });
}); });
@ -360,39 +355,6 @@ namespace Microsoft.AspNetCore.Authentication
Assert.Equal("auth3", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "auth3")); Assert.Equal("auth3", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "auth3"));
} }
[Fact]
public async Task TargetsDefaultSchemeByDefault()
{
var server = CreateServer(services =>
{
services.AddAuthentication(o =>
{
o.DefaultScheme = "default";
o.AddScheme<TestHandler>("default", "default");
})
.AddVirtualScheme("virtual", "virtual", p => { });
});
var transaction = await server.SendAsync("http://example.com/auth/virtual");
Assert.Equal("default", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "default"));
}
[Fact]
public async Task TargetsDefaultSchemeThrowsWithNoDefault()
{
var server = CreateServer(services =>
{
services.AddAuthentication(o =>
{
o.AddScheme<TestHandler>("default", "default");
})
.AddVirtualScheme("virtual", "virtual", p => { });
});
var error = await Assert.ThrowsAsync<InvalidOperationException>(() => server.SendAsync("http://example.com/auth/virtual"));
Assert.Contains("No authenticationScheme was specified", error.Message);
}
private class TestHandler : IAuthenticationSignInHandler private class TestHandler : IAuthenticationSignInHandler
{ {
public AuthenticationScheme Scheme { get; set; } public AuthenticationScheme Scheme { get; set; }