Skip to content

Commit 09b5085

Browse files
authored
Update AuthorizeFilter to no-op when AuthorizationMiddleware has run (#6346)
1 parent 0e1cb6d commit 09b5085

File tree

4 files changed

+46
-4
lines changed

4 files changed

+46
-4
lines changed

src/Middleware/CORS/src/Infrastructure/CorsMiddleware.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
1616
/// </summary>
1717
public class CorsMiddleware
1818
{
19-
// Property key is used by MVC filters to check if CORS middleware has run
19+
// Property key is used by other systems, e.g. MVC, to check if CORS middleware has run
2020
private const string CorsMiddlewareInvokedKey = "__CorsMiddlewareInvoked";
21+
private static readonly object CorsMiddlewareInvokedValue = new object();
2122

2223
private readonly Func<object, Task> OnResponseStartingDelegate = OnResponseStarting;
2324
private readonly RequestDelegate _next;
@@ -137,7 +138,7 @@ private async Task InvokeCore(HttpContext context, ICorsPolicyProvider corsPolic
137138
// 3. If there is no policy on middleware then use name on middleware
138139

139140
// Flag to indicate to other systems, e.g. MVC, that CORS middleware was run for this request
140-
context.Items[CorsMiddlewareInvokedKey] = true;
141+
context.Items[CorsMiddlewareInvokedKey] = CorsMiddlewareInvokedValue;
141142

142143
var endpoint = context.GetEndpoint();
143144

src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/Authorization/AuthorizeFilter.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
2222
/// </summary>
2323
public class AuthorizeFilter : IAsyncAuthorizationFilter, IFilterFactory
2424
{
25+
// Property key set by authorization middleware when it is run
26+
private const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareInvoked";
27+
2528
private AuthorizationPolicy _effectivePolicy;
2629

2730
/// <summary>
@@ -170,11 +173,18 @@ public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext contex
170173
throw new ArgumentNullException(nameof(context));
171174
}
172175

176+
if (context.HttpContext.Items.ContainsKey(AuthorizationMiddlewareInvokedKey))
177+
{
178+
// Authorization has already run in middleware. Don't re-run for performance
179+
return;
180+
}
181+
173182
if (!context.IsEffectivePolicy(this))
174183
{
175184
return;
176185
}
177186

187+
// IMPORTANT: Changes to authorization logic should be mirrored in security's AuthorizationMiddleware
178188
var effectivePolicy = await GetEffectivePolicyAsync(context);
179189
if (effectivePolicy == null)
180190
{

src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/Authorization/AuthorizeFilterTest.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.Linq;
67
using System.Security.Claims;
78
using System.Threading.Tasks;
@@ -48,6 +49,26 @@ public async Task DefaultConstructor_DeniesAnonymousUsers()
4849
Assert.IsType<ChallengeResult>(authorizationContext.Result);
4950
}
5051

52+
[Fact]
53+
public async Task OnAuthorizationAsync_AuthorizationMiddlewareHasRun_NoOp()
54+
{
55+
// Arrange
56+
var authorizationContext = GetAuthorizationContext(anonymous: true);
57+
authorizationContext.HttpContext.Items["__AuthorizationMiddlewareInvoked"] = new object();
58+
59+
var authorizeFilterFactory = new AuthorizeFilter();
60+
var filterFactory = authorizeFilterFactory as IFilterFactory;
61+
var authorizeFilter = (AuthorizeFilter)filterFactory.CreateInstance(
62+
authorizationContext.HttpContext.RequestServices);
63+
authorizationContext.Filters.Add(authorizeFilter);
64+
65+
// Act
66+
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
67+
68+
// Assert
69+
Assert.Null(authorizationContext.Result);
70+
}
71+
5172
[Fact]
5273
public async Task AuthorizeFilter_CreatedWithAuthorizeData_ThrowsWhenOnAuthorizationAsyncIsCalled()
5374
{
@@ -557,6 +578,8 @@ private AuthorizationFilterContext GetAuthorizationContext(
557578
httpContext.Object.User = validUser;
558579
}
559580
httpContext.SetupGet(c => c.RequestServices).Returns(serviceProvider);
581+
var contextItems = new Dictionary<object, object>();
582+
httpContext.SetupGet(c => c.Items).Returns(contextItems);
560583

561584
// AuthorizationFilterContext
562585
var actionContext = new ActionContext(

src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
@@ -15,6 +15,10 @@ namespace Microsoft.AspNetCore.Authorization
1515
{
1616
public class AuthorizationMiddleware
1717
{
18+
// Property key is used by other systems, e.g. MVC, to check if authorization middleware has run
19+
private const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareInvoked";
20+
private static readonly object AuthorizationMiddlewareInvokedValue = new object();
21+
1822
private readonly RequestDelegate _next;
1923
private readonly IAuthorizationPolicyProvider _policyProvider;
2024

@@ -41,6 +45,10 @@ public async Task Invoke(HttpContext context)
4145
throw new ArgumentNullException(nameof(context));
4246
}
4347

48+
// Flag to indicate to other systems, e.g. MVC, that authorization middleware was run for this request
49+
context.Items[AuthorizationMiddlewareInvokedKey] = AuthorizationMiddlewareInvokedValue;
50+
51+
// IMPORTANT: Changes to authorization logic should be mirrored in MVC's AuthorizeFilter
4452
var endpoint = context.GetEndpoint();
4553
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
4654
var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
@@ -101,4 +109,4 @@ public async Task Invoke(HttpContext context)
101109
await _next(context);
102110
}
103111
}
104-
}
112+
}

0 commit comments

Comments
 (0)