Introduce SignInResult/SignOutResult and ControllerBase.SignIn/SignOut

This commit is contained in:
Kévin Chalet 2016-03-09 21:12:23 +01:00 committed by Ryan Nowak
parent 49e0a958c5
commit f9d24a8521
11 changed files with 494 additions and 64 deletions

View File

@ -101,8 +101,10 @@ namespace Microsoft.AspNetCore.Mvc
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<ChallengeResult>();
logger.ChallengeResultExecuting(AuthenticationSchemes);
var authentication = context.HttpContext.Authentication;
if (AuthenticationSchemes.Count > 0)
if (AuthenticationSchemes != null && AuthenticationSchemes.Count > 0)
{
foreach (var scheme in AuthenticationSchemes)
{
@ -113,8 +115,6 @@ namespace Microsoft.AspNetCore.Mvc
{
await authentication.ChallengeAsync(Properties);
}
logger.ChallengeResultExecuting(AuthenticationSchemes);
}
}
}

View File

@ -937,22 +937,13 @@ namespace Microsoft.AspNetCore.Mvc
public virtual ChallengeResult Challenge()
=> new ChallengeResult();
/// <summary>
/// Creates a <see cref="ChallengeResult"/> with the specified authentication scheme.
/// </summary>
/// <param name="authenticationScheme">The authentication scheme to challenge.</param>
/// <returns>The created <see cref="ChallengeResult"/> for the response.</returns>
[NonAction]
public virtual ChallengeResult Challenge(string authenticationScheme)
=> new ChallengeResult(authenticationScheme);
/// <summary>
/// Creates a <see cref="ChallengeResult"/> with the specified authentication schemes.
/// </summary>
/// <param name="authenticationSchemes">The authentication schemes to challenge.</param>
/// <returns>The created <see cref="ChallengeResult"/> for the response.</returns>
[NonAction]
public virtual ChallengeResult Challenge(IList<string> authenticationSchemes)
public virtual ChallengeResult Challenge(params string[] authenticationSchemes)
=> new ChallengeResult(authenticationSchemes);
/// <summary>
@ -965,30 +956,18 @@ namespace Microsoft.AspNetCore.Mvc
public virtual ChallengeResult Challenge(AuthenticationProperties properties)
=> new ChallengeResult(properties);
/// <summary>
/// Creates a <see cref="ChallengeResult"/> with the specified specified authentication scheme and
/// <paramref name="properties" />.
/// </summary>
/// <param name="authenticationScheme">The authentication scheme to challenge.</param>
/// <param name="properties"><see cref="AuthenticationProperties"/> used to perform the authentication
/// challenge.</param>
/// <returns>The created <see cref="ChallengeResult"/> for the response.</returns>
[NonAction]
public virtual ChallengeResult Challenge(string authenticationScheme, AuthenticationProperties properties)
=> new ChallengeResult(authenticationScheme, properties);
/// <summary>
/// Creates a <see cref="ChallengeResult"/> with the specified specified authentication schemes and
/// <paramref name="properties" />.
/// </summary>
/// <param name="authenticationSchemes">The authentication schemes to challenge.</param>
/// <param name="properties"><see cref="AuthenticationProperties"/> used to perform the authentication
/// challenge.</param>
/// <param name="authenticationSchemes">The authentication schemes to challenge.</param>
/// <returns>The created <see cref="ChallengeResult"/> for the response.</returns>
[NonAction]
public virtual ChallengeResult Challenge(
IList<string> authenticationSchemes,
AuthenticationProperties properties)
AuthenticationProperties properties,
params string[] authenticationSchemes)
=> new ChallengeResult(authenticationSchemes, properties);
/// <summary>
@ -999,22 +978,13 @@ namespace Microsoft.AspNetCore.Mvc
public virtual ForbidResult Forbid()
=> new ForbidResult();
/// <summary>
/// Creates a <see cref="ForbidResult"/> with the specified authentication scheme.
/// </summary>
/// <param name="authenticationScheme">The authentication scheme to challenge.</param>
/// <returns>The created <see cref="ForbidResult"/> for the response.</returns>
[NonAction]
public virtual ForbidResult Forbid(string authenticationScheme)
=> new ForbidResult(authenticationScheme);
/// <summary>
/// Creates a <see cref="ForbidResult"/> with the specified authentication schemes.
/// </summary>
/// <param name="authenticationSchemes">The authentication schemes to challenge.</param>
/// <returns>The created <see cref="ForbidResult"/> for the response.</returns>
[NonAction]
public virtual ForbidResult Forbid(IList<string> authenticationSchemes)
public virtual ForbidResult Forbid(params string[] authenticationSchemes)
=> new ForbidResult(authenticationSchemes);
/// <summary>
@ -1027,30 +997,63 @@ namespace Microsoft.AspNetCore.Mvc
public virtual ForbidResult Forbid(AuthenticationProperties properties)
=> new ForbidResult(properties);
/// <summary>
/// Creates a <see cref="ForbidResult"/> with the specified specified authentication scheme and
/// <paramref name="properties" />.
/// </summary>
/// <param name="authenticationScheme">The authentication scheme to challenge.</param>
/// <param name="properties"><see cref="AuthenticationProperties"/> used to perform the authentication
/// challenge.</param>
/// <returns>The created <see cref="ForbidResult"/> for the response.</returns>
[NonAction]
public virtual ForbidResult Forbid(string authenticationScheme, AuthenticationProperties properties)
=> new ForbidResult(authenticationScheme, properties);
/// <summary>
/// Creates a <see cref="ForbidResult"/> with the specified specified authentication schemes and
/// <paramref name="properties" />.
/// </summary>
/// <param name="authenticationSchemes">The authentication schemes to challenge.</param>
/// <param name="properties"><see cref="AuthenticationProperties"/> used to perform the authentication
/// challenge.</param>
/// <param name="authenticationSchemes">The authentication schemes to challenge.</param>
/// <returns>The created <see cref="ForbidResult"/> for the response.</returns>
[NonAction]
public virtual ForbidResult Forbid(IList<string> authenticationSchemes, AuthenticationProperties properties)
public virtual ForbidResult Forbid(AuthenticationProperties properties, params string[] authenticationSchemes)
=> new ForbidResult(authenticationSchemes, properties);
/// <summary>
/// Creates a <see cref="SignInResult"/> with the specified authentication scheme.
/// </summary>
/// <param name="principal">The <see cref="ClaimsPrincipal"/> containing the user claims.</param>
/// <param name="authenticationScheme">The authentication scheme to use for the sign-in operation.</param>
/// <returns>The created <see cref="SignInResult"/> for the response.</returns>
[NonAction]
public virtual SignInResult SignIn(ClaimsPrincipal principal, string authenticationScheme)
=> new SignInResult(authenticationScheme, principal);
/// <summary>
/// Creates a <see cref="SignInResult"/> with the specified specified authentication scheme and
/// <paramref name="properties" />.
/// </summary>
/// <param name="principal">The <see cref="ClaimsPrincipal"/> containing the user claims.</param>
/// <param name="properties"><see cref="AuthenticationProperties"/> used to perform the sign-in operation.</param>
/// <param name="authenticationScheme">The authentication scheme to use for the sign-in operation.</param>
/// <returns>The created <see cref="SignInResult"/> for the response.</returns>
[NonAction]
public virtual SignInResult SignIn(
ClaimsPrincipal principal,
AuthenticationProperties properties,
string authenticationScheme)
=> new SignInResult(authenticationScheme, principal, properties);
/// <summary>
/// Creates a <see cref="SignOutResult"/> with the specified authentication schemes.
/// </summary>
/// <param name="authenticationSchemes">The authentication schemes to use for the sign-out operation.</param>
/// <returns>The created <see cref="SignOutResult"/> for the response.</returns>
[NonAction]
public virtual SignOutResult SignOut(params string[] authenticationSchemes)
=> new SignOutResult(authenticationSchemes);
/// <summary>
/// Creates a <see cref="SignOutResult"/> with the specified specified authentication schemes and
/// <paramref name="properties" />.
/// </summary>
/// <param name="properties"><see cref="AuthenticationProperties"/> used to perform the sign-out operation.</param>
/// <param name="authenticationSchemes">The authentication scheme to use for the sign-out operation.</param>
/// <returns>The created <see cref="SignOutResult"/> for the response.</returns>
[NonAction]
public virtual SignOutResult SignOut(AuthenticationProperties properties, params string[] authenticationSchemes)
=> new SignOutResult(authenticationSchemes, properties);
/// <summary>
/// Updates the specified <paramref name="model"/> instance using values from the controller's current
/// <see cref="IValueProvider"/>.

View File

@ -101,6 +101,8 @@ namespace Microsoft.AspNetCore.Mvc
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<ForbidResult>();
logger.ForbidResultExecuting(AuthenticationSchemes);
var authentication = context.HttpContext.Authentication;
if (AuthenticationSchemes != null && AuthenticationSchemes.Count > 0)
@ -114,8 +116,6 @@ namespace Microsoft.AspNetCore.Mvc
{
await authentication.ForbidAsync(Properties);
}
logger.ForbidResultExecuting(AuthenticationSchemes);
}
}
}

View File

@ -6,6 +6,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.Filters;
@ -40,7 +41,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private static readonly Action<ILogger, object, Exception> _actionFilterShortCircuit;
private static readonly Action<ILogger, object, Exception> _exceptionFilterShortCircuit;
private static readonly Action<ILogger, string[], Exception> _resultExecuting;
private static readonly Action<ILogger, string[], Exception> _forbidResultExecuting;
private static readonly Action<ILogger, string, ClaimsPrincipal, Exception> _signInResultExecuting;
private static readonly Action<ILogger, string[], Exception> _signOutResultExecuting;
private static readonly Action<ILogger, int, Exception> _httpStatusCodeResultExecuting;
@ -126,11 +129,21 @@ namespace Microsoft.AspNetCore.Mvc.Internal
4,
"Request was short circuited at exception filter '{ExceptionFilter}'.");
_resultExecuting = LoggerMessage.Define<string[]>(
_forbidResultExecuting = LoggerMessage.Define<string[]>(
LogLevel.Information,
eventId: 1,
formatString: $"Executing {nameof(ForbidResult)} with authentication schemes ({{Schemes}}).");
_signInResultExecuting = LoggerMessage.Define<string, ClaimsPrincipal>(
LogLevel.Information,
eventId: 1,
formatString: $"Executing {nameof(SignInResult)} with authentication scheme ({{Scheme}}) and the following principal: {{Principal}}.");
_signOutResultExecuting = LoggerMessage.Define<string[]>(
LogLevel.Information,
eventId: 1,
formatString: $"Executing {nameof(SignOutResult)} with authentication schemes ({{Schemes}}).");
_httpStatusCodeResultExecuting = LoggerMessage.Define<int>(
LogLevel.Information,
1,
@ -305,7 +318,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public static void ForbidResultExecuting(this ILogger logger, IList<string> authenticationSchemes)
{
_resultExecuting(logger, authenticationSchemes.ToArray(), null);
_forbidResultExecuting(logger, authenticationSchemes.ToArray(), null);
}
public static void SignInResultExecuting(this ILogger logger, string authenticationScheme, ClaimsPrincipal principal)
{
_signInResultExecuting(logger, authenticationScheme, principal, null);
}
public static void SignOutResultExecuting(this ILogger logger, IList<string> authenticationSchemes)
{
_signOutResultExecuting(logger, authenticationSchemes.ToArray(), null);
}
public static void HttpStatusCodeResultExecuting(this ILogger logger, int statusCode)

View File

@ -1146,6 +1146,22 @@ namespace Microsoft.AspNetCore.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("Formatter_NoMediaTypes"), p0, p1);
}
/// <summary>
/// At least one authentication scheme must be specified.
/// </summary>
internal static string MustSpecifyAtLeastOneAuthenticationScheme
{
get { return GetString("MustSpecifyAtLeastOneAuthenticationScheme"); }
}
/// <summary>
/// At least one authentication scheme must be specified.
/// </summary>
internal static string FormatMustSpecifyAtLeastOneAuthenticationScheme()
{
return string.Format(CultureInfo.CurrentCulture, GetString("MustSpecifyAtLeastOneAuthenticationScheme"));
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -340,4 +340,7 @@
<data name="Formatter_NoMediaTypes" xml:space="preserve">
<value>No media types found in '{0}.{1}'. Add at least one media type to the list of supported media types.</value>
</data>
<data name="MustSpecifyAtLeastOneAuthenticationScheme" xml:space="preserve">
<value>At least one authentication scheme must be specified.</value>
</data>
</root>

View File

@ -0,0 +1,95 @@
// 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.Authentication;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Mvc
{
/// <summary>
/// An <see cref="ActionResult"/> that on execution invokes <see cref="M:AuthenticationManager.SignInAsync"/>.
/// </summary>
public class SignInResult : ActionResult
{
/// <summary>
/// Initializes a new instance of <see cref="SignInResult"/> with the
/// specified authentication scheme.
/// </summary>
/// <param name="authenticationScheme">The authentication scheme to use when signing in the user.</param>
/// <param name="principal">The claims principal containing the user claims.</param>
public SignInResult(string authenticationScheme, ClaimsPrincipal principal)
: this(authenticationScheme, principal, properties: null)
{
}
/// <summary>
/// Initializes a new instance of <see cref="SignInResult"/> with the
/// specified authentication scheme and <paramref name="properties"/>.
/// </summary>
/// <param name="authenticationScheme">The authentication schemes to use when signing in the user.</param>
/// <param name="principal">The claims principal containing the user claims.</param>
/// <param name="properties"><see cref="AuthenticationProperties"/> used to perform the sign-in operation.</param>
public SignInResult(string authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties properties)
{
if (authenticationScheme == null)
{
throw new ArgumentNullException(nameof(authenticationScheme));
}
if (principal == null)
{
throw new ArgumentNullException(nameof(principal));
}
AuthenticationScheme = authenticationScheme;
Principal = principal;
Properties = properties;
}
/// <summary>
/// Gets or sets the authentication scheme that is used to perform the sign-in operation.
/// </summary>
public string AuthenticationScheme { get; set; }
/// <summary>
/// Gets or sets the <see cref="ClaimsPrincipal"/> containing the user claims.
/// </summary>
public ClaimsPrincipal Principal { get; set; }
/// <summary>
/// Gets or sets the <see cref="AuthenticationProperties"/> used to perform the sign-in operation.
/// </summary>
public AuthenticationProperties Properties { get; set; }
/// <inheritdoc />
public override async Task ExecuteResultAsync(ActionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (AuthenticationScheme == null)
{
throw new InvalidOperationException(
Resources.FormatPropertyOfTypeCannotBeNull(
/* property: */ nameof(AuthenticationScheme),
/* type: */ nameof(SignInResult)));
}
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<SignInResult>();
logger.SignInResultExecuting(AuthenticationScheme, Principal);
var authentication = context.HttpContext.Authentication;
await authentication.SignInAsync(AuthenticationScheme, Principal, Properties);
}
}
}

View File

@ -0,0 +1,117 @@
// 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.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Mvc
{
/// <summary>
/// An <see cref="ActionResult"/> that on execution invokes <see cref="M:AuthenticationManager.SignOutAsync"/>.
/// </summary>
public class SignOutResult : ActionResult
{
/// <summary>
/// Initializes a new instance of <see cref="SignOutResult"/> with the
/// specified authentication scheme.
/// </summary>
/// <param name="authenticationScheme">The authentication scheme to use when signing out the user.</param>
public SignOutResult(string authenticationScheme)
: this(new[] { authenticationScheme })
{
}
/// <summary>
/// Initializes a new instance of <see cref="SignOutResult"/> with the
/// specified authentication schemes.
/// </summary>
/// <param name="authenticationSchemes">The authentication schemes to use when signing out the user.</param>
public SignOutResult(IList<string> authenticationSchemes)
: this(authenticationSchemes, properties: null)
{
}
/// <summary>
/// Initializes a new instance of <see cref="SignOutResult"/> with the
/// specified authentication scheme and <paramref name="properties"/>.
/// </summary>
/// <param name="authenticationScheme">The authentication schemes to use when signing out the user.</param>
/// <param name="properties"><see cref="AuthenticationProperties"/> used to perform the sign-out operation.</param>
public SignOutResult(string authenticationScheme, AuthenticationProperties properties)
: this(new[] { authenticationScheme }, properties)
{
}
/// <summary>
/// Initializes a new instance of <see cref="SignOutResult"/> with the
/// specified authentication schemes and <paramref name="properties"/>.
/// </summary>
/// <param name="authenticationSchemes">The authentication scheme to use when signing out the user.</param>
/// <param name="properties"><see cref="AuthenticationProperties"/> used to perform the sign-out operation.</param>
public SignOutResult(IList<string> authenticationSchemes, AuthenticationProperties properties)
{
if (authenticationSchemes == null)
{
throw new ArgumentNullException(nameof(authenticationSchemes));
}
if (authenticationSchemes.Count == 0)
{
throw new ArgumentException(Resources.MustSpecifyAtLeastOneAuthenticationScheme, nameof(authenticationSchemes));
}
AuthenticationSchemes = authenticationSchemes;
Properties = properties;
}
/// <summary>
/// Gets or sets the authentication schemes that are challenged.
/// </summary>
public IList<string> AuthenticationSchemes { get; set; }
/// <summary>
/// Gets or sets the <see cref="AuthenticationProperties"/> used to perform the sign-out operation.
/// </summary>
public AuthenticationProperties Properties { get; set; }
/// <inheritdoc />
public override async Task ExecuteResultAsync(ActionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (AuthenticationSchemes == null)
{
throw new InvalidOperationException(
Resources.FormatPropertyOfTypeCannotBeNull(
/* property: */ nameof(AuthenticationSchemes),
/* type: */ nameof(SignOutResult)));
}
if (AuthenticationSchemes.Count == 0)
{
throw new ArgumentException(Resources.MustSpecifyAtLeastOneAuthenticationScheme, nameof(AuthenticationSchemes));
}
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<SignOutResult>();
logger.SignOutResultExecuting(AuthenticationSchemes);
var authentication = context.HttpContext.Authentication;
for (var i = 0; i < AuthenticationSchemes.Count; i++)
{
await authentication.SignOutAsync(AuthenticationSchemes[i], Properties);
}
}
}
}

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc
public class ForbidResultTest
{
[Fact]
public async Task ExecuteResultAsync_InvokesForbiddenAsyncOnAuthenticationManager()
public async Task ExecuteResultAsync_InvokesForbidAsyncOnAuthenticationManager()
{
// Arrange
var authenticationManager = new Mock<AuthenticationManager>();
@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Mvc
}
[Fact]
public async Task ExecuteResultAsync_InvokesForbiddenAsyncOnAllConfiguredSchemes()
public async Task ExecuteResultAsync_InvokesForbidAsyncOnAllConfiguredSchemes()
{
// Arrange
var authProperties = new AuthenticationProperties();
@ -77,7 +77,7 @@ namespace Microsoft.AspNetCore.Mvc
authenticationManager.Verify();
}
public static TheoryData ExecuteResultAsync_InvokesForbiddenAsyncWithAuthPropertiesData =>
public static TheoryData ExecuteResultAsync_InvokesForbidAsyncWithAuthPropertiesData =>
new TheoryData<AuthenticationProperties>
{
null,
@ -85,8 +85,8 @@ namespace Microsoft.AspNetCore.Mvc
};
[Theory]
[MemberData(nameof(ExecuteResultAsync_InvokesForbiddenAsyncWithAuthPropertiesData))]
public async Task ExecuteResultAsync_InvokesForbiddenAsyncWithAuthProperties(AuthenticationProperties expected)
[MemberData(nameof(ExecuteResultAsync_InvokesForbidAsyncWithAuthPropertiesData))]
public async Task ExecuteResultAsync_InvokesForbidAsyncWithAuthProperties(AuthenticationProperties expected)
{
// Arrange
var authenticationManager = new Mock<AuthenticationManager>();
@ -113,8 +113,8 @@ namespace Microsoft.AspNetCore.Mvc
}
[Theory]
[MemberData(nameof(ExecuteResultAsync_InvokesForbiddenAsyncWithAuthPropertiesData))]
public async Task ExecuteResultAsync_InvokesForbiddenAsyncWithAuthProperties_WhenAuthenticationSchemesIsEmpty(
[MemberData(nameof(ExecuteResultAsync_InvokesForbidAsyncWithAuthPropertiesData))]
public async Task ExecuteResultAsync_InvokesForbidAsyncWithAuthProperties_WhenAuthenticationSchemesIsEmpty(
AuthenticationProperties expected)
{
// Arrange

View File

@ -0,0 +1,86 @@
// 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.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Mvc
{
public class SignInResultTest
{
[Fact]
public async Task ExecuteResultAsync_InvokesSignInAsyncOnAuthenticationManager()
{
// Arrange
var principal = new ClaimsPrincipal();
var authenticationManager = new Mock<AuthenticationManager>();
authenticationManager
.Setup(c => c.SignInAsync("", principal, null))
.Returns(TaskCache.CompletedTask)
.Verifiable();
var httpContext = new Mock<HttpContext>();
httpContext.Setup(c => c.RequestServices).Returns(CreateServices());
httpContext.Setup(c => c.Authentication).Returns(authenticationManager.Object);
var result = new SignInResult("", principal, null);
var routeData = new RouteData();
var actionContext = new ActionContext(
httpContext.Object,
routeData,
new ActionDescriptor());
// Act
await result.ExecuteResultAsync(actionContext);
// Assert
authenticationManager.Verify();
}
[Fact]
public async Task ExecuteResultAsync_InvokesSignInAsyncOnConfiguredScheme()
{
// Arrange
var principal = new ClaimsPrincipal();
var authProperties = new AuthenticationProperties();
var authenticationManager = new Mock<AuthenticationManager>();
authenticationManager
.Setup(c => c.SignInAsync("Scheme1", principal, authProperties))
.Returns(TaskCache.CompletedTask)
.Verifiable();
var httpContext = new Mock<HttpContext>();
httpContext.Setup(c => c.RequestServices).Returns(CreateServices());
httpContext.Setup(c => c.Authentication).Returns(authenticationManager.Object);
var result = new SignInResult("Scheme1", principal, authProperties);
var routeData = new RouteData();
var actionContext = new ActionContext(
httpContext.Object,
routeData,
new ActionDescriptor());
// Act
await result.ExecuteResultAsync(actionContext);
// Assert
authenticationManager.Verify();
}
private static IServiceProvider CreateServices()
{
return new ServiceCollection()
.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance)
.BuildServiceProvider();
}
}
}

View File

@ -0,0 +1,87 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Mvc
{
public class SignOutResultTest
{
[Fact]
public async Task ExecuteResultAsync_InvokesSignOutAsyncOnAuthenticationManager()
{
// Arrange
var authenticationManager = new Mock<AuthenticationManager>();
authenticationManager
.Setup(c => c.SignOutAsync("", null))
.Returns(TaskCache.CompletedTask)
.Verifiable();
var httpContext = new Mock<HttpContext>();
httpContext.Setup(c => c.RequestServices).Returns(CreateServices());
httpContext.Setup(c => c.Authentication).Returns(authenticationManager.Object);
var result = new SignOutResult("", null);
var routeData = new RouteData();
var actionContext = new ActionContext(
httpContext.Object,
routeData,
new ActionDescriptor());
// Act
await result.ExecuteResultAsync(actionContext);
// Assert
authenticationManager.Verify();
}
[Fact]
public async Task ExecuteResultAsync_InvokesSignOutAsyncOnAllConfiguredSchemes()
{
// Arrange
var authProperties = new AuthenticationProperties();
var authenticationManager = new Mock<AuthenticationManager>();
authenticationManager
.Setup(c => c.SignOutAsync("Scheme1", authProperties))
.Returns(TaskCache.CompletedTask)
.Verifiable();
authenticationManager
.Setup(c => c.SignOutAsync("Scheme2", authProperties))
.Returns(TaskCache.CompletedTask)
.Verifiable();
var httpContext = new Mock<HttpContext>();
httpContext.Setup(c => c.RequestServices).Returns(CreateServices());
httpContext.Setup(c => c.Authentication).Returns(authenticationManager.Object);
var result = new SignOutResult(new[] { "Scheme1", "Scheme2" }, authProperties);
var routeData = new RouteData();
var actionContext = new ActionContext(
httpContext.Object,
routeData,
new ActionDescriptor());
// Act
await result.ExecuteResultAsync(actionContext);
// Assert
authenticationManager.Verify();
}
private static IServiceProvider CreateServices()
{
return new ServiceCollection()
.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance)
.BuildServiceProvider();
}
}
}