From 8e15e265fd8ce44df4cad61a91d810a624a4b712 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 26 Nov 2020 16:52:03 +1100 Subject: [PATCH] Adds TODOs, puts back UmbracoRequireHttpsAttribute but in a nicer way, ensures test classes initialize the authz policies with the test scheme --- .../TestServerTest/TestAuthHandler.cs | 4 +- .../UmbracoTestServerTestBase.cs | 5 +- .../Authorization/AdminUsersHandler.cs | 4 +- .../UmbracoAuthorizedApiController.cs | 4 +- .../BackOfficeServiceCollectionExtensions.cs | 102 +++++++++--------- .../Extensions/UmbracoBuilderExtensions.cs | 9 +- .../AppendCurrentEventMessagesAttribute.cs | 1 + .../Filters/UmbracoRequireHttpsAttribute.cs | 27 +++++ .../Controllers/UmbracoApiController.cs | 2 + .../Controllers/UmbracoApiControllerBase.cs | 2 + ...bracoApiControllerTypeCollectionBuilder.cs | 2 + .../Extensions/ServiceCollectionExtensions.cs | 3 + 12 files changed, 107 insertions(+), 58 deletions(-) create mode 100644 src/Umbraco.Web.BackOffice/Filters/UmbracoRequireHttpsAttribute.cs diff --git a/src/Umbraco.Tests.Integration/TestServerTest/TestAuthHandler.cs b/src/Umbraco.Tests.Integration/TestServerTest/TestAuthHandler.cs index 08cd49bd81..2cafaf913f 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/TestAuthHandler.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/TestAuthHandler.cs @@ -14,6 +14,8 @@ namespace Umbraco.Tests.Integration.TestServerTest { public class TestAuthHandler : AuthenticationHandler { + public const string TestAuthenticationScheme = "Test"; + private readonly BackOfficeSignInManager _backOfficeSignInManager; private readonly BackOfficeIdentityUser _fakeUser; @@ -32,7 +34,7 @@ namespace Umbraco.Tests.Integration.TestServerTest { var principal = await _backOfficeSignInManager.CreateUserPrincipalAsync(_fakeUser); - var ticket = new AuthenticationTicket(principal, "Test"); + var ticket = new AuthenticationTicket(principal, TestAuthenticationScheme); return AuthenticateResult.Success(ticket); } diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index f6ece372ea..5867e6522c 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -47,7 +47,9 @@ namespace Umbraco.Tests.Integration.TestServerTest // Executes after the standard ConfigureServices method builder.ConfigureTestServices(services => { - services.AddAuthentication("Test").AddScheme("Test", options => { }); + // Add a test auth scheme with a test auth handler to authn and assign the user + services.AddAuthentication(TestAuthHandler.TestAuthenticationScheme) + .AddScheme(TestAuthHandler.TestAuthenticationScheme, options => { }); }); }); @@ -142,6 +144,7 @@ namespace Umbraco.Tests.Integration.TestServerTest .AddRuntimeMinifier() .AddBackOffice() .AddBackOfficeIdentity() + .AddBackOfficeAuthorizationPolicies(TestAuthHandler.TestAuthenticationScheme) .AddPreviewSupport() //.WithMiniProfiler() // we don't want this running in tests .AddMvcAndRazor(mvcBuilding: mvcBuilder => diff --git a/src/Umbraco.Web.BackOffice/Authorization/AdminUsersHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/AdminUsersHandler.cs index ff7ba13937..13711b825c 100644 --- a/src/Umbraco.Web.BackOffice/Authorization/AdminUsersHandler.cs +++ b/src/Umbraco.Web.BackOffice/Authorization/AdminUsersHandler.cs @@ -1,16 +1,18 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Umbraco.Core; +using Umbraco.Core.Configuration.Models; using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.Editors; namespace Umbraco.Web.BackOffice.Authorization { - /// /// if the users being edited is an admin then we must ensure that the current user is also an admin /// diff --git a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs index f1a39e1a76..a807c663d0 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs @@ -16,11 +16,11 @@ namespace Umbraco.Web.BackOffice.Controllers /// is logged in using forms authentication which indicates the seconds remaining /// before their timeout expires. /// - [IsBackOffice] + [IsBackOffice] [UmbracoUserTimeoutFilter] [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] [DisableBrowserCache] - [RequireHttps] + [UmbracoRequireHttps] [CheckIfUserTicketDataIsStale] [MiddlewareFilter(typeof(UnhandledExceptionLoggerFilter))] public abstract class UmbracoAuthorizedApiController : UmbracoApiController diff --git a/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs index 762e405d62..80c6d81b1b 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs @@ -83,7 +83,7 @@ namespace Umbraco.Extensions /// Add authorization handlers and policies /// /// - public static void AddBackOfficeAuthorizationPolicies(this IServiceCollection services) + public static void AddBackOfficeAuthorizationPolicies(this IServiceCollection services, string backOfficeAuthenticationScheme = Constants.Security.BackOfficeAuthenticationType) { // NOTE: Even though we are registering these handlers globally they will only actually execute their logic for // any auth defining a matching requirement and scheme. @@ -100,141 +100,141 @@ namespace Umbraco.Extensions services.AddSingleton(); services.AddSingleton(); - services.AddAuthorization(CreatePolicies); + services.AddAuthorization(o => CreatePolicies(o, backOfficeAuthenticationScheme)); } - private static void CreatePolicies(AuthorizationOptions options) + private static void CreatePolicies(AuthorizationOptions options, string backOfficeAuthenticationScheme) { options.AddPolicy(AuthorizationPolicies.MediaPermissionPathById, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new MediaPermissionsQueryStringRequirement("id")); }); options.AddPolicy(AuthorizationPolicies.ContentPermissionEmptyRecycleBin, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(Constants.System.RecycleBinContent, ActionDelete.ActionLetter)); }); options.AddPolicy(AuthorizationPolicies.ContentPermissionAdministrationById, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionRights.ActionLetter)); policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionRights.ActionLetter, "contentId")); }); options.AddPolicy(AuthorizationPolicies.ContentPermissionProtectById, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionProtect.ActionLetter)); policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionProtect.ActionLetter, "contentId")); }); options.AddPolicy(AuthorizationPolicies.ContentPermissionRollbackById, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionRollback.ActionLetter)); policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionRollback.ActionLetter, "contentId")); }); options.AddPolicy(AuthorizationPolicies.ContentPermissionPublishById, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionPublish.ActionLetter)); }); options.AddPolicy(AuthorizationPolicies.ContentPermissionBrowseById, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionBrowse.ActionLetter)); policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionBrowse.ActionLetter, "contentId")); }); options.AddPolicy(AuthorizationPolicies.ContentPermissionDeleteById, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionDelete.ActionLetter)); }); options.AddPolicy(AuthorizationPolicies.BackOfficeAccess, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new BackOfficeRequirement()); }); options.AddPolicy(AuthorizationPolicies.BackOfficeAccessWithoutApproval, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new BackOfficeRequirement(false)); }); options.AddPolicy(AuthorizationPolicies.AdminUserEditsRequireAdmin, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new AdminUsersRequirement()); policy.Requirements.Add(new AdminUsersRequirement("userIds")); }); options.AddPolicy(AuthorizationPolicies.UserBelongsToUserGroupInRequest, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new UserGroupRequirement()); policy.Requirements.Add(new UserGroupRequirement("userGroupIds")); }); options.AddPolicy(AuthorizationPolicies.DenyLocalLoginIfConfigured, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new DenyLocalLoginRequirement()); }); options.AddPolicy(AuthorizationPolicies.SectionAccessContent, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new SectionRequirement(Constants.Applications.Content)); }); options.AddPolicy(AuthorizationPolicies.SectionAccessContentOrMedia, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new SectionRequirement(Constants.Applications.Content, Constants.Applications.Media)); }); options.AddPolicy(AuthorizationPolicies.SectionAccessUsers, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new SectionRequirement(Constants.Applications.Users)); }); options.AddPolicy(AuthorizationPolicies.SectionAccessForTinyMce, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new SectionRequirement( Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members)); }); options.AddPolicy(AuthorizationPolicies.SectionAccessMedia, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new SectionRequirement(Constants.Applications.Media)); }); options.AddPolicy(AuthorizationPolicies.SectionAccessMembers, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new SectionRequirement(Constants.Applications.Members)); }); options.AddPolicy(AuthorizationPolicies.SectionAccessPackages, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new SectionRequirement(Constants.Applications.Packages)); }); options.AddPolicy(AuthorizationPolicies.SectionAccessSettings, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new SectionRequirement(Constants.Applications.Settings)); }); @@ -242,21 +242,21 @@ namespace Umbraco.Extensions // this is not ideal but until we change permissions to be tree based (not section) there's not much else we can do here. options.AddPolicy(AuthorizationPolicies.SectionAccessForContentTree, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new SectionRequirement( Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Users, Constants.Applications.Settings, Constants.Applications.Packages, Constants.Applications.Members)); }); options.AddPolicy(AuthorizationPolicies.SectionAccessForMediaTree, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new SectionRequirement( Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Users, Constants.Applications.Settings, Constants.Applications.Packages, Constants.Applications.Members)); }); options.AddPolicy(AuthorizationPolicies.SectionAccessForMemberTree, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new SectionRequirement( Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members)); }); @@ -264,7 +264,7 @@ namespace Umbraco.Extensions // Permission is granted to this policy if the user has access to any of these sections: Content, media, settings, developer, members options.AddPolicy(AuthorizationPolicies.SectionAccessForDataTypeReading, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new SectionRequirement( Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members, Constants.Applications.Settings, Constants.Applications.Packages)); @@ -272,139 +272,139 @@ namespace Umbraco.Extensions options.AddPolicy(AuthorizationPolicies.TreeAccessDocuments, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.Content)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessUsers, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.Users)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessPartialViews, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.PartialViews)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessPartialViewMacros, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.PartialViewMacros)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessPackages, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.Packages)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessLogs, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.LogViewer)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessDataTypes, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.DataTypes)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessTemplates, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.Templates)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessMemberTypes, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.MemberTypes)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessRelationTypes, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.RelationTypes)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessDocumentTypes, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.DocumentTypes)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessMemberGroups, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.MemberGroups)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessMediaTypes, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.MediaTypes)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessMacros, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.Macros)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessLanguages, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.Languages)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessDocumentTypes, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.Dictionary)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessDictionary, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.Dictionary, Constants.Trees.Dictionary)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessDictionaryOrTemplates, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.Dictionary, Constants.Trees.Templates)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessDocumentsOrDocumentTypes, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.DocumentTypes, Constants.Trees.Content)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessMediaOrMediaTypes, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.MediaTypes, Constants.Trees.Media)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessMembersOrMemberTypes, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.MemberTypes, Constants.Trees.Members)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessAnySchemaTypes, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement(Constants.Trees.DataTypes, Constants.Trees.DocumentTypes, Constants.Trees.MediaTypes, Constants.Trees.MemberTypes)); }); options.AddPolicy(AuthorizationPolicies.TreeAccessAnyContentOrTypes, policy => { - policy.AuthenticationSchemes.Add(Constants.Security.BackOfficeAuthenticationType); + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); policy.Requirements.Add(new TreeRequirement( Constants.Trees.DocumentTypes, Constants.Trees.Content, Constants.Trees.MediaTypes, Constants.Trees.Media, diff --git a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBuilderExtensions.cs index 2b6eb1d111..7262966615 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBuilderExtensions.cs @@ -1,6 +1,7 @@ using System; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualBasic; using Umbraco.Core.Builder; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.BackOffice.Security; @@ -54,9 +55,13 @@ namespace Umbraco.Extensions return builder; } - public static IUmbracoBuilder AddBackOfficeAuthorizationPolicies(this IUmbracoBuilder builder) + public static IUmbracoBuilder AddBackOfficeAuthorizationPolicies(this IUmbracoBuilder builder, string backOfficeAuthenticationScheme = Umbraco.Core.Constants.Security.BackOfficeAuthenticationType) { - builder.Services.AddBackOfficeAuthorizationPolicies(); + builder.Services.AddBackOfficeAuthorizationPolicies(backOfficeAuthenticationScheme); + // TODO: See other TODOs in things like UmbracoApiControllerBase ... AFAIK all of this is only used for the back office + // so IMO these controllers and the features auth policies should just be moved to the back office project and then this + // ext method can be removed. + builder.Services.AddUmbracoCommonAuthorizationPolicies(); return builder; } diff --git a/src/Umbraco.Web.BackOffice/Filters/AppendCurrentEventMessagesAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/AppendCurrentEventMessagesAttribute.cs index 082d7c5e52..8d8a3ffa9a 100644 --- a/src/Umbraco.Web.BackOffice/Filters/AppendCurrentEventMessagesAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/AppendCurrentEventMessagesAttribute.cs @@ -7,6 +7,7 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.BackOffice.Filters { + /// /// Automatically checks if any request is a non-GET and if the /// resulting message is INotificationModel in which case it will append any Event Messages diff --git a/src/Umbraco.Web.BackOffice/Filters/UmbracoRequireHttpsAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/UmbracoRequireHttpsAttribute.cs new file mode 100644 index 0000000000..a027531e06 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Filters/UmbracoRequireHttpsAttribute.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Umbraco.Core.Configuration.Models; + +namespace Umbraco.Web.BackOffice.Filters +{ + /// + /// If Umbraco.Core.UseHttps property in web.config is set to true, this filter will redirect any http access to https. + /// + public class UmbracoRequireHttpsAttribute : RequireHttpsAttribute + { + protected override void HandleNonHttpsRequest(AuthorizationFilterContext filterContext) + { + // just like the base class does, we'll just resolve the required services from the httpcontext. + // we want to re-use their code so we don't have much choice, else we have to do some code tricks, + // this is just easiest. + var optionsAccessor = filterContext.HttpContext.RequestServices.GetRequiredService>(); + if (optionsAccessor.Value.UseHttps) + { + // only continue if this flag is set + base.HandleNonHttpsRequest(filterContext); + } + } + } +} diff --git a/src/Umbraco.Web.Common/Controllers/UmbracoApiController.cs b/src/Umbraco.Web.Common/Controllers/UmbracoApiController.cs index c53b08d3df..e3250c2983 100644 --- a/src/Umbraco.Web.Common/Controllers/UmbracoApiController.cs +++ b/src/Umbraco.Web.Common/Controllers/UmbracoApiController.cs @@ -7,6 +7,8 @@ namespace Umbraco.Web.Common.Controllers /// public abstract class UmbracoApiController : UmbracoApiControllerBase, IDiscoverable { + // TODO: Should this only exist in the back office project? These really are only ever used for the back office AFAIK + protected UmbracoApiController() { } diff --git a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerBase.cs b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerBase.cs index 87b7ee7c72..9f9e2b19be 100644 --- a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerBase.cs +++ b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerBase.cs @@ -19,6 +19,8 @@ namespace Umbraco.Web.Common.Controllers [UmbracoApiController] public abstract class UmbracoApiControllerBase : ControllerBase, IUmbracoFeature { + // TODO: Should this only exist in the back office project? These really are only ever used for the back office AFAIK + public UmbracoApiControllerBase() { } diff --git a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs index 4bffb0d5ba..8d68e95dd8 100644 --- a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs +++ b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs @@ -5,6 +5,8 @@ namespace Umbraco.Web.Common.Controllers { public class UmbracoApiControllerTypeCollectionBuilder : TypeCollectionBuilderBase { + // TODO: Should this only exist in the back office project? These really are only ever used for the back office AFAIK + protected override UmbracoApiControllerTypeCollectionBuilder This => this; } } diff --git a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs index 0a51ace294..e8e3e2c329 100644 --- a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs @@ -9,6 +9,9 @@ namespace Umbraco.Extensions { public static void AddUmbracoCommonAuthorizationPolicies(this IServiceCollection services) { + // TODO: Should this only exist in the back office project? These really are only ever used for the back office AFAIK + // If it is moved it should only target the back office scheme + services.AddSingleton(); services.AddAuthorization(options =>