Update AuthorizeFilter to no-op when AuthorizationMiddleware has run (#6346)

This commit is contained in:
James Newton-King 2019-01-15 20:29:23 +13:00 committed by GitHub
parent 0e1cb6d6c2
commit 09b50850bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 46 additions and 4 deletions

View File

@ -16,8 +16,9 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
/// </summary>
public class CorsMiddleware
{
// Property key is used by MVC filters to check if CORS middleware has run
// Property key is used by other systems, e.g. MVC, to check if CORS middleware has run
private const string CorsMiddlewareInvokedKey = "__CorsMiddlewareInvoked";
private static readonly object CorsMiddlewareInvokedValue = new object();
private readonly Func<object, Task> OnResponseStartingDelegate = OnResponseStarting;
private readonly RequestDelegate _next;
@ -137,7 +138,7 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
// 3. If there is no policy on middleware then use name on middleware
// Flag to indicate to other systems, e.g. MVC, that CORS middleware was run for this request
context.Items[CorsMiddlewareInvokedKey] = true;
context.Items[CorsMiddlewareInvokedKey] = CorsMiddlewareInvokedValue;
var endpoint = context.GetEndpoint();

View File

@ -22,6 +22,9 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
/// </summary>
public class AuthorizeFilter : IAsyncAuthorizationFilter, IFilterFactory
{
// Property key set by authorization middleware when it is run
private const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareInvoked";
private AuthorizationPolicy _effectivePolicy;
/// <summary>
@ -170,11 +173,18 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
throw new ArgumentNullException(nameof(context));
}
if (context.HttpContext.Items.ContainsKey(AuthorizationMiddlewareInvokedKey))
{
// Authorization has already run in middleware. Don't re-run for performance
return;
}
if (!context.IsEffectivePolicy(this))
{
return;
}
// IMPORTANT: Changes to authorization logic should be mirrored in security's AuthorizationMiddleware
var effectivePolicy = await GetEffectivePolicyAsync(context);
if (effectivePolicy == null)
{

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
@ -48,6 +49,26 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
Assert.IsType<ChallengeResult>(authorizationContext.Result);
}
[Fact]
public async Task OnAuthorizationAsync_AuthorizationMiddlewareHasRun_NoOp()
{
// Arrange
var authorizationContext = GetAuthorizationContext(anonymous: true);
authorizationContext.HttpContext.Items["__AuthorizationMiddlewareInvoked"] = new object();
var authorizeFilterFactory = new AuthorizeFilter();
var filterFactory = authorizeFilterFactory as IFilterFactory;
var authorizeFilter = (AuthorizeFilter)filterFactory.CreateInstance(
authorizationContext.HttpContext.RequestServices);
authorizationContext.Filters.Add(authorizeFilter);
// Act
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.Null(authorizationContext.Result);
}
[Fact]
public async Task AuthorizeFilter_CreatedWithAuthorizeData_ThrowsWhenOnAuthorizationAsyncIsCalled()
{
@ -557,6 +578,8 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
httpContext.Object.User = validUser;
}
httpContext.SetupGet(c => c.RequestServices).Returns(serviceProvider);
var contextItems = new Dictionary<object, object>();
httpContext.SetupGet(c => c.Items).Returns(contextItems);
// AuthorizationFilterContext
var actionContext = new ActionContext(

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -15,6 +15,10 @@ namespace Microsoft.AspNetCore.Authorization
{
public class AuthorizationMiddleware
{
// Property key is used by other systems, e.g. MVC, to check if authorization middleware has run
private const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareInvoked";
private static readonly object AuthorizationMiddlewareInvokedValue = new object();
private readonly RequestDelegate _next;
private readonly IAuthorizationPolicyProvider _policyProvider;
@ -41,6 +45,10 @@ namespace Microsoft.AspNetCore.Authorization
throw new ArgumentNullException(nameof(context));
}
// Flag to indicate to other systems, e.g. MVC, that authorization middleware was run for this request
context.Items[AuthorizationMiddlewareInvokedKey] = AuthorizationMiddlewareInvokedValue;
// IMPORTANT: Changes to authorization logic should be mirrored in MVC's AuthorizeFilter
var endpoint = context.GetEndpoint();
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
@ -101,4 +109,4 @@ namespace Microsoft.AspNetCore.Authorization
await _next(context);
}
}
}
}