Skip to content

Commit 7aefe76

Browse files
committed
Complain if auth hasn't been set up correctly
Fixes #9041
1 parent b93bc43 commit 7aefe76

File tree

8 files changed

+147
-4
lines changed

8 files changed

+147
-4
lines changed

src/Security/Authorization/Core/src/IAllowAnonymous.cs renamed to src/Http/Http.Abstractions/src/IAllowAnonymous.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
namespace Microsoft.AspNetCore.Authorization
55
{
66
/// <summary>
7-
/// Marker interface to enable the <see cref="AllowAnonymousAttribute"/>.
7+
/// Marker interface to allow anonymous.
88
/// </summary>
99
public interface IAllowAnonymous
1010
{
File renamed without changes.
File renamed without changes.

src/Http/Routing/src/EndpointMiddleware.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
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;
5+
using System.Runtime.CompilerServices;
56
using System.Threading.Tasks;
7+
using Microsoft.AspNetCore.Authorization;
8+
using Microsoft.AspNetCore.Cors.Infrastructure;
69
using Microsoft.AspNetCore.Http;
710
using Microsoft.AspNetCore.Http.Features;
811
using Microsoft.Extensions.Logging;
@@ -11,6 +14,9 @@ namespace Microsoft.AspNetCore.Routing
1114
{
1215
internal sealed class EndpointMiddleware
1316
{
17+
internal const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareInvoked";
18+
internal const string CorsMiddlewareInvokedKey = "__CorsMiddlewareInvoked";
19+
1420
private readonly ILogger _logger;
1521
private readonly RequestDelegate _next;
1622

@@ -35,6 +41,8 @@ public async Task Invoke(HttpContext httpContext)
3541
var endpoint = httpContext.Features.Get<IEndpointFeature>()?.Endpoint;
3642
if (endpoint?.RequestDelegate != null)
3743
{
44+
EnsureRequisiteMiddlewares(httpContext, endpoint);
45+
3846
Log.ExecutingEndpoint(_logger, endpoint);
3947

4048
try
@@ -52,6 +60,34 @@ public async Task Invoke(HttpContext httpContext)
5260
await _next(httpContext);
5361
}
5462

63+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
64+
private static void EnsureRequisiteMiddlewares(HttpContext httpContext, Endpoint endpoint)
65+
{
66+
if (endpoint.Metadata.GetMetadata<IAuthorizeData>() != null &&
67+
!httpContext.Items.ContainsKey(AuthorizationMiddlewareInvokedKey))
68+
{
69+
ThrowMissingAuthMiddlewareException(endpoint);
70+
}
71+
72+
if (endpoint.Metadata.GetMetadata<ICorsMetadata>() != null &&
73+
!httpContext.Items.ContainsKey(CorsMiddlewareInvokedKey))
74+
{
75+
ThrowMissingCorsMiddlewareException(endpoint);
76+
}
77+
}
78+
79+
private static void ThrowMissingAuthMiddlewareException(Endpoint endpoint)
80+
{
81+
throw new InvalidOperationException($"Endpoint {endpoint.DisplayName} contains authorization metadata, " +
82+
"but a middleware was not found that supports authorization.");
83+
}
84+
85+
private static void ThrowMissingCorsMiddlewareException(Endpoint endpoint)
86+
{
87+
throw new InvalidOperationException($"Endpoint {endpoint.DisplayName} contains CORS metadata, " +
88+
"but a middleware was not found that supports CORS.");
89+
}
90+
5591
private static class Log
5692
{
5793
private static readonly Action<ILogger, string, Exception> _executingEndpoint = LoggerMessage.Define<string>(

src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
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;
55
using System.Threading.Tasks;
6+
using Microsoft.AspNetCore.Authorization;
7+
using Microsoft.AspNetCore.Cors.Infrastructure;
68
using Microsoft.AspNetCore.Http;
79
using Microsoft.AspNetCore.Http.Features;
810
using Microsoft.Extensions.Logging.Abstractions;
11+
using Moq;
912
using Xunit;
1013

1114
namespace Microsoft.AspNetCore.Routing
@@ -90,6 +93,100 @@ public async Task Invoke_WithEndpoint_InvokesDelegate()
9093
Assert.True(invoked);
9194
}
9295

96+
[Fact]
97+
public async Task Invoke_WithEndpoint_ThrowsIfAuthAttributesWereFound_ButAuthMiddlewareNotInvoked()
98+
{
99+
// Arrange
100+
var httpContext = new DefaultHttpContext
101+
{
102+
RequestServices = new ServiceProvider()
103+
};
104+
105+
httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
106+
{
107+
Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test"),
108+
});
109+
110+
var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, _ => Task.CompletedTask);
111+
112+
// Act & Assert
113+
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => middleware.Invoke(httpContext));
114+
115+
// Assert
116+
Assert.Equal("Endpoint Test contains authorization metadata, but a middleware was not found that supports authorization.", ex.Message);
117+
}
118+
119+
[Fact]
120+
public async Task Invoke_WithEndpoint_WorksIfAuthAttributesWereFound_AndAuthMiddlewareInvoked()
121+
{
122+
// Arrange
123+
var httpContext = new DefaultHttpContext
124+
{
125+
RequestServices = new ServiceProvider()
126+
};
127+
128+
httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
129+
{
130+
Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test"),
131+
});
132+
133+
httpContext.Items[EndpointMiddleware.AuthorizationMiddlewareInvokedKey] = true;
134+
135+
var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, _ => Task.CompletedTask);
136+
137+
// Act & Assert
138+
await middleware.Invoke(httpContext);
139+
140+
// If we got this far, we can sound the everything's OK alarm.
141+
}
142+
143+
[Fact]
144+
public async Task Invoke_WithEndpoint_ThrowsIfCorsMetadataWasFound_ButCorsMiddlewareNotInvoked()
145+
{
146+
// Arrange
147+
var httpContext = new DefaultHttpContext
148+
{
149+
RequestServices = new ServiceProvider()
150+
};
151+
152+
httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
153+
{
154+
Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<ICorsMetadata>()), "Test"),
155+
});
156+
157+
var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, _ => Task.CompletedTask);
158+
159+
// Act & Assert
160+
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => middleware.Invoke(httpContext));
161+
162+
// Assert
163+
Assert.Equal("Endpoint Test contains CORS metadata, but a middleware was not found that supports CORS.", ex.Message);
164+
}
165+
166+
[Fact]
167+
public async Task Invoke_WithEndpoint_WorksIfCorsMetadataWasFound_AndCorsMiddlewareInvoked()
168+
{
169+
// Arrange
170+
var httpContext = new DefaultHttpContext
171+
{
172+
RequestServices = new ServiceProvider()
173+
};
174+
175+
httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
176+
{
177+
Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<ICorsMetadata>()), "Test"),
178+
});
179+
180+
httpContext.Items[EndpointMiddleware.CorsMiddlewareInvokedKey] = true;
181+
182+
var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, _ => Task.CompletedTask);
183+
184+
// Act & Assert
185+
await middleware.Invoke(httpContext);
186+
187+
// If we got this far, we can sound the everything's OK alarm.
188+
}
189+
93190
private class ServiceProvider : IServiceProvider
94191
{
95192
public object GetService(Type serviceType)

src/Middleware/CORS/src/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
using System.Runtime.CompilerServices;
55

66
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Cors.Test,PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
7+

src/Security/Authorization/Core/src/Microsoft.AspNetCore.Authorization.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<Description>ASP.NET Core authorization classes.
@@ -14,6 +14,7 @@ Microsoft.AspNetCore.Authorization.AuthorizeAttribute</Description>
1414
</PropertyGroup>
1515

1616
<ItemGroup>
17+
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
1718
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
1819
<Reference Include="Microsoft.Extensions.Options" />
1920
</ItemGroup>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Runtime.CompilerServices;
5+
using Microsoft.AspNetCore.Authorization;
6+
7+
[assembly: TypeForwardedTo(typeof(IAuthorizeData))]
8+
[assembly: TypeForwardedTo(typeof(IAllowAnonymous))]

0 commit comments

Comments
 (0)