Skip to content

Port Throw when UseAuthorization is incorrectly configured #14893

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions src/Components/test/testassets/TestServer/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ public void ConfigureServices(IServiceCollection services)
services.AddMvc().AddNewtonsoftJson();
services.AddCors(options =>
{
options.AddPolicy("AllowAll", _ => { /* Controlled below */ });
// It's not enough just to return "Access-Control-Allow-Origin: *", because
// browsers don't allow wildcards in conjunction with credentials. So we must
// specify explicitly which origin we want to allow.
options.AddPolicy("AllowAll", policy =>
policy.SetIsOriginAllowed(host => host.StartsWith("http://localhost:") || host.StartsWith("http://127.0.0.1:"))
.AllowAnyHeader()
.WithExposedHeaders("MyCustomHeader")
.AllowAnyMethod()
.AllowCredentials());
});
services.AddServerSideBlazor()
.AddCircuitOptions(o =>
Expand All @@ -49,18 +57,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseDeveloperExceptionPage();
}

// It's not enough just to return "Access-Control-Allow-Origin: *", because
// browsers don't allow wildcards in conjunction with credentials. So we must
// specify explicitly which origin we want to allow.
app.UseCors(policy =>
{
policy.SetIsOriginAllowed(host => host.StartsWith("http://localhost:") || host.StartsWith("http://127.0.0.1:"))
.AllowAnyHeader()
.WithExposedHeaders("MyCustomHeader")
.AllowAnyMethod()
.AllowCredentials();
});

app.UseAuthentication();

// Mount the server-side Blazor app on /subdir
Expand Down Expand Up @@ -119,6 +115,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
});

app.UseRouting();

app.UseCors();

app.UseEndpoints(endpoints =>
{
Expand Down
30 changes: 30 additions & 0 deletions src/Http/HttpAbstractions.sln
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebUtilities.Performance", "WebUtilities\perf\Microsoft.AspNetCore.WebUtilities.Performance\Microsoft.AspNetCore.WebUtilities.Performance.csproj", "{21AC56E7-4E77-4B0E-B63E-C8E836E4D14E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authorization.Policy", "..\Security\Authorization\Policy\src\Microsoft.AspNetCore.Authorization.Policy.csproj", "{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cors", "..\Middleware\CORS\src\Microsoft.AspNetCore.Cors.csproj", "{09168958-FD5B-4D25-8FBF-75E2C80D903B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -603,6 +607,30 @@ Global
{21AC56E7-4E77-4B0E-B63E-C8E836E4D14E}.Release|x64.Build.0 = Release|Any CPU
{21AC56E7-4E77-4B0E-B63E-C8E836E4D14E}.Release|x86.ActiveCfg = Release|Any CPU
{21AC56E7-4E77-4B0E-B63E-C8E836E4D14E}.Release|x86.Build.0 = Release|Any CPU
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Debug|x64.ActiveCfg = Debug|Any CPU
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Debug|x64.Build.0 = Debug|Any CPU
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Debug|x86.ActiveCfg = Debug|Any CPU
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Debug|x86.Build.0 = Debug|Any CPU
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Release|Any CPU.Build.0 = Release|Any CPU
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Release|x64.ActiveCfg = Release|Any CPU
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Release|x64.Build.0 = Release|Any CPU
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Release|x86.ActiveCfg = Release|Any CPU
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Release|x86.Build.0 = Release|Any CPU
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Debug|x64.ActiveCfg = Debug|Any CPU
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Debug|x64.Build.0 = Debug|Any CPU
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Debug|x86.ActiveCfg = Debug|Any CPU
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Debug|x86.Build.0 = Debug|Any CPU
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Release|Any CPU.Build.0 = Release|Any CPU
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Release|x64.ActiveCfg = Release|Any CPU
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Release|x64.Build.0 = Release|Any CPU
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Release|x86.ActiveCfg = Release|Any CPU
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -651,6 +679,8 @@ Global
{611794D2-EF3A-422A-A077-23E61C7ADE49} = {793FFE24-138A-4C3D-81AB-18D625E36230}
{1062FCDE-E145-40EC-B175-FDBCAA0C59A0} = {793FFE24-138A-4C3D-81AB-18D625E36230}
{21AC56E7-4E77-4B0E-B63E-C8E836E4D14E} = {80A090C8-ED02-4DE3-875A-30DCCDBD84BA}
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B} = {793FFE24-138A-4C3D-81AB-18D625E36230}
{09168958-FD5B-4D25-8FBF-75E2C80D903B} = {793FFE24-138A-4C3D-81AB-18D625E36230}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {85B5E151-2E9D-419C-83DD-0DDCF446C83A}
Expand Down
9 changes: 4 additions & 5 deletions src/Http/Routing/src/EndpointMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.Routing
{
internal sealed class EndpointMiddleware
{
internal const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareInvoked";
internal const string CorsMiddlewareInvokedKey = "__CorsMiddlewareInvoked";
internal const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareWithEndpointInvoked";
internal const string CorsMiddlewareInvokedKey = "__CorsMiddlewareWithEndpointInvoked";

private readonly ILogger _logger;
private readonly RequestDelegate _next;
Expand Down Expand Up @@ -91,15 +90,15 @@ private static void ThrowMissingAuthMiddlewareException(Endpoint endpoint)
throw new InvalidOperationException($"Endpoint {endpoint.DisplayName} contains authorization metadata, " +
"but a middleware was not found that supports authorization." +
Environment.NewLine +
"Configure your application startup by adding app.UseAuthorization() inside the call to Configure(..) in the application startup code.");
"Configure your application startup by adding app.UseAuthorization() inside the call to Configure(..) in the application startup code. The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).");
}

private static void ThrowMissingCorsMiddlewareException(Endpoint endpoint)
{
throw new InvalidOperationException($"Endpoint {endpoint.DisplayName} contains CORS metadata, " +
"but a middleware was not found that supports CORS." +
Environment.NewLine +
"Configure your application startup by adding app.UseCors() inside the call to Configure(..) in the application startup code.");
"Configure your application startup by adding app.UseCors() inside the call to Configure(..) in the application startup code. The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).");
}

private static class Log
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
// 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.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace Microsoft.AspNetCore.Routing.FunctionalTests
{
public class EndpointRoutingIntegrationTest
{
private static readonly RequestDelegate TestDelegate = async context => await Task.Yield();
private static readonly string AuthErrorMessage = "Endpoint / contains authorization metadata, but a middleware was not found that supports authorization." +
Environment.NewLine +
"Configure your application startup by adding app.UseAuthorization() inside the call to Configure(..) in the application startup code. " +
"The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).";

private static readonly string CORSErrorMessage = "Endpoint / contains CORS metadata, but a middleware was not found that supports CORS." +
Environment.NewLine +
"Configure your application startup by adding app.UseCors() inside the call to Configure(..) in the application startup code. " +
"The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).";

[Fact]
public async Task AuthorizationMiddleware_WhenNoAuthMetadataIsConfigured()
{
// Arrange
var builder = new WebHostBuilder();
builder.Configure(app =>
{
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(b => b.Map("/", TestDelegate));
})
.ConfigureServices(services =>
{
services.AddAuthorization();
services.AddRouting();
});

using var server = new TestServer(builder);

var response = await server.CreateRequest("/").SendAsync("GET");

response.EnsureSuccessStatusCode();
}

[Fact]
public async Task AuthorizationMiddleware_WhenEndpointIsNotFound()
{
// Arrange
var builder = new WebHostBuilder();
builder.Configure(app =>
{
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(b => b.Map("/", TestDelegate));
})
.ConfigureServices(services =>
{
services.AddAuthorization();
services.AddRouting();
});

using var server = new TestServer(builder);

var response = await server.CreateRequest("/not-found").SendAsync("GET");

Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}

[Fact]
public async Task AuthorizationMiddleware_WithAuthorizedEndpoint()
{
// Arrange
var builder = new WebHostBuilder();
builder.Configure(app =>
{
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireAuthorization());
})
.ConfigureServices(services =>
{
services.AddAuthorization(options => options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build());
services.AddRouting();
});

using var server = new TestServer(builder);

var response = await server.CreateRequest("/").SendAsync("GET");

response.EnsureSuccessStatusCode();
}

[Fact]
public async Task AuthorizationMiddleware_NotConfigured_Throws()
{
// Arrange
var builder = new WebHostBuilder();
builder.Configure(app =>
{
app.UseRouting();
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireAuthorization());

})
.ConfigureServices(services =>
{
services.AddAuthorization(options => options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build());
services.AddRouting();
});

using var server = new TestServer(builder);

var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => server.CreateRequest("/").SendAsync("GET"));
Assert.Equal(AuthErrorMessage, ex.Message);
}

[Fact]
public async Task AuthorizationMiddleware_NotConfigured_WhenEndpointIsNotFound()
{
// Arrange
var builder = new WebHostBuilder();
builder.Configure(app =>
{
app.UseRouting();
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireAuthorization());
})
.ConfigureServices(services =>
{
services.AddRouting();
});

using var server = new TestServer(builder);

var response = await server.CreateRequest("/not-found").SendAsync("GET");

Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}

[Fact]
public async Task AuthorizationMiddleware_ConfiguredBeforeRouting_Throws()
{
// Arrange
var builder = new WebHostBuilder();
builder.Configure(app =>
{
app.UseAuthorization();
app.UseRouting();
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireAuthorization());
})
.ConfigureServices(services =>
{
services.AddAuthorization(options => options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build());
services.AddRouting();
});

using var server = new TestServer(builder);

var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => server.CreateRequest("/").SendAsync("GET"));
Assert.Equal(AuthErrorMessage, ex.Message);
}

[Fact]
public async Task AuthorizationMiddleware_ConfiguredAfterRouting_Throws()
{
// Arrange
var builder = new WebHostBuilder();
builder.Configure(app =>
{
app.UseRouting();
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireAuthorization());
app.UseAuthorization();
})
.ConfigureServices(services =>
{
services.AddAuthorization(options => options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build());
services.AddRouting();
});

using var server = new TestServer(builder);

var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => server.CreateRequest("/").SendAsync("GET"));
Assert.Equal(AuthErrorMessage, ex.Message);
}

[Fact]
public async Task CorsMiddleware_WithCorsEndpoint()
{
// Arrange
var builder = new WebHostBuilder();
builder.Configure(app =>
{
app.UseRouting();
app.UseCors();
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireCors(policy => policy.AllowAnyOrigin()));
})
.ConfigureServices(services =>
{
services.AddCors();
services.AddRouting();
});

using var server = new TestServer(builder);

var response = await server.CreateRequest("/").SendAsync("PUT");

response.EnsureSuccessStatusCode();
}

[Fact]
public async Task CorsMiddleware_ConfiguredBeforeRouting_Throws()
{
// Arrange
var builder = new WebHostBuilder();
builder.Configure(app =>
{
app.UseCors();
app.UseRouting();
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireCors(policy => policy.AllowAnyOrigin()));
})
.ConfigureServices(services =>
{
services.AddCors();
services.AddRouting();
});

using var server = new TestServer(builder);

var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => server.CreateRequest("/").SendAsync("GET"));
Assert.Equal(CORSErrorMessage, ex.Message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
</ItemGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Authorization.Policy" />
<Reference Include="Microsoft.AspNetCore.Cors" />
<Reference Include="Microsoft.AspNetCore.Routing" />
<Reference Include="Microsoft.AspNetCore.TestHost" />
</ItemGroup>
Expand Down
Loading