Throw when UseAuthorization is incorrectly configured
* Update AuthZ & Cors middlewares to only set endpoint routing metadata when executing in the context of endpoint routing * Add analyzers for incorrect UseAuth use Fixes https://github.com/aspnet/AspNetCore/issues/14049
This commit is contained in:
parent
51ae61baca
commit
29a1d50e02
|
|
@ -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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
|
@ -19,6 +19,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
{
|
{
|
||||||
// ASP
|
// ASP
|
||||||
BuildServiceProviderShouldNotCalledInConfigureServicesMethod,
|
BuildServiceProviderShouldNotCalledInConfigureServicesMethod,
|
||||||
|
IncorrectlyConfiguredAuthorizationMiddleware,
|
||||||
|
|
||||||
// MVC
|
// MVC
|
||||||
UnsupportedUseMvcWithEndpointRouting,
|
UnsupportedUseMvcWithEndpointRouting,
|
||||||
|
|
@ -42,6 +43,15 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
DiagnosticSeverity.Warning,
|
DiagnosticSeverity.Warning,
|
||||||
isEnabledByDefault: true,
|
isEnabledByDefault: true,
|
||||||
helpLinkUri: "https://aka.ms/YJggeFn");
|
helpLinkUri: "https://aka.ms/YJggeFn");
|
||||||
|
|
||||||
|
internal readonly static DiagnosticDescriptor IncorrectlyConfiguredAuthorizationMiddleware = new DiagnosticDescriptor(
|
||||||
|
"ASP0001",
|
||||||
|
"Authorization middleware is incorrectly configured.",
|
||||||
|
"The call to UseAuthorization should appear between app.UseRouting() and app.UseEndpoints(..) for authorization to be correctly evaluated.",
|
||||||
|
"Usage",
|
||||||
|
DiagnosticSeverity.Warning,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
helpLinkUri: "https://aka.ms/AA64fv1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -82,6 +82,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
var analysis = builder.Build();
|
var analysis = builder.Build();
|
||||||
new UseMvcAnalyzer(analysis).AnalyzeSymbol(context);
|
new UseMvcAnalyzer(analysis).AnalyzeSymbol(context);
|
||||||
new BuildServiceProviderValidator(analysis).AnalyzeSymbol(context);
|
new BuildServiceProviderValidator(analysis).AnalyzeSymbol(context);
|
||||||
|
new UseAuthorizationAnalyzer(analysis).AnalyzeSymbol(context);
|
||||||
});
|
});
|
||||||
|
|
||||||
}, SymbolKind.NamedType);
|
}, SymbolKind.NamedType);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
{
|
||||||
|
internal class UseAuthorizationAnalyzer
|
||||||
|
{
|
||||||
|
private readonly StartupAnalysis _context;
|
||||||
|
|
||||||
|
public UseAuthorizationAnalyzer(StartupAnalysis context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AnalyzeSymbol(SymbolAnalysisContext context)
|
||||||
|
{
|
||||||
|
Debug.Assert(context.Symbol.Kind == SymbolKind.NamedType);
|
||||||
|
Debug.Assert(StartupFacts.IsStartupClass(_context.StartupSymbols, (INamedTypeSymbol)context.Symbol));
|
||||||
|
|
||||||
|
var type = (INamedTypeSymbol)context.Symbol;
|
||||||
|
|
||||||
|
foreach (var middlewareAnalysis in _context.GetRelatedAnalyses<MiddlewareAnalysis>(type))
|
||||||
|
{
|
||||||
|
MiddlewareItem? useAuthorizationItem = default;
|
||||||
|
MiddlewareItem? useRoutingItem = default;
|
||||||
|
|
||||||
|
var length = middlewareAnalysis.Middleware.Length;
|
||||||
|
for (var i = length - 1; i >= 0; i-- )
|
||||||
|
{
|
||||||
|
var middlewareItem = middlewareAnalysis.Middleware[i];
|
||||||
|
var middleware = middlewareItem.UseMethod.Name;
|
||||||
|
|
||||||
|
if (middleware == "UseAuthorization")
|
||||||
|
{
|
||||||
|
if (useRoutingItem != null && useAuthorizationItem == null)
|
||||||
|
{
|
||||||
|
// This looks like
|
||||||
|
//
|
||||||
|
// app.UseAuthorization();
|
||||||
|
// ...
|
||||||
|
// app.UseRouting();
|
||||||
|
// app.UseEndpoints(...);
|
||||||
|
|
||||||
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
|
StartupAnalyzer.Diagnostics.IncorrectlyConfiguredAuthorizationMiddleware,
|
||||||
|
middlewareItem.Operation.Syntax.GetLocation(),
|
||||||
|
middlewareItem.UseMethod.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
useAuthorizationItem = middlewareItem;
|
||||||
|
}
|
||||||
|
else if (middleware == "UseEndpoints")
|
||||||
|
{
|
||||||
|
if (useAuthorizationItem != null)
|
||||||
|
{
|
||||||
|
// This configuration looks like
|
||||||
|
//
|
||||||
|
// app.UseRouting();
|
||||||
|
// app.UseEndpoints(...);
|
||||||
|
// ...
|
||||||
|
// app.UseAuthorization();
|
||||||
|
//
|
||||||
|
|
||||||
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
|
StartupAnalyzer.Diagnostics.IncorrectlyConfiguredAuthorizationMiddleware,
|
||||||
|
useAuthorizationItem.Operation.Syntax.GetLocation(),
|
||||||
|
middlewareItem.UseMethod.Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (middleware == "UseRouting")
|
||||||
|
{
|
||||||
|
useRoutingItem = middlewareItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -162,7 +162,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
middlewareAnalysis.Middleware,
|
middlewareAnalysis.Middleware,
|
||||||
item => Assert.Equal("UseAuthorization", item.UseMethod.Name),
|
item => Assert.Equal("UseStaticFiles", item.UseMethod.Name),
|
||||||
item => Assert.Equal("UseMiddleware", item.UseMethod.Name),
|
item => Assert.Equal("UseMiddleware", item.UseMethod.Name),
|
||||||
item => Assert.Equal("UseMvc", item.UseMethod.Name),
|
item => Assert.Equal("UseMvc", item.UseMethod.Name),
|
||||||
item => Assert.Equal("UseRouting", item.UseMethod.Name),
|
item => Assert.Equal("UseRouting", item.UseMethod.Name),
|
||||||
|
|
@ -228,5 +228,90 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic.Location);
|
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic.Location);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StartupAnalyzer_UseAuthorizationConfiguredCorrectly_ReportsNoDiagnostics()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = Read(nameof(TestFiles.StartupAnalyzerTest.UseAuthConfiguredCorrectly));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
||||||
|
Assert.NotEmpty(middlewareAnalysis.Middleware);
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StartupAnalyzer_UseAuthorizationInvokedMultipleTimesInEndpointRoutingBlock_ReportsNoDiagnostics()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = Read(nameof(TestFiles.StartupAnalyzerTest.UseAuthMultipleTimes));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
||||||
|
Assert.NotEmpty(middlewareAnalysis.Middleware);
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StartupAnalyzer_UseAuthorizationConfiguredBeforeUseRouting_ReportsDiagnostics()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = Read(nameof(TestFiles.StartupAnalyzerTest.UseAuthBeforeUseRouting));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
||||||
|
Assert.NotEmpty(middlewareAnalysis.Middleware);
|
||||||
|
Assert.Collection(diagnostics,
|
||||||
|
diagnostic =>
|
||||||
|
{
|
||||||
|
Assert.Same(StartupAnalyzer.Diagnostics.IncorrectlyConfiguredAuthorizationMiddleware, diagnostic.Descriptor);
|
||||||
|
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StartupAnalyzer_UseAuthorizationConfiguredAfterUseEndpoints_ReportsDiagnostics()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = Read(nameof(TestFiles.StartupAnalyzerTest.UseAuthAfterUseEndpoints));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
||||||
|
Assert.NotEmpty(middlewareAnalysis.Middleware);
|
||||||
|
Assert.Collection(diagnostics,
|
||||||
|
diagnostic =>
|
||||||
|
{
|
||||||
|
Assert.Same(StartupAnalyzer.Diagnostics.IncorrectlyConfiguredAuthorizationMiddleware, diagnostic.Descriptor);
|
||||||
|
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StartupAnalyzer_MultipleUseAuthorization_ReportsNoDiagnostics()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = Read(nameof(TestFiles.StartupAnalyzerTest.UseAuthFallbackPolicy));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
||||||
|
Assert.NotEmpty(middlewareAnalysis.Middleware);
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Analyzers.TestFiles.StartupAnalyzerTest
|
||||||
{
|
{
|
||||||
/*MM1*/app.UseMvcWithDefaultRoute();
|
/*MM1*/app.UseMvcWithDefaultRoute();
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseStaticFiles();
|
||||||
app.UseMiddleware<AuthorizationMiddleware>();
|
app.UseMiddleware<AuthorizationMiddleware>();
|
||||||
|
|
||||||
/*MM2*/app.UseMvc();
|
/*MM2*/app.UseMvc();
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Analyzers.TestFiles.StartupAnalyzerTest
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
app.UseAuthorization();
|
app.UseStaticFiles();
|
||||||
app.UseMiddleware<AuthorizationMiddleware>();
|
app.UseMiddleware<AuthorizationMiddleware>();
|
||||||
|
|
||||||
/*MM*/app.UseMvc();
|
/*MM*/app.UseMvc();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers.TestFiles.StartupAnalyzerTest
|
||||||
|
{
|
||||||
|
public class UseAuthAfterUseEndpoints
|
||||||
|
{
|
||||||
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
app.UseRouting();
|
||||||
|
app.UseEndpoints(r => { });
|
||||||
|
/*MM*/app.UseAuthorization();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers.TestFiles.StartupAnalyzerTest
|
||||||
|
{
|
||||||
|
public class UseAuthBeforeUseRouting
|
||||||
|
{
|
||||||
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
app.UseFileServer();
|
||||||
|
/*MM*/app.UseAuthorization();
|
||||||
|
app.UseRouting();
|
||||||
|
app.UseEndpoints(r => { });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers.TestFiles.StartupAnalyzerTest
|
||||||
|
{
|
||||||
|
public class UseAuthConfiguredCorrectly
|
||||||
|
{
|
||||||
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
app.UseRouting();
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.UseEndpoints(r => { });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers.TestFiles.StartupAnalyzerTest
|
||||||
|
{
|
||||||
|
public class UseAuthFallbackPolicy
|
||||||
|
{
|
||||||
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
// This sort of setup would be useful if the user wants to use Auth for non-endpoint content to be handled using the Fallback policy, while
|
||||||
|
// using the second instance for regular endpoint routing based auth. We do not want to produce a warning in this case.
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.UseStaticFiles();
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.UseEndpoints(r => { });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers.TestFiles.StartupAnalyzerTest
|
||||||
|
{
|
||||||
|
public class UseAuthMultipleTimes
|
||||||
|
{
|
||||||
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
app.UseRouting();
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.UseEndpoints(r => { });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,16 @@ namespace TestServer
|
||||||
services.AddMvc();
|
services.AddMvc();
|
||||||
services.AddCors(options =>
|
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());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,18 +42,6 @@ namespace TestServer
|
||||||
app.UseDeveloperExceptionPage();
|
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();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Mount the server-side Blazor app on /subdir
|
// Mount the server-side Blazor app on /subdir
|
||||||
app.Map("/subdir", app =>
|
app.Map("/subdir", app =>
|
||||||
{
|
{
|
||||||
|
|
@ -52,12 +49,14 @@ namespace TestServer
|
||||||
app.UseClientSideBlazorFiles<BasicTestApp.Startup>();
|
app.UseClientSideBlazorFiles<BasicTestApp.Startup>();
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseCors();
|
||||||
|
|
||||||
app.UseEndpoints(endpoints =>
|
app.UseEndpoints(endpoints =>
|
||||||
{
|
{
|
||||||
endpoints.MapControllers();
|
endpoints.MapControllers();
|
||||||
endpoints.MapFallbackToClientSideBlazor<BasicTestApp.Startup>("index.html");
|
endpoints.MapFallbackToClientSideBlazor<BasicTestApp.Startup>("index.html");
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
@ -68,9 +69,7 @@ namespace TestServer
|
||||||
</html>");
|
</html>");
|
||||||
var content = writer.ToString();
|
var content = writer.ToString();
|
||||||
ctx.Response.ContentLength = content.Length;
|
ctx.Response.ContentLength = content.Length;
|
||||||
using var responseWriter = new StreamWriter(ctx.Response.Body);
|
await ctx.Response.WriteAsync(content);
|
||||||
await responseWriter.WriteAsync(content);
|
|
||||||
await responseWriter.FlushAsync();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server
|
||||||
EndProject
|
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}"
|
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
|
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
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -603,6 +607,30 @@ Global
|
||||||
{21AC56E7-4E77-4B0E-B63E-C8E836E4D14E}.Release|x64.Build.0 = Release|Any CPU
|
{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.ActiveCfg = Release|Any CPU
|
||||||
{21AC56E7-4E77-4B0E-B63E-C8E836E4D14E}.Release|x86.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -651,6 +679,8 @@ Global
|
||||||
{611794D2-EF3A-422A-A077-23E61C7ADE49} = {793FFE24-138A-4C3D-81AB-18D625E36230}
|
{611794D2-EF3A-422A-A077-23E61C7ADE49} = {793FFE24-138A-4C3D-81AB-18D625E36230}
|
||||||
{1062FCDE-E145-40EC-B175-FDBCAA0C59A0} = {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}
|
{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
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {85B5E151-2E9D-419C-83DD-0DDCF446C83A}
|
SolutionGuid = {85B5E151-2E9D-419C-83DD-0DDCF446C83A}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
|
@ -14,8 +13,8 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
{
|
{
|
||||||
internal sealed class EndpointMiddleware
|
internal sealed class EndpointMiddleware
|
||||||
{
|
{
|
||||||
internal const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareInvoked";
|
internal const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareWithEndpointInvoked";
|
||||||
internal const string CorsMiddlewareInvokedKey = "__CorsMiddlewareInvoked";
|
internal const string CorsMiddlewareInvokedKey = "__CorsMiddlewareWithEndpointInvoked";
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly RequestDelegate _next;
|
private readonly RequestDelegate _next;
|
||||||
|
|
@ -91,7 +90,7 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
throw new InvalidOperationException($"Endpoint {endpoint.DisplayName} contains authorization metadata, " +
|
throw new InvalidOperationException($"Endpoint {endpoint.DisplayName} contains authorization metadata, " +
|
||||||
"but a middleware was not found that supports authorization." +
|
"but a middleware was not found that supports authorization." +
|
||||||
Environment.NewLine +
|
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)
|
private static void ThrowMissingCorsMiddlewareException(Endpoint endpoint)
|
||||||
|
|
@ -99,7 +98,7 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
throw new InvalidOperationException($"Endpoint {endpoint.DisplayName} contains CORS metadata, " +
|
throw new InvalidOperationException($"Endpoint {endpoint.DisplayName} contains CORS metadata, " +
|
||||||
"but a middleware was not found that supports CORS." +
|
"but a middleware was not found that supports CORS." +
|
||||||
Environment.NewLine +
|
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
|
private static class Log
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.AspNetCore.Authorization.Policy" />
|
||||||
|
<Reference Include="Microsoft.AspNetCore.Cors" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Routing" />
|
<Reference Include="Microsoft.AspNetCore.Routing" />
|
||||||
<Reference Include="Microsoft.AspNetCore.TestHost" />
|
<Reference Include="Microsoft.AspNetCore.TestHost" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,8 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
// Arrange
|
// Arrange
|
||||||
var expected = "Endpoint Test contains authorization metadata, but a middleware was not found that supports authorization." +
|
var expected = "Endpoint Test contains authorization metadata, but a middleware was not found that supports authorization." +
|
||||||
Environment.NewLine +
|
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(...).";
|
||||||
var httpContext = new DefaultHttpContext
|
var httpContext = new DefaultHttpContext
|
||||||
{
|
{
|
||||||
RequestServices = new ServiceProvider()
|
RequestServices = new ServiceProvider()
|
||||||
|
|
@ -197,7 +198,8 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
// Arrange
|
// Arrange
|
||||||
var expected = "Endpoint Test contains CORS metadata, but a middleware was not found that supports CORS." +
|
var expected = "Endpoint Test contains CORS metadata, but a middleware was not found that supports CORS." +
|
||||||
Environment.NewLine +
|
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(...).";
|
||||||
var httpContext = new DefaultHttpContext
|
var httpContext = new DefaultHttpContext
|
||||||
{
|
{
|
||||||
RequestServices = new ServiceProvider()
|
RequestServices = new ServiceProvider()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"solution": {
|
||||||
|
"path": "D:\\work\\aspnetcore\\src\\Middleware\\Middleware.sln",
|
||||||
|
"projects": [
|
||||||
|
"CORS\\src\\Microsoft.AspNetCore.Cors.csproj",
|
||||||
|
"CORS\\test\\UnitTests\\Microsoft.AspNetCore.Cors.Test.csproj",
|
||||||
|
"CORS\\test\\testassets\\CorsMiddlewareWebSite\\CorsMiddlewareWebSite.csproj"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,8 +14,8 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
public class CorsMiddleware
|
public class CorsMiddleware
|
||||||
{
|
{
|
||||||
// Property key is used by other systems, e.g. MVC, 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 const string CorsMiddlewareWithEndpointInvokedKey = "__CorsMiddlewareWithEndpointInvoked";
|
||||||
private static readonly object CorsMiddlewareInvokedValue = new object();
|
private static readonly object CorsMiddlewareWithEndpointInvokedValue = new object();
|
||||||
|
|
||||||
private readonly Func<object, Task> OnResponseStartingDelegate = OnResponseStarting;
|
private readonly Func<object, Task> OnResponseStartingDelegate = OnResponseStarting;
|
||||||
private readonly RequestDelegate _next;
|
private readonly RequestDelegate _next;
|
||||||
|
|
@ -116,14 +116,6 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task Invoke(HttpContext context, ICorsPolicyProvider corsPolicyProvider)
|
public Task Invoke(HttpContext context, ICorsPolicyProvider corsPolicyProvider)
|
||||||
{
|
{
|
||||||
// Flag to indicate to other systems, that CORS middleware was run for this request
|
|
||||||
context.Items[CorsMiddlewareInvokedKey] = CorsMiddlewareInvokedValue;
|
|
||||||
|
|
||||||
if (!context.Request.Headers.ContainsKey(CorsConstants.Origin))
|
|
||||||
{
|
|
||||||
return _next(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
// CORS policy resolution rules:
|
// CORS policy resolution rules:
|
||||||
//
|
//
|
||||||
// 1. If there is an endpoint with IDisableCorsAttribute then CORS is not run
|
// 1. If there is an endpoint with IDisableCorsAttribute then CORS is not run
|
||||||
|
|
@ -131,9 +123,20 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
// there is an endpoint with IEnableCorsAttribute that has a policy name then
|
// there is an endpoint with IEnableCorsAttribute that has a policy name then
|
||||||
// fetch policy by name, prioritizing it above policy on middleware
|
// fetch policy by name, prioritizing it above policy on middleware
|
||||||
// 3. If there is no policy on middleware then use name on middleware
|
// 3. If there is no policy on middleware then use name on middleware
|
||||||
|
|
||||||
var endpoint = context.GetEndpoint();
|
var endpoint = context.GetEndpoint();
|
||||||
|
|
||||||
|
if (endpoint != null)
|
||||||
|
{
|
||||||
|
// EndpointRoutingMiddleware uses this flag to check if the CORS middleware processed CORS metadata on the endpoint.
|
||||||
|
// The CORS middleware can only make this claim if it observes an actual endpoint.
|
||||||
|
context.Items[CorsMiddlewareWithEndpointInvokedKey] = CorsMiddlewareWithEndpointInvokedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context.Request.Headers.ContainsKey(CorsConstants.Origin))
|
||||||
|
{
|
||||||
|
return _next(context);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the most significant CORS metadata for the endpoint
|
// Get the most significant CORS metadata for the endpoint
|
||||||
// For backwards compatibility reasons this is then downcast to Enable/Disable metadata
|
// For backwards compatibility reasons this is then downcast to Enable/Disable metadata
|
||||||
var corsMetadata = endpoint?.Metadata.GetMetadata<ICorsMetadata>();
|
var corsMetadata = endpoint?.Metadata.GetMetadata<ICorsMetadata>();
|
||||||
|
|
|
||||||
|
|
@ -918,7 +918,7 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
await middleware.Invoke(httpContext, mockProvider);
|
await middleware.Invoke(httpContext, mockProvider);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Contains(httpContext.Items, item => string.Equals(item.Key as string, "__CorsMiddlewareInvoked"));
|
Assert.Contains(httpContext.Items, item => string.Equals(item.Key as string, "__CorsMiddlewareWithEndpointInvoked"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -936,12 +936,37 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
"DefaultPolicyName");
|
"DefaultPolicyName");
|
||||||
|
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
|
httpContext.SetEndpoint(new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(new EnableCorsAttribute("MetadataPolicyName"), new DisableCorsAttribute()), "Test endpoint"));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await middleware.Invoke(httpContext, mockProvider);
|
await middleware.Invoke(httpContext, mockProvider);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Contains(httpContext.Items, item => string.Equals(item.Key as string, "__CorsMiddlewareInvoked"));
|
Assert.Contains(httpContext.Items, item => string.Equals(item.Key as string, "__CorsMiddlewareWithEndpointInvoked"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Invoke_WithoutEndpoint_InvokeFlagSet()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var corsService = Mock.Of<ICorsService>();
|
||||||
|
var mockProvider = Mock.Of<ICorsPolicyProvider>();
|
||||||
|
var loggerFactory = NullLoggerFactory.Instance;
|
||||||
|
|
||||||
|
var middleware = new CorsMiddleware(
|
||||||
|
Mock.Of<RequestDelegate>(),
|
||||||
|
corsService,
|
||||||
|
loggerFactory,
|
||||||
|
"DefaultPolicyName");
|
||||||
|
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
httpContext.Request.Headers.Add(CorsConstants.Origin, new[] { "http://example.com" });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await middleware.Invoke(httpContext, mockProvider);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.DoesNotContain(httpContext.Items, item => string.Equals(item.Key as string, "__CorsMiddlewareWithEndpointInvoked"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,9 @@ namespace Microsoft.AspNetCore.Authorization
|
||||||
{
|
{
|
||||||
public class AuthorizationMiddleware
|
public class AuthorizationMiddleware
|
||||||
{
|
{
|
||||||
// Property key is used by other systems, e.g. MVC, to check if authorization middleware has run
|
// Property key is used by Endpoint routing to determine if Authorization has run
|
||||||
private const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareInvoked";
|
private const string AuthorizationMiddlewareInvokedWithEndpointKey = "__AuthorizationMiddlewareWithEndpointInvoked";
|
||||||
private static readonly object AuthorizationMiddlewareInvokedValue = new object();
|
private static readonly object AuthorizationMiddlewareWithEndpointInvokedValue = new object();
|
||||||
|
|
||||||
private readonly RequestDelegate _next;
|
private readonly RequestDelegate _next;
|
||||||
private readonly IAuthorizationPolicyProvider _policyProvider;
|
private readonly IAuthorizationPolicyProvider _policyProvider;
|
||||||
|
|
@ -35,8 +35,12 @@ namespace Microsoft.AspNetCore.Authorization
|
||||||
|
|
||||||
var endpoint = context.GetEndpoint();
|
var endpoint = context.GetEndpoint();
|
||||||
|
|
||||||
// Flag to indicate to other systems, e.g. MVC, that authorization middleware was run for this request
|
if (endpoint != null)
|
||||||
context.Items[AuthorizationMiddlewareInvokedKey] = AuthorizationMiddlewareInvokedValue;
|
{
|
||||||
|
// EndpointRoutingMiddleware uses this flag to check if the Authorization middleware processed auth metadata on the endpoint.
|
||||||
|
// The Authorization middleware can only make this claim if it observes an actual endpoint.
|
||||||
|
context.Items[AuthorizationMiddlewareInvokedWithEndpointKey] = AuthorizationMiddlewareWithEndpointInvokedValue;
|
||||||
|
}
|
||||||
|
|
||||||
// IMPORTANT: Changes to authorization logic should be mirrored in MVC's AuthorizeFilter
|
// IMPORTANT: Changes to authorization logic should be mirrored in MVC's AuthorizeFilter
|
||||||
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
|
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue