From 7cadb58e12b609307018519f22694b6ba058e91d Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 27 Feb 2017 15:49:12 -0800 Subject: [PATCH] Add convenience extension methods for IPageApplicationModelConvention --- .../IPageApplicationModelConvention.cs | 2 +- .../MvcRazorPagesMvcBuilderExtensions.cs | 39 +++++ .../MvcRazorPagesMvcCoreBuilderExtensions.cs | 7 + .../RazorPagesOptionsExtensions.cs | 134 ++++++++++++++++++ .../PageActionDescriptorProvider.cs | 4 - .../Internal/RazorPagesOptionsSetup.cs | 26 ++++ .../RazorPagesOptions.cs | 5 +- .../RazorPagesTest.cs | 14 ++ .../MvcRazorPagesMvcBuilderExtensionsTest.cs | 38 +++++ .../RazorPagesOptionsExtensionsTest.cs | 117 +++++++++++++++ .../PageActionDescriptorProviderTest.cs | 30 ++-- .../Internal/RazorPagesOptionsSetupTest.cs | 33 +++++ .../HelloWorldWithAuth.cshtml | 3 + test/WebSites/RazorPagesWebSite/Login.cshtml | 2 + .../RazorPagesWebSite.csproj | 1 + test/WebSites/RazorPagesWebSite/Startup.cs | 17 ++- 16 files changed, 451 insertions(+), 21 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcBuilderExtensions.cs create mode 100644 src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/RazorPagesOptionsExtensions.cs create mode 100644 src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/RazorPagesOptionsSetup.cs create mode 100644 test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/MvcRazorPagesMvcBuilderExtensionsTest.cs create mode 100644 test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/RazorPagesOptionsExtensionsTest.cs create mode 100644 test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/RazorPagesOptionsSetupTest.cs create mode 100644 test/WebSites/RazorPagesWebSite/HelloWorldWithAuth.cshtml create mode 100644 test/WebSites/RazorPagesWebSite/Login.cshtml diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageApplicationModelConvention.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageApplicationModelConvention.cs index 2dbe608d8c..2ffe77c41b 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageApplicationModelConvention.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageApplicationModelConvention.cs @@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels /// /// Allows customization of the of the . /// - public interface IPageModelConvention + public interface IPageApplicationModelConvention { /// /// Called to apply the convention to the . diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcBuilderExtensions.cs new file mode 100644 index 0000000000..353eaab441 --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcBuilderExtensions.cs @@ -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 +{ + /// + /// Extensions methods for configuring Razor Pages via an . + /// + public static class MvcRazorPagesMvcBuilderExtensions + { + /// + /// Configures a set of for the application. + /// + /// The . + /// An action to configure the . + /// The . + public static IMvcBuilder AddRazorPagesOptions( + this IMvcBuilder builder, + Action setupAction) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (setupAction == null) + { + throw new ArgumentNullException(nameof(setupAction)); + } + + builder.Services.Configure(setupAction); + return builder; + } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs index 974dfb8ff5..bb71084366 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.RazorPages.Internal; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; namespace Microsoft.Extensions.DependencyInjection { @@ -51,6 +52,11 @@ namespace Microsoft.Extensions.DependencyInjection // Internal for testing. internal static void AddServices(IServiceCollection services) { + services.TryAddEnumerable( + ServiceDescriptor.Transient< + IConfigureOptions, + RazorPagesOptionsSetup>()); + services.TryAddEnumerable( ServiceDescriptor.Singleton()); @@ -72,6 +78,7 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAddSingleton(); services.TryAddSingleton(); + } } } diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/RazorPagesOptionsExtensions.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/RazorPagesOptionsExtensions.cs new file mode 100644 index 0000000000..7723852da9 --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/RazorPagesOptionsExtensions.cs @@ -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 +{ + /// + /// Extensions for . + /// + public static class RazorPagesOptionsExtensions + { + /// + /// Configures the specified to apply to all Razor Pages. + /// + /// The to configure. + /// The to add. + /// The . + 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; + } + + /// + /// Adds a with the specified policy to the page with the specified path. + /// + /// The to configure. + /// The path of the Razor Page. + /// The authorization policy. + /// The . + 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; + } + + /// + /// Adds a with the specified policy to all page under the specified path. + /// + /// The to configure. + /// The folder path. + /// The authorization policy. + /// The . + 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 _action; + + public PageConvention(string path, Action 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 _action; + + public FolderConvention(string folderPath, Action 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); + } + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs index c748673790..b9333afe6c 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs @@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Routing; -using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal; using Microsoft.AspNetCore.Razor.Evolution; using Microsoft.Extensions.Options; @@ -85,9 +84,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure 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++) { _pagesOptions.Conventions[i].Apply(model); diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/RazorPagesOptionsSetup.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/RazorPagesOptionsSetup.cs new file mode 100644 index 0000000000..18c32c7c82 --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/RazorPagesOptionsSetup.cs @@ -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 + { + 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()); + } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/RazorPagesOptions.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/RazorPagesOptions.cs index 4d931a796c..e7a5d5b37e 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/RazorPagesOptions.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/RazorPagesOptions.cs @@ -1,6 +1,7 @@ // 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.Collections.Generic; using Microsoft.AspNetCore.Mvc.ApplicationModels; @@ -12,9 +13,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages public class RazorPagesOptions { /// - /// Gets a list of instances that will be applied to + /// Gets a list of instances that will be applied to /// the when discovering Razor Pages. /// - public IList Conventions { get; } = new List(); + public IList Conventions { get; } = new List(); } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs index b67c5405f2..1420b1a1c6 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs @@ -131,6 +131,20 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests 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) { var setCookie = response.Headers.GetValues("Set-Cookie").ToArray(); diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/MvcRazorPagesMvcBuilderExtensionsTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/MvcRazorPagesMvcBuilderExtensionsTest.cs new file mode 100644 index 0000000000..548fc09dbd --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/MvcRazorPagesMvcBuilderExtensionsTest.cs @@ -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(); + var builder = new MvcBuilder(services, new ApplicationPartManager()); + builder.AddRazorPagesOptions(options => + { + options.Conventions.Add(expected); + }); + var serviceProvider = services.BuildServiceProvider(); + var accessor = serviceProvider.GetRequiredService>(); + + // Act + var conventions = accessor.Value.Conventions; + + // Assert + Assert.Collection(conventions, + convention => Assert.Same(expected, convention)); + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/RazorPagesOptionsExtensionsTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/RazorPagesOptionsExtensionsTest.cs new file mode 100644 index 0000000000..6f2bca034c --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/RazorPagesOptionsExtensionsTest.cs @@ -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(); + 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(Assert.Single(model.Filters)); + var authorizeData = Assert.IsType(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(Assert.Single(model.Filters)); + var authorizeData = Assert.IsType(Assert.Single(authorizeFilter.AuthorizeData)); + Assert.Equal("Manage-Accounts", authorizeData.Policy); + }, + model => + { + Assert.Equal("/Users/Contact.cshtml", model.ViewEnginePath); + var authorizeFilter = Assert.IsType(Assert.Single(model.Filters)); + var authorizeData = Assert.IsType(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); + } + } + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorProviderTest.cs index d023636248..dc3f7055e5 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorProviderTest.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Razor.Internal; +using Microsoft.AspNetCore.Mvc.RazorPages.Internal; using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal; using Microsoft.AspNetCore.Razor.Evolution; using Microsoft.Extensions.Options; @@ -30,7 +31,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure var provider = new PageActionDescriptorProvider( razorProject.Object, GetAccessor(), - GetAccessor()); + GetRazorPagesOptions()); var context = new ActionDescriptorProviderContext(); // Act @@ -53,7 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure var provider = new PageActionDescriptorProvider( razorProject.Object, GetAccessor(), - GetAccessor()); + GetRazorPagesOptions()); var context = new ActionDescriptorProviderContext(); // Act @@ -80,7 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure var provider = new PageActionDescriptorProvider( razorProject.Object, GetAccessor(), - GetAccessor()); + GetRazorPagesOptions()); var context = new ActionDescriptorProviderContext(); // Act @@ -109,7 +110,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure var provider = new PageActionDescriptorProvider( razorProject.Object, GetAccessor(), - GetAccessor()); + GetRazorPagesOptions()); var context = new ActionDescriptorProviderContext(); // Act and Assert @@ -133,7 +134,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure var provider = new PageActionDescriptorProvider( razorProject.Object, GetAccessor(), - GetAccessor()); + GetRazorPagesOptions()); var context = new ActionDescriptorProviderContext(); // Act @@ -170,7 +171,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure var provider = new PageActionDescriptorProvider( razorProject.Object, GetAccessor(), - GetAccessor()); + GetRazorPagesOptions()); var context = new ActionDescriptorProviderContext(); // Act @@ -208,7 +209,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure var provider = new PageActionDescriptorProvider( razorProject.Object, GetAccessor(options), - GetAccessor()); + GetRazorPagesOptions()); var context = new ActionDescriptorProviderContext(); // Act @@ -250,7 +251,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure var provider = new PageActionDescriptorProvider( razorProject.Object, GetAccessor(options), - GetAccessor()); + GetRazorPagesOptions()); var context = new ActionDescriptorProviderContext(); // Act @@ -291,14 +292,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure var localFilter = Mock.Of(); var options = new MvcOptions(); options.Filters.Add(globalFilter); - var convention = new Mock(); + var convention = new Mock(); convention.Setup(c => c.Apply(It.IsAny())) .Callback((PageApplicationModel model) => { model.Filters.Add(localFilter); }); - var razorOptions = new RazorPagesOptions(); - razorOptions.Conventions.Add(convention.Object); + var razorOptions = GetRazorPagesOptions(); + razorOptions.Value.Conventions.Add(convention.Object); var razorProject = new Mock(); razorProject.Setup(p => p.EnumerateItems("/")) @@ -309,7 +310,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure var provider = new PageActionDescriptorProvider( razorProject.Object, GetAccessor(options), - GetAccessor(razorOptions)); + razorOptions); var context = new ActionDescriptorProviderContext(); // Act @@ -349,6 +350,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure return accessor.Object; } + private static IOptions GetRazorPagesOptions() + { + return new OptionsManager(new[] { new RazorPagesOptionsSetup() }); + } + private static RazorProjectItem GetProjectItem(string basePath, string path, string content) { var testFileInfo = new TestFileInfo diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/RazorPagesOptionsSetupTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/RazorPagesOptionsSetupTest.cs new file mode 100644 index 0000000000..54b3d4d863 --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/RazorPagesOptionsSetupTest.cs @@ -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(filter), + filter => Assert.IsType(filter)); + } + } +} diff --git a/test/WebSites/RazorPagesWebSite/HelloWorldWithAuth.cshtml b/test/WebSites/RazorPagesWebSite/HelloWorldWithAuth.cshtml new file mode 100644 index 0000000000..c6a654ccce --- /dev/null +++ b/test/WebSites/RazorPagesWebSite/HelloWorldWithAuth.cshtml @@ -0,0 +1,3 @@ +@page + +Can't see me \ No newline at end of file diff --git a/test/WebSites/RazorPagesWebSite/Login.cshtml b/test/WebSites/RazorPagesWebSite/Login.cshtml new file mode 100644 index 0000000000..939fa6c3ed --- /dev/null +++ b/test/WebSites/RazorPagesWebSite/Login.cshtml @@ -0,0 +1,2 @@ +@page +@Context.Request.Query["ReturnUrl"] \ No newline at end of file diff --git a/test/WebSites/RazorPagesWebSite/RazorPagesWebSite.csproj b/test/WebSites/RazorPagesWebSite/RazorPagesWebSite.csproj index 2303099db6..31acae6182 100644 --- a/test/WebSites/RazorPagesWebSite/RazorPagesWebSite.csproj +++ b/test/WebSites/RazorPagesWebSite/RazorPagesWebSite.csproj @@ -10,6 +10,7 @@ + diff --git a/test/WebSites/RazorPagesWebSite/Startup.cs b/test/WebSites/RazorPagesWebSite/Startup.cs index 0959cd28d8..1ec471eca4 100644 --- a/test/WebSites/RazorPagesWebSite/Startup.cs +++ b/test/WebSites/RazorPagesWebSite/Startup.cs @@ -10,15 +10,28 @@ namespace RazorPagesWebSite { public void ConfigureServices(IServiceCollection services) { - services.AddMvc().AddCookieTempDataProvider(); + services + .AddMvc() + .AddCookieTempDataProvider() + .AddRazorPagesOptions(options => + { + options.AuthorizePage("/HelloWorldWithAuth", string.Empty); + }); } public void Configure(IApplicationBuilder app) { app.UseCultureReplacer(); + app.UseCookieAuthentication(new CookieAuthenticationOptions + { + LoginPath = "/Login", + AutomaticAuthenticate = true, + AutomaticChallenge = true + }); + app.UseStaticFiles(); - + app.UseMvc(); } }