From f45d4a84f7c7a33c326a3df9451fa701ab03b880 Mon Sep 17 00:00:00 2001 From: VWA Software internet Date: Tue, 19 Apr 2022 14:04:11 +0200 Subject: [PATCH 1/5] =?UTF-8?q?Include=20the=20PluginController=20Area=20w?= =?UTF-8?q?hen=20searching=20for=20matching=20surface=E2=80=A6=20(#12218)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Routing/ControllerActionSearcher.cs | 22 ++++++++++++++++--- .../Routing/IControllerActionSearcher.cs | 6 +++++ .../Routing/UmbracoRouteValueTransformer.cs | 2 +- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.Website/Routing/ControllerActionSearcher.cs b/src/Umbraco.Web.Website/Routing/ControllerActionSearcher.cs index 5c758a948c..bf7c0aff59 100644 --- a/src/Umbraco.Web.Website/Routing/ControllerActionSearcher.cs +++ b/src/Umbraco.Web.Website/Routing/ControllerActionSearcher.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Http; @@ -31,13 +32,20 @@ namespace Umbraco.Cms.Web.Website.Routing _actionSelector = actionSelector; } + /// /// Determines if a custom controller can hijack the current route /// /// The controller type to find - public ControllerActionDescriptor Find(HttpContext httpContext, string controller, string action) + public ControllerActionDescriptor Find(HttpContext httpContext, string controller, string action) => Find(httpContext, controller, action, null); + + /// + /// Determines if a custom controller can hijack the current route + /// + /// The controller type to find + public ControllerActionDescriptor Find(HttpContext httpContext, string controller, string action, string area) { - IReadOnlyList candidates = FindControllerCandidates(httpContext, controller, action, DefaultActionName); + IReadOnlyList candidates = FindControllerCandidates(httpContext, controller, action, DefaultActionName, area); if (candidates.Count > 0) { @@ -47,6 +55,7 @@ namespace Umbraco.Cms.Web.Website.Routing return null; } + /// /// Return a list of controller candidates that match the custom controller and action names /// @@ -54,7 +63,8 @@ namespace Umbraco.Cms.Web.Website.Routing HttpContext httpContext, string customControllerName, string customActionName, - string defaultActionName) + string defaultActionName, + string area = null) { // Use aspnetcore's IActionSelector to do the finding since it uses an optimized cache lookup var routeValues = new RouteValueDictionary @@ -62,6 +72,12 @@ namespace Umbraco.Cms.Web.Website.Routing [ControllerToken] = customControllerName, [ActionToken] = customActionName, // first try to find the custom action }; + + if (area != null) + { + routeValues[AreaToken] = area; + } + var routeData = new RouteData(routeValues); var routeContext = new RouteContext(httpContext) { diff --git a/src/Umbraco.Web.Website/Routing/IControllerActionSearcher.cs b/src/Umbraco.Web.Website/Routing/IControllerActionSearcher.cs index b272b4afd3..1b50638fff 100644 --- a/src/Umbraco.Web.Website/Routing/IControllerActionSearcher.cs +++ b/src/Umbraco.Web.Website/Routing/IControllerActionSearcher.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Controllers; @@ -5,6 +6,11 @@ namespace Umbraco.Cms.Web.Website.Routing { public interface IControllerActionSearcher { + ControllerActionDescriptor Find(HttpContext httpContext, string controller, string action); + + ControllerActionDescriptor Find(HttpContext httpContext, string controller, string action, string area) + => Find(httpContext, controller, action); + } } diff --git a/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs b/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs index 60384de752..bb00f958cf 100644 --- a/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs +++ b/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs @@ -240,7 +240,7 @@ namespace Umbraco.Cms.Web.Website.Routing [ActionToken] = postedInfo.ActionName }; - ControllerActionDescriptor surfaceControllerDescriptor = _controllerActionSearcher.Find(httpContext, postedInfo.ControllerName, postedInfo.ActionName); + ControllerActionDescriptor surfaceControllerDescriptor = _controllerActionSearcher.Find(httpContext, postedInfo.ControllerName, postedInfo.ActionName, postedInfo.Area); if (surfaceControllerDescriptor == null) { From c77bf2c27056c782ff8fccb0b6e81af825727dcc Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 20 Apr 2022 10:56:15 +0200 Subject: [PATCH 2/5] Added notification when requires user 2fa, so implementors can use this to send emails etc. --- .../UserTwoFactorRequestedNotification.cs | 14 ++++++ .../Services/UserServiceExtensions.cs | 6 +++ .../Security/BackOfficeSignInManager.cs | 50 ++++++++++++++++++- 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Core/Notifications/UserTwoFactorRequestedNotification.cs diff --git a/src/Umbraco.Core/Notifications/UserTwoFactorRequestedNotification.cs b/src/Umbraco.Core/Notifications/UserTwoFactorRequestedNotification.cs new file mode 100644 index 0000000000..ccb07c593c --- /dev/null +++ b/src/Umbraco.Core/Notifications/UserTwoFactorRequestedNotification.cs @@ -0,0 +1,14 @@ +using System; + +namespace Umbraco.Cms.Core.Notifications +{ + public class UserTwoFactorRequestedNotification : INotification + { + public UserTwoFactorRequestedNotification(Guid userKey) + { + UserKey = userKey; + } + + public Guid UserKey { get; } + } +} diff --git a/src/Umbraco.Core/Services/UserServiceExtensions.cs b/src/Umbraco.Core/Services/UserServiceExtensions.cs index 57d09077fc..19f1a7ac5c 100644 --- a/src/Umbraco.Core/Services/UserServiceExtensions.cs +++ b/src/Umbraco.Core/Services/UserServiceExtensions.cs @@ -84,5 +84,11 @@ namespace Umbraco.Extensions }); } + + public static IUser GetByKey(this IUserService userService, Guid key) + { + int id = BitConverter.ToInt32(key.ToByteArray(), 0); + return userService.GetUserById(id); + } } } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs index ec0d273b56..dc71c5f6bb 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs @@ -6,10 +6,14 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Cms.Web.Common.Security; using Umbraco.Extensions; @@ -24,6 +28,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security { private readonly BackOfficeUserManager _userManager; private readonly IBackOfficeExternalLoginProviders _externalLogins; + private readonly IEventAggregator _eventAggregator; private readonly GlobalSettings _globalSettings; protected override string AuthenticationType => Constants.Security.BackOfficeAuthenticationType; @@ -43,14 +48,32 @@ namespace Umbraco.Cms.Web.BackOffice.Security IOptions globalSettings, ILogger> logger, IAuthenticationSchemeProvider schemes, - IUserConfirmation confirmation) + IUserConfirmation confirmation, + IEventAggregator eventAggregator) : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation) { _userManager = userManager; _externalLogins = externalLogins; + _eventAggregator = eventAggregator; _globalSettings = globalSettings.Value; } + [Obsolete("Use ctor with all params")] + public BackOfficeSignInManager( + BackOfficeUserManager userManager, + IHttpContextAccessor contextAccessor, + IBackOfficeExternalLoginProviders externalLogins, + IUserClaimsPrincipalFactory claimsFactory, + IOptions optionsAccessor, + IOptions globalSettings, + ILogger> logger, + IAuthenticationSchemeProvider schemes, + IUserConfirmation confirmation) + : this(userManager, contextAccessor, externalLogins, claimsFactory, optionsAccessor, globalSettings, logger, schemes, confirmation, StaticServiceProvider.Instance.GetRequiredService()) + { + + } + /// /// Custom ExternalLoginSignInAsync overload for handling external sign in with auto-linking /// @@ -284,6 +307,31 @@ namespace Umbraco.Cms.Web.BackOffice.Security } } + protected override async Task SignInOrTwoFactorAsync(BackOfficeIdentityUser user, bool isPersistent, + string loginProvider = null, bool bypassTwoFactor = false) + { + var result = await base.SignInOrTwoFactorAsync(user, isPersistent, loginProvider, bypassTwoFactor); + + if (result.RequiresTwoFactor) + { + NotifyRequiresTwoFactor(user); + } + + return result; + } + + protected void NotifyRequiresTwoFactor(BackOfficeIdentityUser user) => Notify(user, + (currentUser) => new UserTwoFactorRequestedNotification(currentUser.Key) + ); + + private T Notify(BackOfficeIdentityUser currentUser, Func createNotification) where T : INotification + { + + var notification = createNotification(currentUser); + _eventAggregator.Publish(notification); + return notification; + } + private void LogFailedExternalLogin(ExternalLoginInfo loginInfo, BackOfficeIdentityUser user) => Logger.LogWarning("The AutoLinkOptions of the external authentication provider '{LoginProvider}' have refused the login based on the OnExternalLogin method. Affected user id: '{UserId}'", loginInfo.LoginProvider, user.Id); } From 64c451bf91db965c766b17de9c20c621c3224fdd Mon Sep 17 00:00:00 2001 From: Mole Date: Thu, 21 Apr 2022 13:51:24 +0200 Subject: [PATCH 3/5] Fix member properties always being sensitive (#12282) --- src/Umbraco.Core/Models/MemberType.cs | 2 +- .../Umbraco.Core/Models/ContentTypeTests.cs | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Models/MemberType.cs b/src/Umbraco.Core/Models/MemberType.cs index b55c598cac..30f7c1da65 100644 --- a/src/Umbraco.Core/Models/MemberType.cs +++ b/src/Umbraco.Core/Models/MemberType.cs @@ -161,7 +161,7 @@ namespace Umbraco.Cms.Core.Models } else { - var tuple = new MemberTypePropertyProfileAccess(false, false, true); + var tuple = new MemberTypePropertyProfileAccess(false, false, value); _memberTypePropertyTypes.Add(propertyTypeAlias, tuple); } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Models/ContentTypeTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Models/ContentTypeTests.cs index b939eb15a5..84d44133d2 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Models/ContentTypeTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Models/ContentTypeTests.cs @@ -310,6 +310,33 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Models Debug.Print(json); } + [Test] + [TestCase(false, false, false)] + [TestCase(true, false, false)] + [TestCase(true, true, false)] + [TestCase(true, true, true)] + public void Can_Set_Is_Member_Specific_Property_Type_Options(bool isSensitive, bool canView, bool canEdit) + { + var propertyTypeAlias = "testType"; + MemberType memberType = BuildMemberType(); + var propertyType = new PropertyTypeBuilder() + .WithAlias("testType") + .Build(); + + memberType.AddPropertyType(propertyType); + + memberType.SetIsSensitiveProperty(propertyTypeAlias, isSensitive); + memberType.SetMemberCanViewProperty(propertyTypeAlias, canView); + memberType.SetMemberCanEditProperty(propertyTypeAlias, canEdit); + + Assert.Multiple(() => + { + Assert.AreEqual(isSensitive, memberType.IsSensitiveProperty(propertyTypeAlias)); + Assert.AreEqual(canView, memberType.MemberCanViewProperty(propertyTypeAlias)); + Assert.AreEqual(canEdit, memberType.MemberCanEditProperty(propertyTypeAlias)); + }); + } + private static MemberType BuildMemberType() { var builder = new MemberTypeBuilder(); From fd8750f99a09c859a7df5bedd6f21a61bf66f973 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Fri, 22 Apr 2022 09:26:12 +0200 Subject: [PATCH 4/5] Try fixing flaky language test (#12281) --- .../cypress/integration/Languages/languages.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts index 33d5de24cb..ceb747a7bc 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts @@ -13,7 +13,8 @@ context('Languages', () => { cy.umbracoEnsureLanguageCultureNotExists(culture); cy.umbracoSection('settings'); - cy.get('.umb-tree-root-link').contains('Settings') + cy.get('.umb-box-content').should('be.visible'); + cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); // Enter language tree and create new language cy.umbracoTreeItem('settings', ['Languages']).click(); cy.umbracoButtonByLabelKey('languages_addLanguage').click(); From cd00bcb1e1e56991880d4f12ac3377a5154453d2 Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Fri, 22 Apr 2022 11:06:14 +0200 Subject: [PATCH 5/5] Filter out providers with no setup view --- .../Controllers/TwoFactorLoginController.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/TwoFactorLoginController.cs b/src/Umbraco.Web.BackOffice/Controllers/TwoFactorLoginController.cs index 41c25e3b47..cd4f6583f4 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TwoFactorLoginController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TwoFactorLoginController.cs @@ -72,7 +72,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var enabledProviderNameHashSet = new HashSet(await _twoFactorLoginService.GetEnabledTwoFactorProviderNamesAsync(user.Key)); - var providerNames = await _backOfficeUserManager.GetValidTwoFactorProvidersAsync(user); + IEnumerable providerNames = await _backOfficeUserManager.GetValidTwoFactorProvidersAsync(user); + + // Filter out any providers that does not have a view attached to it, since it's unusable then. + providerNames = providerNames.Where(providerName => + { + TwoFactorLoginViewOptions options = _twoFactorLoginViewOptions.Get(providerName); + return options is not null && !string.IsNullOrWhiteSpace(options.SetupViewPath); + }); return providerNames.Select(providerName => new UserTwoFactorProviderModel(providerName, enabledProviderNameHashSet.Contains(providerName))).ToArray();