No-op Authorization middleware for Razor Pages

Workaround for https://github.com/aspnet/AspNetCore/issues/7011
This commit is contained in:
Pranav K 2019-01-25 10:34:24 -08:00 committed by Artak
parent 2b7c26a123
commit f2ee3201ae
8 changed files with 129 additions and 4 deletions

View File

@ -4,6 +4,7 @@
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{
// This is used to store the uncombined parts of the final page route
// Note: This type name is referenced by name in AuthorizationMiddleware, do not change this without addressing https://github.com/aspnet/AspNetCore/issues/7011
internal class PageRouteMetadata
{
public PageRouteMetadata(string pageRoute, string routeTemplate)

View File

@ -1190,11 +1190,11 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore.InjectedPa
public async Task AuthFiltersAppliedToPageModel_AreExecuted()
{
// Act
var response = await Client.GetAsync("/ModelWithAuthFilter");
var response = await Client.GetAsync("/Pages/ModelWithAuthFilter");
// Assert
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal("/Login?ReturnUrl=%2FModelWithAuthFilter", response.Headers.Location.PathAndQuery);
Assert.Equal("/Login?ReturnUrl=%2FPages%2FModelWithAuthFilter", response.Headers.Location.PathAndQuery);
}
[Fact]

View File

@ -0,0 +1,66 @@
// 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.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class RazorPagesWithEndpointRoutingTest : IClassFixture<MvcTestFixture<RazorPagesWebSite.StartupWithEndpointRouting>>
{
public RazorPagesWithEndpointRoutingTest(MvcTestFixture<RazorPagesWebSite.StartupWithEndpointRouting> fixture)
{
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }
[Fact]
public async Task Authorize_AppliedUsingConvention_Works()
{
// Act
var response = await Client.GetAsync("/Conventions/AuthFolder");
// Assert
await response.AssertStatusCodeAsync(HttpStatusCode.Redirect);
Assert.Equal("/Login?ReturnUrl=%2FConventions%2FAuthFolder", response.Headers.Location.PathAndQuery);
}
[Fact]
public async Task Authorize_AppliedUsingConvention_CanByOverridenByAllowAnonymousAppliedToModel()
{
// Act
var response = await Client.GetAsync("/Conventions/AuthFolder/AnonymousViaModel");
// Assert
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello from Anonymous", content.Trim());
}
[Fact]
public async Task Authorize_AppliedUsingAttributeOnModel_Works()
{
// Act
var response = await Client.GetAsync("/ModelWithAuthFilter");
// Assert
await response.AssertStatusCodeAsync(HttpStatusCode.Redirect);
Assert.Equal("/Login?ReturnUrl=%2FModelWithAuthFilter", response.Headers.Location.PathAndQuery);
}
[Fact]
public async Task Authorize_WithEndpointRouting_WorksForControllers()
{
// Act
var response = await Client.GetAsync("/AuthorizedAction/Index");
// Assert
await response.AssertStatusCodeAsync(HttpStatusCode.Redirect);
Assert.Equal("/Login?ReturnUrl=%2FAuthorizedAction%2FIndex", response.Headers.Location.PathAndQuery);
}
}
}

View File

@ -0,0 +1,15 @@
// 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.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace RazorPagesWebSite.Controllers
{
[Route("[controller]/[action]")]
[Authorize]
public class AuthorizedActionController : Controller
{
public IActionResult Index() => Ok();
}
}

View File

@ -0,0 +1,36 @@
// 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.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
namespace RazorPagesWebSite
{
public class StartupWithEndpointRouting
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => options.LoginPath = "/Login");
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.Conventions.AuthorizeFolder("/Admin");
})
.SetCompatibilityVersion(CompatibilityVersion.Latest);
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
{
routes.MapApplication();
});
app.UseAuthorization();
}
}
}

View File

@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Endpoints;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Authorization
@ -45,11 +44,19 @@ namespace Microsoft.AspNetCore.Authorization
throw new ArgumentNullException(nameof(context));
}
var endpoint = context.GetEndpoint();
// Workaround for https://github.com/aspnet/AspNetCore/issues/7011. Do not use the AuthorizationMiddleware for Razor Pages
if (endpoint.Metadata.Any(m => m.GetType().FullName == "Microsoft.AspNetCore.Mvc.ApplicationModels.PageRouteMetadata"))
{
await _next(context);
return;
}
// 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);
if (policy == null)