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
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StaticFilesAuth", "samples\StaticFilesAuth\StaticFilesAuth.csproj", "{E1E8A599-AB42-4551-8C24-BE4404B65283}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StaticFilesAuth", "samples\StaticFilesAuth\StaticFilesAuth.csproj", "{E1E8A599-AB42-4551-8C24-BE4404B65283}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomAuthorizationFailureResponse", "samples\CustomAuthorizationFailureResponse\CustomAuthorizationFailureResponse.csproj", "{EA51BBBC-58AC-42F8-97C1-5CF3C9725513}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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|x64.Build.0 = Release|Any CPU
|
||||||
{E1E8A599-AB42-4551-8C24-BE4404B65283}.Release|x86.ActiveCfg = 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
|
{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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -434,6 +448,7 @@ Global
|
||||||
{82C0816D-7051-4DDB-9B9E-6777973AD7AE} = {142C8260-90B5-4D72-9564-17BFDD72F496}
|
{82C0816D-7051-4DDB-9B9E-6777973AD7AE} = {142C8260-90B5-4D72-9564-17BFDD72F496}
|
||||||
{38C0E122-64D0-497F-ABB0-C6A9C3349F02} = {CA4538F5-9DA8-4139-B891-A13279889F79}
|
{38C0E122-64D0-497F-ABB0-C6A9C3349F02} = {CA4538F5-9DA8-4139-B891-A13279889F79}
|
||||||
{E1E8A599-AB42-4551-8C24-BE4404B65283} = {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
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {39E3AF62-B1FD-4156-92AA-F4FA99B5AD89}
|
SolutionGuid = {39E3AF62-B1FD-4156-92AA-F4FA99B5AD89}
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,19 @@ namespace Microsoft.AspNetCore.Authorization
|
||||||
[System.Diagnostics.DebuggerStepThroughAttribute]
|
[System.Diagnostics.DebuggerStepThroughAttribute]
|
||||||
public System.Threading.Tasks.Task Invoke(Microsoft.AspNetCore.Http.HttpContext context) { throw null; }
|
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
|
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
|
public partial interface IPolicyEvaluator
|
||||||
{
|
{
|
||||||
System.Threading.Tasks.Task<Microsoft.AspNetCore.Authentication.AuthenticateResult> AuthenticateAsync(Microsoft.AspNetCore.Authorization.AuthorizationPolicy policy, Microsoft.AspNetCore.Http.HttpContext context);
|
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
|
public partial class PolicyAuthorizationResult
|
||||||
{
|
{
|
||||||
internal 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 Challenged { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||||
public bool Forbidden { [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 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 Challenge() { throw null; }
|
||||||
public static Microsoft.AspNetCore.Authorization.Policy.PolicyAuthorizationResult Forbid() { 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 static Microsoft.AspNetCore.Authorization.Policy.PolicyAuthorizationResult Success() { throw null; }
|
||||||
}
|
}
|
||||||
public partial class PolicyEvaluator : Microsoft.AspNetCore.Authorization.Policy.IPolicyEvaluator
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Authorization.Policy;
|
using Microsoft.AspNetCore.Authorization.Policy;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
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
|
// Note that the resource will be null if there is no matched endpoint
|
||||||
var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult, context, resource: endpoint);
|
var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult, context, resource: endpoint);
|
||||||
|
|
||||||
if (authorizeResult.Challenged)
|
var authorizationMiddlewareResultHandler = context.RequestServices.GetRequiredService<IAuthorizationMiddlewareResultHandler>();
|
||||||
{
|
await authorizationMiddlewareResultHandler.HandleAsync(_next, context, policy, authorizeResult);
|
||||||
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,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,7 +33,7 @@ namespace Microsoft.AspNetCore.Authorization.Policy
|
||||||
/// If a resource is not required for policy evaluation you may pass null as the value.
|
/// If a resource is not required for policy evaluation you may pass null as the value.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <returns>Returns <see cref="PolicyAuthorizationResult.Success"/> if authorization succeeds.
|
/// <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>
|
/// returns <see cref="PolicyAuthorizationResult.Challenge"/></returns>
|
||||||
Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource);
|
Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,19 @@ namespace Microsoft.AspNetCore.Authorization.Policy
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Succeeded { get; private set; }
|
public bool Succeeded { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains information about why authorization failed.
|
||||||
|
/// </summary>
|
||||||
|
public AuthorizationFailure AuthorizationFailure { get; private set; }
|
||||||
|
|
||||||
public static PolicyAuthorizationResult Challenge()
|
public static PolicyAuthorizationResult Challenge()
|
||||||
=> new PolicyAuthorizationResult { Challenged = true };
|
=> new PolicyAuthorizationResult { Challenged = true };
|
||||||
|
|
||||||
public static PolicyAuthorizationResult Forbid()
|
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()
|
public static PolicyAuthorizationResult Success()
|
||||||
=> new PolicyAuthorizationResult { Succeeded = true };
|
=> new PolicyAuthorizationResult { Succeeded = true };
|
||||||
|
|
|
||||||
|
|
@ -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.
|
/// If a resource is not required for policy evaluation you may pass null as the value.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <returns>Returns <see cref="PolicyAuthorizationResult.Success"/> if authorization succeeds.
|
/// <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>
|
/// returns <see cref="PolicyAuthorizationResult.Challenge"/></returns>
|
||||||
public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource)
|
public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource)
|
||||||
{
|
{
|
||||||
|
|
@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Authorization.Policy
|
||||||
|
|
||||||
// If authentication was successful, return forbidden, otherwise challenge
|
// If authentication was successful, return forbidden, otherwise challenge
|
||||||
return (authenticationResult.Succeeded)
|
return (authenticationResult.Succeeded)
|
||||||
? PolicyAuthorizationResult.Forbid()
|
? PolicyAuthorizationResult.Forbid(result.Failure)
|
||||||
: PolicyAuthorizationResult.Challenge();
|
: PolicyAuthorizationResult.Challenge();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
}
|
}
|
||||||
|
|
||||||
services.TryAddSingleton<AuthorizationPolicyMarkerService>();
|
services.TryAddSingleton<AuthorizationPolicyMarkerService>();
|
||||||
services.TryAdd(ServiceDescriptor.Transient<IPolicyEvaluator, PolicyEvaluator>());
|
services.TryAddTransient<IPolicyEvaluator, PolicyEvaluator>();
|
||||||
|
services.TryAddTransient<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();
|
||||||
return services;
|
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.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authorization.Policy;
|
||||||
using Microsoft.AspNetCore.Authorization.Test.TestObjects;
|
using Microsoft.AspNetCore.Authorization.Test.TestObjects;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -449,6 +449,7 @@ namespace Microsoft.AspNetCore.Authorization.Test
|
||||||
authenticationService = authenticationService ?? Mock.Of<IAuthenticationService>();
|
authenticationService = authenticationService ?? Mock.Of<IAuthenticationService>();
|
||||||
|
|
||||||
serviceCollection.AddSingleton(authenticationService);
|
serviceCollection.AddSingleton(authenticationService);
|
||||||
|
serviceCollection.AddTransient<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();
|
||||||
serviceCollection.AddOptions();
|
serviceCollection.AddOptions();
|
||||||
serviceCollection.AddLogging();
|
serviceCollection.AddLogging();
|
||||||
serviceCollection.AddAuthorization();
|
serviceCollection.AddAuthorization();
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Authorization.Policy.Test
|
namespace Microsoft.AspNetCore.Authorization.Policy.Test
|
||||||
|
|
@ -120,6 +121,28 @@ namespace Microsoft.AspNetCore.Authorization.Policy.Test
|
||||||
Assert.True(result.Forbidden);
|
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)
|
private IPolicyEvaluator BuildEvaluator(Action<IServiceCollection> setupServices = null)
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection()
|
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="$(RepoRoot)src\Hosting\Server.IntegrationTesting\src\Microsoft.AspNetCore.Server.IntegrationTesting.csproj" />
|
||||||
<ProjectReference Include="..\..\samples\Cookies\Cookies.csproj" />
|
<ProjectReference Include="..\..\samples\Cookies\Cookies.csproj" />
|
||||||
<ProjectReference Include="..\..\samples\ClaimsTransformation\ClaimsTransformation.csproj" />
|
<ProjectReference Include="..\..\samples\ClaimsTransformation\ClaimsTransformation.csproj" />
|
||||||
|
<ProjectReference Include="..\..\samples\CustomAuthorizationFailureResponse\CustomAuthorizationFailureResponse.csproj" />
|
||||||
<ProjectReference Include="..\..\samples\CustomPolicyProvider\CustomPolicyProvider.csproj" />
|
<ProjectReference Include="..\..\samples\CustomPolicyProvider\CustomPolicyProvider.csproj" />
|
||||||
<ProjectReference Include="..\..\samples\DynamicSchemes\DynamicSchemes.csproj" />
|
<ProjectReference Include="..\..\samples\DynamicSchemes\DynamicSchemes.csproj" />
|
||||||
<ProjectReference Include="..\..\samples\Identity.ExternalClaims\Identity.ExternalClaims.csproj" />
|
<ProjectReference Include="..\..\samples\Identity.ExternalClaims\Identity.ExternalClaims.csproj" />
|
||||||
|
|
@ -33,6 +34,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<_PublishFiles Include="$(ArtifactsBinDir)ClaimsTransformation\$(Configuration)\$(DefaultNetCoreTargetFramework)\ClaimsTransformation.deps.json" />
|
<_PublishFiles Include="$(ArtifactsBinDir)ClaimsTransformation\$(Configuration)\$(DefaultNetCoreTargetFramework)\ClaimsTransformation.deps.json" />
|
||||||
<_PublishFiles Include="$(ArtifactsBinDir)Cookies\$(Configuration)\$(DefaultNetCoreTargetFramework)\Cookies.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)CustomPolicyProvider\$(Configuration)\$(DefaultNetCoreTargetFramework)\CustomPolicyProvider.deps.json" />
|
||||||
<_PublishFiles Include="$(ArtifactsBinDir)DynamicSchemes\$(Configuration)\$(DefaultNetCoreTargetFramework)\DynamicSchemes.deps.json" />
|
<_PublishFiles Include="$(ArtifactsBinDir)DynamicSchemes\$(Configuration)\$(DefaultNetCoreTargetFramework)\DynamicSchemes.deps.json" />
|
||||||
<_PublishFiles Include="$(ArtifactsBinDir)Identity.ExternalClaims\$(Configuration)\$(DefaultNetCoreTargetFramework)\Identity.ExternalClaims.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" />
|
<_PublishFiles Include="$(ArtifactsBinDir)StaticFilesAuth\$(Configuration)\$(DefaultNetCoreTargetFramework)\StaticFilesAuth.deps.json" />
|
||||||
<_claimsWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\ClaimsTransformation\wwwroot\**\*.*" />
|
<_claimsWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\ClaimsTransformation\wwwroot\**\*.*" />
|
||||||
<_cookiesWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\Cookies\wwwroot\**\*.*" />
|
<_cookiesWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\Cookies\wwwroot\**\*.*" />
|
||||||
|
<_customAuthorizationFailureResponseFiles Include="$(MSBuildThisFileDirectory)..\..\samples\CustomAuthorizationFailureResponse\**\*.*" />
|
||||||
<_customProviderFiles Include="$(MSBuildThisFileDirectory)..\..\samples\CustomPolicyProvider\**\*.*" />
|
<_customProviderFiles Include="$(MSBuildThisFileDirectory)..\..\samples\CustomPolicyProvider\**\*.*" />
|
||||||
<_schemesWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\DynamicSchemes\wwwroot\**\*.*" />
|
<_schemesWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\DynamicSchemes\wwwroot\**\*.*" />
|
||||||
<_identityWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\Identity.ExternalClaims\wwwroot\**\*.*" />
|
<_identityWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\Identity.ExternalClaims\wwwroot\**\*.*" />
|
||||||
<_pathWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\PathSchemeSelection\wwwroot\**\*.*" />
|
<_pathWwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\samples\PathSchemeSelection\wwwroot\**\*.*" />
|
||||||
<_staticFiles Include="$(MSBuildThisFileDirectory)..\..\samples\StaticFilesAuth\**\*.*" />
|
<_staticFiles Include="$(MSBuildThisFileDirectory)..\..\samples\StaticFilesAuth\**\*.*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Copy
|
<Copy SourceFiles="@(_PublishFiles)" DestinationFolder="$(PublishDir)" />
|
||||||
SourceFiles="@(_PublishFiles)"
|
<Copy SourceFiles="@(_claimsWwwrootFiles)" DestinationFolder="$(PublishDir)\ClaimsTransformation\wwwroot" />
|
||||||
DestinationFolder="$(PublishDir)" />
|
<Copy SourceFiles="@(_cookiesWwwrootFiles)" DestinationFolder="$(PublishDir)\Cookies\wwwroot" />
|
||||||
<Copy
|
<Copy SourceFiles="@(_customAuthorizationFailureResponseFiles)" DestinationFolder="$(PublishDir)\CustomAuthorizationFailureResponse\\%(RecursiveDir)" />
|
||||||
SourceFiles="@(_claimsWwwrootFiles)"
|
<Copy SourceFiles="@(_customProviderFiles)" DestinationFolder="$(PublishDir)\CustomPolicyProvider\\%(RecursiveDir)" />
|
||||||
DestinationFolder="$(PublishDir)\ClaimsTransformation\wwwroot" />
|
<Copy SourceFiles="@(_schemesWwwrootFiles)" DestinationFolder="$(PublishDir)\DynamicSchemes\wwwroot" />
|
||||||
<Copy
|
<Copy SourceFiles="@(_pathWwwrootFiles)" DestinationFolder="$(PublishDir)\Identity.ExternalClaims\wwwroot" />
|
||||||
SourceFiles="@(_cookiesWwwrootFiles)"
|
<Copy SourceFiles="@(_schemesWwwrootFiles)" DestinationFolder="$(PublishDir)\PathSchemeSelection\wwwroot" />
|
||||||
DestinationFolder="$(PublishDir)\Cookies\wwwroot" />
|
<Copy SourceFiles="@(_staticFiles)" DestinationFolder="$(PublishDir)\StaticFilesAuth\\%(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 -->
|
<!-- Drop a dummy sln to specify content root location -->
|
||||||
<WriteLinesToFile
|
<WriteLinesToFile File="$(PublishDir)\contentroot.sln" Lines="Ignored" Overwrite="true" Encoding="Unicode" />
|
||||||
File="$(PublishDir)\contentroot.sln"
|
|
||||||
Lines="Ignored"
|
|
||||||
Overwrite="true"
|
|
||||||
Encoding="Unicode" />
|
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</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