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
{
public class CookieAuthenticationHandler :
AuthenticationHandler<CookieAuthenticationOptions>,
IAuthenticationSignInHandler,
IAuthenticationSignOutHandler
public class CookieAuthenticationHandler : SignInAuthenticationHandler<CookieAuthenticationOptions>
{
private const string HeaderValueNoCache = "no-cache";
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)
{
throw new ArgumentNullException(nameof(user));
}
var target = ResolveTarget(Options.ForwardSignIn);
if (target != null)
{
await Context.SignInAsync(target, user, properties);
return;
}
properties = properties ?? new AuthenticationProperties();
_signInCalled = true;
@ -346,15 +336,8 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
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();
_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>
/// 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.
/// </summary>
/// <param name="authenticationScheme">The 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>
/// <returns>The builder.</returns>
public virtual AuthenticationBuilder AddVirtualScheme(string authenticationScheme, string displayName, Action<VirtualSchemeOptions> configureOptions)
=> AddSchemeHelper<VirtualSchemeOptions, VirtualAuthenticationHandler>(authenticationScheme, displayName, configureOptions);
public virtual AuthenticationBuilder AddPolicyScheme(string authenticationScheme, string displayName, Action<PolicySchemeOptions> configureOptions)
=> AddSchemeHelper<PolicySchemeOptions, PolicySchemeHandler>(authenticationScheme, displayName, configureOptions);
// 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

View File

@ -2,7 +2,6 @@
// 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.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
{
public class VirtualHandlerTests
public class PolicyTests
{
[Fact]
public async Task CanDispatch()
{
var server = CreateServer(services =>
{
services.AddAuthentication(o =>
services.AddLogging().AddAuthentication(o =>
{
o.AddScheme<TestHandler>("auth1", "auth1");
o.AddScheme<TestHandler>("auth2", "auth2");
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]
public async Task DefaultTargetSelectorWinsOverDefaultTarget()
{
var services = new ServiceCollection().AddOptions();
var services = new ServiceCollection().AddOptions().AddLogging();
services.AddAuthentication(o =>
{
o.AddScheme<TestHandler>("auth1", "auth1");
o.AddScheme<TestHandler2>("auth2", "auth2");
})
.AddVirtualScheme("forward", "forward", p => {
p.Default = "auth2";
p.DefaultSelector = ctx => "auth1";
.AddPolicyScheme("forward", "forward", p => {
p.ForwardDefault= "auth2";
p.ForwardDefaultSelector = ctx => "auth1";
});
var handler1 = new TestHandler();
@ -110,16 +109,15 @@ namespace Microsoft.AspNetCore.Authentication
[Fact]
public async Task NullDefaultTargetSelectorFallsBacktoDefaultTarget()
{
var services = new ServiceCollection().AddOptions();
var services = new ServiceCollection().AddOptions().AddLogging();
services.AddAuthentication(o =>
{
o.AddScheme<TestHandler>("auth1", "auth1");
o.AddScheme<TestHandler2>("auth2", "auth2");
})
.AddVirtualScheme("forward", "forward", p => {
p.Default = "auth1";
p.DefaultSelector = ctx => null;
.AddPolicyScheme("forward", "forward", p => {
p.ForwardDefault= "auth1";
p.ForwardDefaultSelector = ctx => null;
});
var handler1 = new TestHandler();
@ -166,21 +164,20 @@ namespace Microsoft.AspNetCore.Authentication
[Fact]
public async Task SpecificTargetAlwaysWinsOverDefaultTarget()
{
var services = new ServiceCollection().AddOptions();
var services = new ServiceCollection().AddOptions().AddLogging();
services.AddAuthentication(o =>
{
o.AddScheme<TestHandler>("auth1", "auth1");
o.AddScheme<TestHandler2>("auth2", "auth2");
})
.AddVirtualScheme("forward", "forward", p => {
p.Default = "auth2";
p.DefaultSelector = ctx => "auth2";
p.Authenticate = "auth1";
p.SignIn = "auth1";
p.SignOut = "auth1";
p.Forbid = "auth1";
p.Challenge = "auth1";
.AddPolicyScheme("forward", "forward", p => {
p.ForwardDefault= "auth2";
p.ForwardDefaultSelector = ctx => "auth2";
p.ForwardAuthenticate = "auth1";
p.ForwardSignIn = "auth1";
p.ForwardSignOut = "auth1";
p.ForwardForbid = "auth1";
p.ForwardChallenge = "auth1";
});
var handler1 = new TestHandler();
@ -227,14 +224,13 @@ namespace Microsoft.AspNetCore.Authentication
[Fact]
public async Task VirtualSchemeTargetsForwardWithDefaultTarget()
{
var services = new ServiceCollection().AddOptions();
var services = new ServiceCollection().AddOptions().AddLogging();
services.AddAuthentication(o =>
{
o.AddScheme<TestHandler>("auth1", "auth1");
o.AddScheme<TestHandler2>("auth2", "auth2");
})
.AddVirtualScheme("forward", "forward", p => p.Default = "auth1");
.AddPolicyScheme("forward", "forward", p => p.ForwardDefault= "auth1");
var handler1 = new TestHandler();
services.AddSingleton(handler1);
@ -280,18 +276,17 @@ namespace Microsoft.AspNetCore.Authentication
[Fact]
public async Task VirtualSchemeTargetsOverrideDefaultTarget()
{
var services = new ServiceCollection().AddOptions();
var services = new ServiceCollection().AddOptions().AddLogging();
services.AddAuthentication(o =>
{
o.AddScheme<TestHandler>("auth1", "auth1");
o.AddScheme<TestHandler2>("auth2", "auth2");
})
.AddVirtualScheme("forward", "forward", p =>
.AddPolicyScheme("forward", "forward", p =>
{
p.Default = "auth1";
p.Challenge = "auth2";
p.SignIn = "auth2";
p.ForwardDefault= "auth1";
p.ForwardChallenge = "auth2";
p.ForwardSignIn = "auth2";
});
var handler1 = new TestHandler();
@ -346,9 +341,9 @@ namespace Microsoft.AspNetCore.Authentication
o.AddScheme<TestHandler>("auth2", "auth2");
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"));
}
[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
{
public AuthenticationScheme Scheme { get; set; }