Add convenience extension methods for IPageApplicationModelConvention
This commit is contained in:
parent
f6d25f7117
commit
7cadb58e12
|
|
@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows customization of the of the <see cref="PageApplicationModel"/>.
|
/// Allows customization of the of the <see cref="PageApplicationModel"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IPageModelConvention
|
public interface IPageApplicationModelConvention
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called to apply the convention to the <see cref="PageApplicationModel"/>.
|
/// Called to apply the convention to the <see cref="PageApplicationModel"/>.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Mvc.Razor;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extensions methods for configuring Razor Pages via an <see cref="IMvcBuilder"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class MvcRazorPagesMvcBuilderExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures a set of <see cref="RazorViewEngineOptions"/> for the application.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
|
||||||
|
/// <param name="setupAction">An action to configure the <see cref="RazorViewEngineOptions"/>.</param>
|
||||||
|
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
|
||||||
|
public static IMvcBuilder AddRazorPagesOptions(
|
||||||
|
this IMvcBuilder builder,
|
||||||
|
Action<RazorPagesOptions> setupAction)
|
||||||
|
{
|
||||||
|
if (builder == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setupAction == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(setupAction));
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Services.Configure(setupAction);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
|
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
{
|
{
|
||||||
|
|
@ -51,6 +52,11 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
// Internal for testing.
|
// Internal for testing.
|
||||||
internal static void AddServices(IServiceCollection services)
|
internal static void AddServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
services.TryAddEnumerable(
|
||||||
|
ServiceDescriptor.Transient<
|
||||||
|
IConfigureOptions<RazorPagesOptions>,
|
||||||
|
RazorPagesOptionsSetup>());
|
||||||
|
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Singleton<IActionDescriptorProvider, PageActionDescriptorProvider>());
|
ServiceDescriptor.Singleton<IActionDescriptorProvider, PageActionDescriptorProvider>());
|
||||||
|
|
||||||
|
|
@ -72,6 +78,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
services.TryAddSingleton<IActionDescriptorChangeProvider, PageActionDescriptorChangeProvider>();
|
services.TryAddSingleton<IActionDescriptorChangeProvider, PageActionDescriptorChangeProvider>();
|
||||||
|
|
||||||
services.TryAddSingleton<TempDataPropertyProvider>();
|
services.TryAddSingleton<TempDataPropertyProvider>();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extensions for <see cref="RazorPagesOptions"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class RazorPagesOptionsExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures the specified <paramref name="filter"/> to apply to all Razor Pages.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The <see cref="RazorPagesOptions"/> to configure.</param>
|
||||||
|
/// <param name="filter">The <see cref="IFilterMetadata"/> to add.</param>
|
||||||
|
/// <returns>The <see cref="RazorPagesOptions"/>.</returns>
|
||||||
|
public static RazorPagesOptions ConfigureFilter(this RazorPagesOptions options, IFilterMetadata filter)
|
||||||
|
{
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
options.Conventions.Add(new FolderConvention("/", model => model.Filters.Add(filter)));
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a <see cref="AuthorizeFilter"/> with the specified policy to the page with the specified path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The <see cref="RazorPagesOptions"/> to configure.</param>
|
||||||
|
/// <param name="path">The path of the Razor Page.</param>
|
||||||
|
/// <param name="policy">The authorization policy.</param>
|
||||||
|
/// <returns>The <see cref="RazorPagesOptions"/>.</returns>
|
||||||
|
public static RazorPagesOptions AuthorizePage(this RazorPagesOptions options, string path, string policy)
|
||||||
|
{
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
var authorizeFilter = new AuthorizeFilter(policy);
|
||||||
|
options.Conventions.Add(new PageConvention(path, model => model.Filters.Add(authorizeFilter)));
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a <see cref="AuthorizeFilter"/> with the specified policy to all page under the specified path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The <see cref="RazorPagesOptions"/> to configure.</param>
|
||||||
|
/// <param name="folderPath">The folder path.</param>
|
||||||
|
/// <param name="policy">The authorization policy.</param>
|
||||||
|
/// <returns>The <see cref="RazorPagesOptions"/>.</returns>
|
||||||
|
public static RazorPagesOptions AuthorizeFolder(this RazorPagesOptions options, string folderPath, string policy)
|
||||||
|
{
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(folderPath))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(folderPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
var authorizeFilter = new AuthorizeFilter(policy);
|
||||||
|
options.Conventions.Add(new FolderConvention(folderPath, model => model.Filters.Add(authorizeFilter)));
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PageConvention : IPageApplicationModelConvention
|
||||||
|
{
|
||||||
|
private readonly string _path;
|
||||||
|
private readonly Action<PageApplicationModel> _action;
|
||||||
|
|
||||||
|
public PageConvention(string path, Action<PageApplicationModel> action)
|
||||||
|
{
|
||||||
|
_path = path;
|
||||||
|
_action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply(PageApplicationModel model)
|
||||||
|
{
|
||||||
|
if (string.Equals(model.ViewEnginePath, _path, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
_action(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FolderConvention : IPageApplicationModelConvention
|
||||||
|
{
|
||||||
|
private readonly string _folderPath;
|
||||||
|
private readonly Action<PageApplicationModel> _action;
|
||||||
|
|
||||||
|
public FolderConvention(string folderPath, Action<PageApplicationModel> action)
|
||||||
|
{
|
||||||
|
_folderPath = folderPath.TrimEnd('/');
|
||||||
|
_action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply(PageApplicationModel model)
|
||||||
|
{
|
||||||
|
var viewEnginePath = model.ViewEnginePath;
|
||||||
|
|
||||||
|
var applyConvention = _folderPath == "/" ||
|
||||||
|
(viewEnginePath.Length > _folderPath.Length &&
|
||||||
|
viewEnginePath.StartsWith(_folderPath, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
viewEnginePath[_folderPath.Length] == '/');
|
||||||
|
|
||||||
|
if (applyConvention)
|
||||||
|
{
|
||||||
|
_action(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
|
||||||
using Microsoft.AspNetCore.Razor.Evolution;
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
|
@ -85,9 +84,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
model.Selectors.Add(CreateSelectorModel(parentDirectoryPath, template));
|
model.Selectors.Add(CreateSelectorModel(parentDirectoryPath, template));
|
||||||
}
|
}
|
||||||
|
|
||||||
model.Filters.Add(new SaveTempDataPropertyFilter()); // Support for [TempData] on properties
|
|
||||||
model.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); // Always require an antiforgery token on post
|
|
||||||
|
|
||||||
for (var i = 0; i < _pagesOptions.Conventions.Count; i++)
|
for (var i = 0; i < _pagesOptions.Conventions.Count; i++)
|
||||||
{
|
{
|
||||||
_pagesOptions.Conventions[i].Apply(model);
|
_pagesOptions.Conventions[i].Apply(model);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
{
|
||||||
|
public class RazorPagesOptionsSetup : IConfigureOptions<RazorPagesOptions>
|
||||||
|
{
|
||||||
|
public void Configure(RazorPagesOptions options)
|
||||||
|
{
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support for [TempData] on properties
|
||||||
|
options.ConfigureFilter(new SaveTempDataPropertyFilter());
|
||||||
|
// Always require an antiforgery token on post
|
||||||
|
options.ConfigureFilter(new AutoValidateAntiforgeryTokenAttribute());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// 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.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
|
||||||
|
|
@ -12,9 +13,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
public class RazorPagesOptions
|
public class RazorPagesOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a list of <see cref="IPageModelConvention"/> instances that will be applied to
|
/// Gets a list of <see cref="IPageApplicationModelConvention"/> instances that will be applied to
|
||||||
/// the <see cref="PageModel"/> when discovering Razor Pages.
|
/// the <see cref="PageModel"/> when discovering Razor Pages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<IPageModelConvention> Conventions { get; } = new List<IPageModelConvention>();
|
public IList<IPageApplicationModelConvention> Conventions { get; } = new List<IPageApplicationModelConvention>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,6 +131,20 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
Assert.Equal("Hi2", content.Trim());
|
Assert.Equal("Hi2", content.Trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AuthorizePage_AddsAuthorizationForSpecificPages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var url = "/HelloWorldWithAuth";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetAsync(url);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||||
|
Assert.Equal("/Login?ReturnUrl=%2FHelloWorldWithAuth", response.Headers.Location.PathAndQuery);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetCookie(HttpResponseMessage response)
|
private static string GetCookie(HttpResponseMessage response)
|
||||||
{
|
{
|
||||||
var setCookie = response.Headers.GetValues("Set-Cookie").ToArray();
|
var setCookie = response.Headers.GetValues("Set-Cookie").ToArray();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// 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.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
{
|
||||||
|
public class MvcRazorPagesMvcBuilderExtensionsTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void AddRazorPagesOptions_AddsApplicationModelConventions()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var services = new ServiceCollection().AddOptions();
|
||||||
|
var expected = Mock.Of<IPageApplicationModelConvention>();
|
||||||
|
var builder = new MvcBuilder(services, new ApplicationPartManager());
|
||||||
|
builder.AddRazorPagesOptions(options =>
|
||||||
|
{
|
||||||
|
options.Conventions.Add(expected);
|
||||||
|
});
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
var accessor = serviceProvider.GetRequiredService<IOptions<RazorPagesOptions>>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var conventions = accessor.Value.Conventions;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(conventions,
|
||||||
|
convention => Assert.Same(expected, convention));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
// 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.ApplicationModels;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
{
|
||||||
|
public class RazorPagesOptionsExtensionsTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void AddFilter_AddsFiltersToAllPages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter = Mock.Of<IFilterMetadata>();
|
||||||
|
var options = new RazorPagesOptions();
|
||||||
|
var models = new[]
|
||||||
|
{
|
||||||
|
new PageApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||||
|
new PageApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||||
|
new PageApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
options.ConfigureFilter(filter);
|
||||||
|
ApplyConventions(options, models);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(models,
|
||||||
|
model => Assert.Same(filter, Assert.Single(model.Filters)),
|
||||||
|
model => Assert.Same(filter, Assert.Single(model.Filters)),
|
||||||
|
model => Assert.Same(filter, Assert.Single(model.Filters)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AuthorizePage_AddsAuthorizeFilterToSpecificPage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var options = new RazorPagesOptions();
|
||||||
|
var models = new[]
|
||||||
|
{
|
||||||
|
new PageApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||||
|
new PageApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||||
|
new PageApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
options.AuthorizePage("/Users/Account.cshtml", "Manage-Accounts");
|
||||||
|
ApplyConventions(options, models);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(models,
|
||||||
|
model => Assert.Empty(model.Filters),
|
||||||
|
model =>
|
||||||
|
{
|
||||||
|
Assert.Equal("/Users/Account.cshtml", model.ViewEnginePath);
|
||||||
|
var authorizeFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(model.Filters));
|
||||||
|
var authorizeData = Assert.IsType<AuthorizeAttribute>(Assert.Single(authorizeFilter.AuthorizeData));
|
||||||
|
Assert.Equal("Manage-Accounts", authorizeData.Policy);
|
||||||
|
},
|
||||||
|
model => Assert.Empty(model.Filters));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("/Users")]
|
||||||
|
[InlineData("/Users/")]
|
||||||
|
public void AuthorizePage_AddsAuthorizeFilterToPagesUnderFolder(string folderName)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var options = new RazorPagesOptions();
|
||||||
|
var models = new[]
|
||||||
|
{
|
||||||
|
new PageApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||||
|
new PageApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||||
|
new PageApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
options.AuthorizeFolder(folderName, "Manage-Accounts");
|
||||||
|
ApplyConventions(options, models);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(models,
|
||||||
|
model => Assert.Empty(model.Filters),
|
||||||
|
model =>
|
||||||
|
{
|
||||||
|
Assert.Equal("/Users/Account.cshtml", model.ViewEnginePath);
|
||||||
|
var authorizeFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(model.Filters));
|
||||||
|
var authorizeData = Assert.IsType<AuthorizeAttribute>(Assert.Single(authorizeFilter.AuthorizeData));
|
||||||
|
Assert.Equal("Manage-Accounts", authorizeData.Policy);
|
||||||
|
},
|
||||||
|
model =>
|
||||||
|
{
|
||||||
|
Assert.Equal("/Users/Contact.cshtml", model.ViewEnginePath);
|
||||||
|
var authorizeFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(model.Filters));
|
||||||
|
var authorizeData = Assert.IsType<AuthorizeAttribute>(Assert.Single(authorizeFilter.AuthorizeData));
|
||||||
|
Assert.Equal("Manage-Accounts", authorizeData.Policy);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ApplyConventions(RazorPagesOptions options, PageApplicationModel[] models)
|
||||||
|
{
|
||||||
|
foreach (var convention in options.Conventions)
|
||||||
|
{
|
||||||
|
foreach (var model in models)
|
||||||
|
{
|
||||||
|
convention.Apply(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor;
|
using Microsoft.AspNetCore.Mvc.Razor;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution;
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
@ -30,7 +31,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
var provider = new PageActionDescriptorProvider(
|
var provider = new PageActionDescriptorProvider(
|
||||||
razorProject.Object,
|
razorProject.Object,
|
||||||
GetAccessor<MvcOptions>(),
|
GetAccessor<MvcOptions>(),
|
||||||
GetAccessor<RazorPagesOptions>());
|
GetRazorPagesOptions());
|
||||||
var context = new ActionDescriptorProviderContext();
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -53,7 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
var provider = new PageActionDescriptorProvider(
|
var provider = new PageActionDescriptorProvider(
|
||||||
razorProject.Object,
|
razorProject.Object,
|
||||||
GetAccessor<MvcOptions>(),
|
GetAccessor<MvcOptions>(),
|
||||||
GetAccessor<RazorPagesOptions>());
|
GetRazorPagesOptions());
|
||||||
var context = new ActionDescriptorProviderContext();
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -80,7 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
var provider = new PageActionDescriptorProvider(
|
var provider = new PageActionDescriptorProvider(
|
||||||
razorProject.Object,
|
razorProject.Object,
|
||||||
GetAccessor<MvcOptions>(),
|
GetAccessor<MvcOptions>(),
|
||||||
GetAccessor<RazorPagesOptions>());
|
GetRazorPagesOptions());
|
||||||
var context = new ActionDescriptorProviderContext();
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -109,7 +110,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
var provider = new PageActionDescriptorProvider(
|
var provider = new PageActionDescriptorProvider(
|
||||||
razorProject.Object,
|
razorProject.Object,
|
||||||
GetAccessor<MvcOptions>(),
|
GetAccessor<MvcOptions>(),
|
||||||
GetAccessor<RazorPagesOptions>());
|
GetRazorPagesOptions());
|
||||||
var context = new ActionDescriptorProviderContext();
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
// Act and Assert
|
// Act and Assert
|
||||||
|
|
@ -133,7 +134,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
var provider = new PageActionDescriptorProvider(
|
var provider = new PageActionDescriptorProvider(
|
||||||
razorProject.Object,
|
razorProject.Object,
|
||||||
GetAccessor<MvcOptions>(),
|
GetAccessor<MvcOptions>(),
|
||||||
GetAccessor<RazorPagesOptions>());
|
GetRazorPagesOptions());
|
||||||
var context = new ActionDescriptorProviderContext();
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -170,7 +171,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
var provider = new PageActionDescriptorProvider(
|
var provider = new PageActionDescriptorProvider(
|
||||||
razorProject.Object,
|
razorProject.Object,
|
||||||
GetAccessor<MvcOptions>(),
|
GetAccessor<MvcOptions>(),
|
||||||
GetAccessor<RazorPagesOptions>());
|
GetRazorPagesOptions());
|
||||||
var context = new ActionDescriptorProviderContext();
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -208,7 +209,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
var provider = new PageActionDescriptorProvider(
|
var provider = new PageActionDescriptorProvider(
|
||||||
razorProject.Object,
|
razorProject.Object,
|
||||||
GetAccessor(options),
|
GetAccessor(options),
|
||||||
GetAccessor<RazorPagesOptions>());
|
GetRazorPagesOptions());
|
||||||
var context = new ActionDescriptorProviderContext();
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -250,7 +251,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
var provider = new PageActionDescriptorProvider(
|
var provider = new PageActionDescriptorProvider(
|
||||||
razorProject.Object,
|
razorProject.Object,
|
||||||
GetAccessor(options),
|
GetAccessor(options),
|
||||||
GetAccessor<RazorPagesOptions>());
|
GetRazorPagesOptions());
|
||||||
var context = new ActionDescriptorProviderContext();
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -291,14 +292,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
var localFilter = Mock.Of<IFilterMetadata>();
|
var localFilter = Mock.Of<IFilterMetadata>();
|
||||||
var options = new MvcOptions();
|
var options = new MvcOptions();
|
||||||
options.Filters.Add(globalFilter);
|
options.Filters.Add(globalFilter);
|
||||||
var convention = new Mock<IPageModelConvention>();
|
var convention = new Mock<IPageApplicationModelConvention>();
|
||||||
convention.Setup(c => c.Apply(It.IsAny<PageApplicationModel>()))
|
convention.Setup(c => c.Apply(It.IsAny<PageApplicationModel>()))
|
||||||
.Callback((PageApplicationModel model) =>
|
.Callback((PageApplicationModel model) =>
|
||||||
{
|
{
|
||||||
model.Filters.Add(localFilter);
|
model.Filters.Add(localFilter);
|
||||||
});
|
});
|
||||||
var razorOptions = new RazorPagesOptions();
|
var razorOptions = GetRazorPagesOptions();
|
||||||
razorOptions.Conventions.Add(convention.Object);
|
razorOptions.Value.Conventions.Add(convention.Object);
|
||||||
|
|
||||||
var razorProject = new Mock<RazorProject>();
|
var razorProject = new Mock<RazorProject>();
|
||||||
razorProject.Setup(p => p.EnumerateItems("/"))
|
razorProject.Setup(p => p.EnumerateItems("/"))
|
||||||
|
|
@ -309,7 +310,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
var provider = new PageActionDescriptorProvider(
|
var provider = new PageActionDescriptorProvider(
|
||||||
razorProject.Object,
|
razorProject.Object,
|
||||||
GetAccessor(options),
|
GetAccessor(options),
|
||||||
GetAccessor(razorOptions));
|
razorOptions);
|
||||||
var context = new ActionDescriptorProviderContext();
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -349,6 +350,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
return accessor.Object;
|
return accessor.Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IOptions<RazorPagesOptions> GetRazorPagesOptions()
|
||||||
|
{
|
||||||
|
return new OptionsManager<RazorPagesOptions>(new[] { new RazorPagesOptionsSetup() });
|
||||||
|
}
|
||||||
|
|
||||||
private static RazorProjectItem GetProjectItem(string basePath, string path, string content)
|
private static RazorProjectItem GetProjectItem(string basePath, string path, string content)
|
||||||
{
|
{
|
||||||
var testFileInfo = new TestFileInfo
|
var testFileInfo = new TestFileInfo
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// 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.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
{
|
||||||
|
public class RazorPagesOptionsSetupTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Configure_AddsGlobalFilters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var options = new RazorPagesOptions();
|
||||||
|
var setup = new RazorPagesOptionsSetup();
|
||||||
|
var applicationModel = new PageApplicationModel("/Home.cshtml", "/Home.cshtml");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
setup.Configure(options);
|
||||||
|
foreach (var convention in options.Conventions)
|
||||||
|
{
|
||||||
|
convention.Apply(applicationModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(applicationModel.Filters,
|
||||||
|
filter => Assert.IsType<SaveTempDataPropertyFilter>(filter),
|
||||||
|
filter => Assert.IsType<AutoValidateAntiforgeryTokenAttribute>(filter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
@page
|
||||||
|
|
||||||
|
Can't see me
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
@page
|
||||||
|
@Context.Request.Query["ReturnUrl"]
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.Mvc\Microsoft.AspNetCore.Mvc.csproj" />
|
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.Mvc\Microsoft.AspNetCore.Mvc.csproj" />
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.TestConfiguration\Microsoft.AspNetCore.Mvc.TestConfiguration.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.TestConfiguration\Microsoft.AspNetCore.Mvc.TestConfiguration.csproj" />
|
||||||
|
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.2.0-*" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.2.0-*" />
|
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.2.0-*" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.2.0-*" />
|
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.2.0-*" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.2.0-*" />
|
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.2.0-*" />
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,28 @@ namespace RazorPagesWebSite
|
||||||
{
|
{
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddMvc().AddCookieTempDataProvider();
|
services
|
||||||
|
.AddMvc()
|
||||||
|
.AddCookieTempDataProvider()
|
||||||
|
.AddRazorPagesOptions(options =>
|
||||||
|
{
|
||||||
|
options.AuthorizePage("/HelloWorldWithAuth", string.Empty);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
app.UseCultureReplacer();
|
app.UseCultureReplacer();
|
||||||
|
|
||||||
|
app.UseCookieAuthentication(new CookieAuthenticationOptions
|
||||||
|
{
|
||||||
|
LoginPath = "/Login",
|
||||||
|
AutomaticAuthenticate = true,
|
||||||
|
AutomaticChallenge = true
|
||||||
|
});
|
||||||
|
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
|
|
||||||
app.UseMvc();
|
app.UseMvc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue