Allow custom handling of authorization failures (with sample app) (#21117)
This commit is contained in:
parent
263c376a80
commit
6c7a8bb397
|
|
@ -68,6 +68,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomPolicyProvider", "sam
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StaticFilesAuth", "samples\StaticFilesAuth\StaticFilesAuth.csproj", "{E1E8A599-AB42-4551-8C24-BE4404B65283}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomAuthorizationFailureResponse", "samples\CustomAuthorizationFailureResponse\CustomAuthorizationFailureResponse.csproj", "{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -402,6 +404,18 @@ Global
|
|||
{E1E8A599-AB42-4551-8C24-BE4404B65283}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E1E8A599-AB42-4551-8C24-BE4404B65283}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E1E8A599-AB42-4551-8C24-BE4404B65283}.Release|x86.Build.0 = Release|Any CPU
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}.Release|x64.Build.0 = Release|Any CPU
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -434,6 +448,7 @@ Global
|
|||
{82C0816D-7051-4DDB-9B9E-6777973AD7AE} = {142C8260-90B5-4D72-9564-17BFDD72F496}
|
||||
{38C0E122-64D0-497F-ABB0-C6A9C3349F02} = {CA4538F5-9DA8-4139-B891-A13279889F79}
|
||||
{E1E8A599-AB42-4551-8C24-BE4404B65283} = {CA4538F5-9DA8-4139-B891-A13279889F79}
|
||||
{EA51BBBC-58AC-42F8-97C1-5CF3C9725513} = {CA4538F5-9DA8-4139-B891-A13279889F79}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {39E3AF62-B1FD-4156-92AA-F4FA99B5AD89}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,19 @@ namespace Microsoft.AspNetCore.Authorization
|
|||
[System.Diagnostics.DebuggerStepThroughAttribute]
|
||||
public System.Threading.Tasks.Task Invoke(Microsoft.AspNetCore.Http.HttpContext context) { throw null; }
|
||||
}
|
||||
public partial interface IAuthorizationMiddlewareResultHandler
|
||||
{
|
||||
System.Threading.Tasks.Task HandleAsync(Microsoft.AspNetCore.Http.RequestDelegate next, Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Authorization.AuthorizationPolicy policy, Microsoft.AspNetCore.Authorization.Policy.PolicyAuthorizationResult authorizeResult);
|
||||
}
|
||||
}
|
||||
namespace Microsoft.AspNetCore.Authorization.Policy
|
||||
{
|
||||
public partial class AuthorizationMiddlewareResultHandler : Microsoft.AspNetCore.Authorization.IAuthorizationMiddlewareResultHandler
|
||||
{
|
||||
public AuthorizationMiddlewareResultHandler() { }
|
||||
[System.Diagnostics.DebuggerStepThroughAttribute]
|
||||
public System.Threading.Tasks.Task HandleAsync(Microsoft.AspNetCore.Http.RequestDelegate next, Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Authorization.AuthorizationPolicy policy, Microsoft.AspNetCore.Authorization.Policy.PolicyAuthorizationResult authorizeResult) { throw null; }
|
||||
}
|
||||
public partial interface IPolicyEvaluator
|
||||
{
|
||||
System.Threading.Tasks.Task<Microsoft.AspNetCore.Authentication.AuthenticateResult> AuthenticateAsync(Microsoft.AspNetCore.Authorization.AuthorizationPolicy policy, Microsoft.AspNetCore.Http.HttpContext context);
|
||||
|
|
@ -20,11 +30,13 @@ namespace Microsoft.AspNetCore.Authorization.Policy
|
|||
public partial class PolicyAuthorizationResult
|
||||
{
|
||||
internal PolicyAuthorizationResult() { }
|
||||
public Microsoft.AspNetCore.Authorization.AuthorizationFailure AuthorizationFailure { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
public bool Challenged { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
public bool Forbidden { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
public bool Succeeded { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
public static Microsoft.AspNetCore.Authorization.Policy.PolicyAuthorizationResult Challenge() { throw null; }
|
||||
public static Microsoft.AspNetCore.Authorization.Policy.PolicyAuthorizationResult Forbid() { throw null; }
|
||||
public static Microsoft.AspNetCore.Authorization.Policy.PolicyAuthorizationResult Forbid(Microsoft.AspNetCore.Authorization.AuthorizationFailure authorizationFailure) { throw null; }
|
||||
public static Microsoft.AspNetCore.Authorization.Policy.PolicyAuthorizationResult Success() { throw null; }
|
||||
}
|
||||
public partial class PolicyEvaluator : Microsoft.AspNetCore.Authorization.Policy.IPolicyEvaluator
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization.Policy;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -66,40 +64,8 @@ namespace Microsoft.AspNetCore.Authorization
|
|||
// Note that the resource will be null if there is no matched endpoint
|
||||
var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult, context, resource: endpoint);
|
||||
|
||||
if (authorizeResult.Challenged)
|
||||
{
|
||||
if (policy.AuthenticationSchemes.Count > 0)
|
||||
{
|
||||
foreach (var scheme in policy.AuthenticationSchemes)
|
||||
{
|
||||
await context.ChallengeAsync(scheme);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.ChallengeAsync();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (authorizeResult.Forbidden)
|
||||
{
|
||||
if (policy.AuthenticationSchemes.Count > 0)
|
||||
{
|
||||
foreach (var scheme in policy.AuthenticationSchemes)
|
||||
{
|
||||
await context.ForbidAsync(scheme);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.ForbidAsync();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await _next(context);
|
||||
var authorizationMiddlewareResultHandler = context.RequestServices.GetRequiredService<IAuthorizationMiddlewareResultHandler>();
|
||||
await authorizationMiddlewareResultHandler.HandleAsync(_next, context, policy, authorizeResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authorization.Policy
|
||||
{
|
||||
public class AuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
|
||||
{
|
||||
public async Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
|
||||
{
|
||||
if (authorizeResult.Challenged)
|
||||
{
|
||||
if (policy.AuthenticationSchemes.Count > 0)
|
||||
{
|
||||
foreach (var scheme in policy.AuthenticationSchemes)
|
||||
{
|
||||
await context.ChallengeAsync(scheme);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.ChallengeAsync();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (authorizeResult.Forbidden)
|
||||
{
|
||||
if (policy.AuthenticationSchemes.Count > 0)
|
||||
{
|
||||
foreach (var scheme in policy.AuthenticationSchemes)
|
||||
{
|
||||
await context.ForbidAsync(scheme);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.ForbidAsync();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await next(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization.Policy;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authorization
|
||||
{
|
||||
public interface IAuthorizationMiddlewareResultHandler
|
||||
{
|
||||
Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult);
|
||||
}
|
||||
}
|
||||
|
|
@ -33,8 +33,8 @@ namespace Microsoft.AspNetCore.Authorization.Policy
|
|||
/// If a resource is not required for policy evaluation you may pass null as the value.
|
||||
/// </param>
|
||||
/// <returns>Returns <see cref="PolicyAuthorizationResult.Success"/> if authorization succeeds.
|
||||
/// Otherwise returns <see cref="PolicyAuthorizationResult.Forbid"/> if <see cref="AuthenticateResult.Succeeded"/>, otherwise
|
||||
/// Otherwise returns <see cref="PolicyAuthorizationResult.Forbid(AuthorizationFailure)"/> if <see cref="AuthenticateResult.Succeeded"/>, otherwise
|
||||
/// returns <see cref="PolicyAuthorizationResult.Challenge"/></returns>
|
||||
Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,22 @@ namespace Microsoft.AspNetCore.Authorization.Policy
|
|||
/// </summary>
|
||||
public bool Succeeded { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contains information about why authorization failed.
|
||||
/// </summary>
|
||||
public AuthorizationFailure AuthorizationFailure { get; private set; }
|
||||
|
||||
public static PolicyAuthorizationResult Challenge()
|
||||
=> new PolicyAuthorizationResult { Challenged = true };
|
||||
|
||||
public static PolicyAuthorizationResult Forbid()
|
||||
=> new PolicyAuthorizationResult { Forbidden = true };
|
||||
=> Forbid(null);
|
||||
|
||||
public static PolicyAuthorizationResult Forbid(AuthorizationFailure authorizationFailure)
|
||||
=> new PolicyAuthorizationResult { Forbidden = true, AuthorizationFailure = authorizationFailure };
|
||||
|
||||
public static PolicyAuthorizationResult Success()
|
||||
=> new PolicyAuthorizationResult { Succeeded = true };
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Authorization.Policy
|
|||
}
|
||||
}
|
||||
|
||||
return (context.User?.Identity?.IsAuthenticated ?? false)
|
||||
return (context.User?.Identity?.IsAuthenticated ?? false)
|
||||
? AuthenticateResult.Success(new AuthenticationTicket(context.User, "context.User"))
|
||||
: AuthenticateResult.NoResult();
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Authorization.Policy
|
|||
/// If a resource is not required for policy evaluation you may pass null as the value.
|
||||
/// </param>
|
||||
/// <returns>Returns <see cref="PolicyAuthorizationResult.Success"/> if authorization succeeds.
|
||||
/// Otherwise returns <see cref="PolicyAuthorizationResult.Forbid"/> if <see cref="AuthenticateResult.Succeeded"/>, otherwise
|
||||
/// Otherwise returns <see cref="PolicyAuthorizationResult.Forbid(AuthorizationFailure)"/> if <see cref="AuthenticateResult.Succeeded"/>, otherwise
|
||||
/// returns <see cref="PolicyAuthorizationResult.Challenge"/></returns>
|
||||
public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource)
|
||||
{
|
||||
|
|
@ -88,9 +88,9 @@ namespace Microsoft.AspNetCore.Authorization.Policy
|
|||
}
|
||||
|
||||
// If authentication was successful, return forbidden, otherwise challenge
|
||||
return (authenticationResult.Succeeded)
|
||||
? PolicyAuthorizationResult.Forbid()
|
||||
return (authenticationResult.Succeeded)
|
||||
? PolicyAuthorizationResult.Forbid(result.Failure)
|
||||
: PolicyAuthorizationResult.Challenge();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
|
||||
services.TryAddSingleton<AuthorizationPolicyMarkerService>();
|
||||
services.TryAdd(ServiceDescriptor.Transient<IPolicyEvaluator, PolicyEvaluator>());
|
||||
services.TryAddTransient<IPolicyEvaluator, PolicyEvaluator>();
|
||||
services.TryAddTransient<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();
|
||||
return services;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
// 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.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization.Policy;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authorization.Test
|
||||
{
|
||||
public class AuthorizationMiddlewareResultHandlerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task CallRequestDelegate_If_PolicyAuthorizationResultSucceeded()
|
||||
{
|
||||
var requestDelegate = new Mock<RequestDelegate>();
|
||||
var httpContext = CreateHttpContext();
|
||||
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
|
||||
var policyAuthorizationResult = PolicyAuthorizationResult.Success();
|
||||
var handler = CreateAuthorizationMiddlewareResultHandler();
|
||||
|
||||
await handler.HandleAsync(requestDelegate.Object, httpContext, policy, policyAuthorizationResult);
|
||||
|
||||
requestDelegate.Verify(next => next(It.IsAny<HttpContext>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task NotCallRequestDelegate_If_PolicyAuthorizationResultWasChallenged()
|
||||
{
|
||||
var requestDelegate = new Mock<RequestDelegate>();
|
||||
var httpContext = CreateHttpContext();
|
||||
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
|
||||
var policyAuthorizationResult = PolicyAuthorizationResult.Challenge();
|
||||
var handler = CreateAuthorizationMiddlewareResultHandler();
|
||||
|
||||
await handler.HandleAsync(requestDelegate.Object, httpContext, policy, policyAuthorizationResult);
|
||||
|
||||
requestDelegate.Verify(next => next(It.IsAny<HttpContext>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task NotCallRequestDelegate_If_PolicyAuthorizationResultWasForbidden()
|
||||
{
|
||||
var requestDelegate = new Mock<RequestDelegate>();
|
||||
var httpContext = CreateHttpContext();
|
||||
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
|
||||
var policyAuthorizationResult = PolicyAuthorizationResult.Forbid();
|
||||
var handler = CreateAuthorizationMiddlewareResultHandler();
|
||||
|
||||
await handler.HandleAsync(requestDelegate.Object, httpContext, policy, policyAuthorizationResult);
|
||||
|
||||
requestDelegate.Verify(next => next(It.IsAny<HttpContext>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChallangeEachAuthenticationScheme_If_PolicyAuthorizationResultWasChallenged()
|
||||
{
|
||||
var authenticationServiceMock = new Mock<IAuthenticationService>();
|
||||
var requestDelegate = new Mock<RequestDelegate>();
|
||||
var httpContext = CreateHttpContext(authenticationServiceMock.Object);
|
||||
var firstScheme = Guid.NewGuid().ToString();
|
||||
var secondScheme = Guid.NewGuid().ToString();
|
||||
var thirdScheme = Guid.NewGuid().ToString();
|
||||
var policy = new AuthorizationPolicyBuilder()
|
||||
.RequireAuthenticatedUser()
|
||||
.AddAuthenticationSchemes(firstScheme, secondScheme, thirdScheme)
|
||||
.Build();
|
||||
var policyAuthorizationResult = PolicyAuthorizationResult.Challenge();
|
||||
var handler = CreateAuthorizationMiddlewareResultHandler();
|
||||
|
||||
await handler.HandleAsync(requestDelegate.Object, httpContext, policy, policyAuthorizationResult);
|
||||
|
||||
authenticationServiceMock.Verify(service => service.ChallengeAsync(httpContext, It.IsAny<string>(), null), Times.Exactly(3));
|
||||
authenticationServiceMock.Verify(service => service.ChallengeAsync(httpContext, firstScheme, null), Times.Once);
|
||||
authenticationServiceMock.Verify(service => service.ChallengeAsync(httpContext, secondScheme, null), Times.Once);
|
||||
authenticationServiceMock.Verify(service => service.ChallengeAsync(httpContext, thirdScheme, null), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChallangeWithoutAuthenticationScheme_If_PolicyAuthorizationResultWasChallenged()
|
||||
{
|
||||
var authenticationServiceMock = new Mock<IAuthenticationService>();
|
||||
var requestDelegate = new Mock<RequestDelegate>();
|
||||
var httpContext = CreateHttpContext(authenticationServiceMock.Object);
|
||||
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
|
||||
var policyAuthorizationResult = PolicyAuthorizationResult.Challenge();
|
||||
var handler = CreateAuthorizationMiddlewareResultHandler();
|
||||
|
||||
await handler.HandleAsync(requestDelegate.Object, httpContext, policy, policyAuthorizationResult);
|
||||
|
||||
authenticationServiceMock.Verify(service => service.ChallengeAsync(httpContext, null, null), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ForbidEachAuthenticationScheme_If_PolicyAuthorizationResultWasForbidden()
|
||||
{
|
||||
var authenticationServiceMock = new Mock<IAuthenticationService>();
|
||||
var requestDelegate = new Mock<RequestDelegate>();
|
||||
var httpContext = CreateHttpContext(authenticationServiceMock.Object);
|
||||
var firstScheme = Guid.NewGuid().ToString();
|
||||
var secondScheme = Guid.NewGuid().ToString();
|
||||
var thirdScheme = Guid.NewGuid().ToString();
|
||||
var policy = new AuthorizationPolicyBuilder()
|
||||
.RequireAuthenticatedUser()
|
||||
.AddAuthenticationSchemes(firstScheme, secondScheme, thirdScheme)
|
||||
.Build();
|
||||
var policyAuthorizationResult = PolicyAuthorizationResult.Forbid();
|
||||
var handler = CreateAuthorizationMiddlewareResultHandler();
|
||||
|
||||
await handler.HandleAsync(requestDelegate.Object, httpContext, policy, policyAuthorizationResult);
|
||||
|
||||
authenticationServiceMock.Verify(service => service.ForbidAsync(httpContext, It.IsAny<string>(), null), Times.Exactly(3));
|
||||
authenticationServiceMock.Verify(service => service.ForbidAsync(httpContext, firstScheme, null), Times.Once);
|
||||
authenticationServiceMock.Verify(service => service.ForbidAsync(httpContext, secondScheme, null), Times.Once);
|
||||
authenticationServiceMock.Verify(service => service.ForbidAsync(httpContext, thirdScheme, null), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ForbidWithoutAuthenticationScheme_If_PolicyAuthorizationResultWasForbidden()
|
||||
{
|
||||
var authenticationServiceMock = new Mock<IAuthenticationService>();
|
||||
var requestDelegate = new Mock<RequestDelegate>();
|
||||
var httpContext = CreateHttpContext(authenticationServiceMock.Object);
|
||||
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
|
||||
var policyAuthorizationResult = PolicyAuthorizationResult.Forbid();
|
||||
var handler = CreateAuthorizationMiddlewareResultHandler();
|
||||
|
||||
await handler.HandleAsync(requestDelegate.Object, httpContext, policy, policyAuthorizationResult);
|
||||
|
||||
authenticationServiceMock.Verify(service => service.ForbidAsync(httpContext, null, null), Times.Once);
|
||||
}
|
||||
|
||||
private HttpContext CreateHttpContext(IAuthenticationService authenticationService = null)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services.AddTransient(provider => authenticationService ?? new Mock<IAuthenticationService>().Object);
|
||||
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
return new DefaultHttpContext { RequestServices = serviceProvider };
|
||||
}
|
||||
|
||||
private AuthorizationMiddlewareResultHandler CreateAuthorizationMiddlewareResultHandler() => new AuthorizationMiddlewareResultHandler();
|
||||
}
|
||||
}
|
||||
|
|
@ -5,9 +5,9 @@ using System;
|
|||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization.Policy;
|
||||
using Microsoft.AspNetCore.Authorization.Test.TestObjects;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -449,6 +449,7 @@ namespace Microsoft.AspNetCore.Authorization.Test
|
|||
authenticationService = authenticationService ?? Mock.Of<IAuthenticationService>();
|
||||
|
||||
serviceCollection.AddSingleton(authenticationService);
|
||||
serviceCollection.AddTransient<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();
|
||||
serviceCollection.AddOptions();
|
||||
serviceCollection.AddLogging();
|
||||
serviceCollection.AddAuthorization();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authorization.Policy.Test
|
||||
|
|
@ -120,6 +121,28 @@ namespace Microsoft.AspNetCore.Authorization.Policy.Test
|
|||
Assert.True(result.Forbidden);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AuthorizeForbidsAndFailureIsIncludedIfAuthenticationSuceeds()
|
||||
{
|
||||
// Arrange
|
||||
var evaluator = BuildEvaluator();
|
||||
var context = new DefaultHttpContext();
|
||||
var policy = new AuthorizationPolicyBuilder()
|
||||
.AddRequirements(new DummyRequirement())
|
||||
.RequireAssertion(_ => false)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var result = await evaluator.AuthorizeAsync(policy, AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(), "scheme")), context, resource: null);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.Succeeded);
|
||||
Assert.False(result.Challenged);
|
||||
Assert.True(result.Forbidden);
|
||||
Assert.NotNull(result.AuthorizationFailure);
|
||||
Assert.Contains(result.AuthorizationFailure.FailedRequirements, requirement => requirement is DummyRequirement);
|
||||
}
|
||||
|
||||
private IPolicyEvaluator BuildEvaluator(Action<IServiceCollection> setupServices = null)
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
|
|
@ -204,5 +227,6 @@ namespace Microsoft.AspNetCore.Authorization.Policy.Test
|
|||
}
|
||||
}
|
||||
|
||||
private class DummyRequirement : IAuthorizationRequirement {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace CustomAuthorizationFailureResponse.Authentication
|
||||
{
|
||||
public class SampleAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||
{
|
||||
private readonly ClaimsPrincipal _id;
|
||||
|
||||
public SampleAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
|
||||
{
|
||||
_id = new ClaimsPrincipal(new ClaimsIdentity("Api"));
|
||||
}
|
||||
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
=> Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(_id, "Api")));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace CustomAuthorizationFailureResponse.Authentication
|
||||
{
|
||||
public static class SampleAuthenticationSchemes
|
||||
{
|
||||
public const string CustomScheme = "CustomScheme";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using System.Threading.Tasks;
|
||||
using CustomAuthorizationFailureResponse.Authorization.Requirements;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace CustomAuthorizationFailureResponse.Authorization.Handlers
|
||||
{
|
||||
public class SampleRequirementHandler : AuthorizationHandler<SampleRequirement>
|
||||
{
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SampleRequirement requirement)
|
||||
{
|
||||
// assuming the requirement was not met
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using System.Threading.Tasks;
|
||||
using CustomAuthorizationFailureResponse.Authorization.Requirements;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace CustomAuthorizationFailureResponse.Authorization.Handlers
|
||||
{
|
||||
public class SampleWithCustomMessageRequirementHandler : AuthorizationHandler<SampleWithCustomMessageRequirement>
|
||||
{
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SampleWithCustomMessageRequirement requirement)
|
||||
{
|
||||
// assuming the requirement was not met
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace CustomAuthorizationFailureResponse.Authorization.Requirements
|
||||
{
|
||||
public class SampleRequirement : IAuthorizationRequirement
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace CustomAuthorizationFailureResponse.Authorization.Requirements
|
||||
{
|
||||
public class SampleWithCustomMessageRequirement : IAuthorizationRequirement
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CustomAuthorizationFailureResponse.Authorization.Requirements;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Authorization.Policy;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace CustomAuthorizationFailureResponse.Authorization
|
||||
{
|
||||
public class SampleAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
|
||||
{
|
||||
private readonly IAuthorizationMiddlewareResultHandler _handler;
|
||||
|
||||
public SampleAuthorizationMiddlewareResultHandler(IAuthorizationMiddlewareResultHandler handler)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public async Task HandleAsync(
|
||||
RequestDelegate requestDelegate,
|
||||
HttpContext httpContext,
|
||||
AuthorizationPolicy authorizationPolicy,
|
||||
PolicyAuthorizationResult policyAuthorizationResult)
|
||||
{
|
||||
// if the authorization was forbidden, let's use custom logic to handle that.
|
||||
if (policyAuthorizationResult.Forbidden && policyAuthorizationResult.AuthorizationFailure != null)
|
||||
{
|
||||
// as an example, let's return 404 if specific requirement has failed
|
||||
if (policyAuthorizationResult.AuthorizationFailure.FailedRequirements.Any(requirement => requirement is SampleRequirement))
|
||||
{
|
||||
httpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
await httpContext.Response.WriteAsync(Startup.CustomForbiddenMessage);
|
||||
|
||||
// return right away as the default implementation would overwrite the status code
|
||||
return;
|
||||
}
|
||||
else if (policyAuthorizationResult.AuthorizationFailure.FailedRequirements.Any(requirement => requirement is SampleWithCustomMessageRequirement))
|
||||
{
|
||||
// if other requirements failed, let's just use a custom message
|
||||
// but we have to use OnStarting callback because the default handlers will want to modify i.e. status code of the response
|
||||
// and modifications of the response are not allowed once the writing has started
|
||||
var message = Startup.CustomForbiddenMessage;
|
||||
|
||||
httpContext.Response.OnStarting(() => httpContext.Response.BodyWriter.WriteAsync(Encoding.UTF8.GetBytes(message)).AsTask());
|
||||
}
|
||||
}
|
||||
|
||||
await _handler.HandleAsync(requestDelegate, httpContext, authorizationPolicy, policyAuthorizationResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
namespace CustomAuthorizationFailureResponse.Authorization
|
||||
{
|
||||
public static class SamplePolicyNames
|
||||
{
|
||||
public const string CustomPolicy = "Custom";
|
||||
public const string CustomPolicyWithCustomForbiddenMessage = "CustomPolicyWithCustomForbiddenMessage";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
using CustomAuthorizationFailureResponse.Authorization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CustomAuthorizationFailureResponse.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class SampleController : ControllerBase
|
||||
{
|
||||
[HttpGet("customPolicyWithCustomForbiddenMessage")]
|
||||
[Authorize(Policy = SamplePolicyNames.CustomPolicyWithCustomForbiddenMessage)]
|
||||
public string GetWithCustomPolicyWithCustomForbiddenMessage()
|
||||
{
|
||||
return "Hello world from GetWithCustomPolicyWithCustomForbiddenMessage";
|
||||
}
|
||||
|
||||
[HttpGet("customPolicy")]
|
||||
[Authorize(Policy = SamplePolicyNames.CustomPolicy)]
|
||||
public string GetWithCustomPolicy()
|
||||
{
|
||||
return "Hello world from GetWithCustomPolicy";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
||||
<IsTestAssetProject>true</IsTestAssetProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.AspNetCore" />
|
||||
<Reference Include="Microsoft.AspNetCore.Authentication" />
|
||||
<Reference Include="Microsoft.AspNetCore.Authorization" />
|
||||
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace CustomAuthorizationFailureResponse.Extensions
|
||||
{
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection Decorate<TServiceType, TServiceImplementation>(this IServiceCollection services)
|
||||
{
|
||||
var descriptors = services.Where(descriptor => descriptor.ServiceType == typeof(TServiceType)).ToList();
|
||||
foreach(var descriptor in descriptors)
|
||||
{
|
||||
var index = services.IndexOf(descriptor);
|
||||
services[index] = ServiceDescriptor.Describe(typeof(TServiceType), provider => ActivatorUtilities.CreateInstance(provider, typeof(TServiceImplementation), ActivatorUtilities.GetServiceOrCreateInstance(provider, descriptor.ImplementationType)), descriptor.Lifetime);
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace CustomAuthorizationFailureResponse
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
using CustomAuthorizationFailureResponse.Authentication;
|
||||
using CustomAuthorizationFailureResponse.Authorization;
|
||||
using CustomAuthorizationFailureResponse.Authorization.Handlers;
|
||||
using CustomAuthorizationFailureResponse.Authorization.Requirements;
|
||||
using CustomAuthorizationFailureResponse.Extensions;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace CustomAuthorizationFailureResponse
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public const string CustomForbiddenMessage = "Some info about the error";
|
||||
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddControllers();
|
||||
|
||||
services
|
||||
.AddAuthentication(SampleAuthenticationSchemes.CustomScheme)
|
||||
.AddScheme<AuthenticationSchemeOptions, SampleAuthenticationHandler>(SampleAuthenticationSchemes.CustomScheme, o => { });
|
||||
|
||||
services.AddAuthorization(options => options.AddPolicy(SamplePolicyNames.CustomPolicy, policy => policy.AddRequirements(new SampleRequirement())));
|
||||
services.AddAuthorization(options => options.AddPolicy(SamplePolicyNames.CustomPolicyWithCustomForbiddenMessage, policy => policy.AddRequirements(new SampleWithCustomMessageRequirement())));
|
||||
|
||||
services.AddTransient<IAuthorizationHandler, SampleRequirementHandler>();
|
||||
services.Decorate<IAuthorizationMiddlewareResultHandler, SampleAuthorizationMiddlewareResultHandler>();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapDefaultControllerRoute();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
<ProjectReference Include="$(RepoRoot)src\Hosting\Server.IntegrationTesting\src\Microsoft.AspNetCore.Server.IntegrationTesting.csproj" />
|
||||
<ProjectReference Include="..\..\samples\Cookies\Cookies.csproj" />
|
||||
<ProjectReference Include="..\..\samples\ClaimsTransformation\ClaimsTransformation.csproj" />
|
||||
<ProjectReference Include="..\..\samples\CustomAuthorizationFailureResponse\CustomAuthorizationFailureResponse.csproj" />
|
||||
<ProjectReference Include="..\..\samples\CustomPolicyProvider\CustomPolicyProvider.csproj" />
|
||||
<ProjectReference Include="..\..\samples\DynamicSchemes\DynamicSchemes.csproj" />
|
||||
<ProjectReference Include="..\..\samples\Identity.ExternalClaims\Identity.ExternalClaims.csproj" />
|
||||
|
|
@ -33,6 +34,7 @@
|
|||
<ItemGroup>
|
||||
<_PublishFiles Include="$(ArtifactsBinDir)ClaimsTransformation\$(Configuration)\$(DefaultNetCoreTargetFramework)\ClaimsTransformation.deps.json" />
|
||||
<_PublishFiles Include="$(ArtifactsBinDir)Cookies\$(Configuration)\$(DefaultNetCoreTargetFramework)\Cookies.deps.json" />
|
||||
<_PublishFiles Include="$(ArtifactsBinDir)CustomAuthorizationFailureResponse\$(Configuration)\$(DefaultNetCoreTargetFramework)\CustomAuthorizationFailureResponse.deps.json" />
|
||||
<_PublishFiles Include="$(ArtifactsBinDir)CustomPolicyProvider\$(Configuration)\$(DefaultNetCoreTargetFramework)\CustomPolicyProvider.deps.json" />
|
||||
<_PublishFiles Include="$(ArtifactsBinDir)DynamicSchemes\$(Configuration)\$(DefaultNetCoreTargetFramework)\DynamicSchemes.deps.json" />
|
||||
<_PublishFiles Include="$(ArtifactsBinDir)Identity.ExternalClaims\$(Configuration)\$(DefaultNetCoreTargetFramework)\Identity.ExternalClaims.deps.json" />
|
||||
|
|
@ -40,43 +42,25 @@
|
|||
<_PublishFiles Include="$(ArtifactsBinDir)StaticFilesAuth\$(Configuration)\$(DefaultNetCoreTargetFramework)\StaticFilesAuth.deps.json" />
|
||||
<_claimsWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\ClaimsTransformation\wwwroot\**\*.*" />
|
||||
<_cookiesWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\Cookies\wwwroot\**\*.*" />
|
||||
<_customAuthorizationFailureResponseFiles Include="$(MSBuildThisFileDirectory)..\..\samples\CustomAuthorizationFailureResponse\**\*.*" />
|
||||
<_customProviderFiles Include="$(MSBuildThisFileDirectory)..\..\samples\CustomPolicyProvider\**\*.*" />
|
||||
<_schemesWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\DynamicSchemes\wwwroot\**\*.*" />
|
||||
<_identityWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\Identity.ExternalClaims\wwwroot\**\*.*" />
|
||||
<_pathWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\PathSchemeSelection\wwwroot\**\*.*" />
|
||||
<_staticFiles Include="$(MSBuildThisFileDirectory)..\..\samples\StaticFilesAuth\**\*.*" />
|
||||
</ItemGroup>
|
||||
<Copy
|
||||
SourceFiles="@(_PublishFiles)"
|
||||
DestinationFolder="$(PublishDir)" />
|
||||
<Copy
|
||||
SourceFiles="@(_claimsWwwrootFiles)"
|
||||
DestinationFolder="$(PublishDir)\ClaimsTransformation\wwwroot" />
|
||||
<Copy
|
||||
SourceFiles="@(_cookiesWwwrootFiles)"
|
||||
DestinationFolder="$(PublishDir)\Cookies\wwwroot" />
|
||||
<Copy
|
||||
SourceFiles="@(_customProviderFiles)"
|
||||
DestinationFolder="$(PublishDir)\CustomPolicyProvider\\%(RecursiveDir)" />
|
||||
<Copy
|
||||
SourceFiles="@(_schemesWwwrootFiles)"
|
||||
DestinationFolder="$(PublishDir)\DynamicSchemes\wwwroot" />
|
||||
<Copy
|
||||
SourceFiles="@(_pathWwwrootFiles)"
|
||||
DestinationFolder="$(PublishDir)\Identity.ExternalClaims\wwwroot" />
|
||||
<Copy
|
||||
SourceFiles="@(_schemesWwwrootFiles)"
|
||||
DestinationFolder="$(PublishDir)\PathSchemeSelection\wwwroot" />
|
||||
<Copy
|
||||
SourceFiles="@(_staticFiles)"
|
||||
DestinationFolder="$(PublishDir)\StaticFilesAuth\\%(RecursiveDir)" />
|
||||
<Copy SourceFiles="@(_PublishFiles)" DestinationFolder="$(PublishDir)" />
|
||||
<Copy SourceFiles="@(_claimsWwwrootFiles)" DestinationFolder="$(PublishDir)\ClaimsTransformation\wwwroot" />
|
||||
<Copy SourceFiles="@(_cookiesWwwrootFiles)" DestinationFolder="$(PublishDir)\Cookies\wwwroot" />
|
||||
<Copy SourceFiles="@(_customAuthorizationFailureResponseFiles)" DestinationFolder="$(PublishDir)\CustomAuthorizationFailureResponse\\%(RecursiveDir)" />
|
||||
<Copy SourceFiles="@(_customProviderFiles)" DestinationFolder="$(PublishDir)\CustomPolicyProvider\\%(RecursiveDir)" />
|
||||
<Copy SourceFiles="@(_schemesWwwrootFiles)" DestinationFolder="$(PublishDir)\DynamicSchemes\wwwroot" />
|
||||
<Copy SourceFiles="@(_pathWwwrootFiles)" DestinationFolder="$(PublishDir)\Identity.ExternalClaims\wwwroot" />
|
||||
<Copy SourceFiles="@(_schemesWwwrootFiles)" DestinationFolder="$(PublishDir)\PathSchemeSelection\wwwroot" />
|
||||
<Copy SourceFiles="@(_staticFiles)" DestinationFolder="$(PublishDir)\StaticFilesAuth\\%(RecursiveDir)" />
|
||||
|
||||
<!-- Drop a dummy sln to specify content root location -->
|
||||
<WriteLinesToFile
|
||||
File="$(PublishDir)\contentroot.sln"
|
||||
Lines="Ignored"
|
||||
Overwrite="true"
|
||||
Encoding="Unicode" />
|
||||
<WriteLinesToFile File="$(PublishDir)\contentroot.sln" Lines="Ignored" Overwrite="true" Encoding="Unicode" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
// 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.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace AuthSamples.FunctionalTests
|
||||
{
|
||||
public class CustomAuthorizationFailureResponseTests : IClassFixture<WebApplicationFactory<CustomAuthorizationFailureResponse.Startup>>
|
||||
{
|
||||
private HttpClient Client { get; }
|
||||
|
||||
public CustomAuthorizationFailureResponseTests(WebApplicationFactory<CustomAuthorizationFailureResponse.Startup> fixture)
|
||||
{
|
||||
Client = fixture.CreateClient();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SampleGetWithCustomPolicyWithCustomForbiddenMessage_Returns403WithCustomMessage()
|
||||
{
|
||||
var response = await Client.GetAsync("api/Sample/customPolicyWithCustomForbiddenMessage");
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
||||
Assert.Equal(CustomAuthorizationFailureResponse.Startup.CustomForbiddenMessage, content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SampleGetWithCustomPolicy_Returns404WithCustomMessage()
|
||||
{
|
||||
var response = await Client.GetAsync("api/Sample/customPolicy");
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
Assert.Equal(CustomAuthorizationFailureResponse.Startup.CustomForbiddenMessage, content);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue