From 4f1d9997d5d17a81f7ee8473cd40606e92022394 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 28 Aug 2020 02:10:08 +1000 Subject: [PATCH] removes custom invite link, adds ability in logout event to specify an external logout get redirect for external logins. --- .../services/externallogininfo.service.js | 16 ------ .../src/common/services/user.service.js | 12 +++-- .../common/overlays/user/user.controller.js | 5 +- .../users/views/users/users.controller.js | 53 ++++++++----------- .../Editors/AuthenticationController.cs | 14 ++++- .../BackOfficeExternalLoginProviderOptions.cs | 5 -- .../Security/BackOfficeUserManager.cs | 6 ++- .../Security/IdentityAuditEventArgs.cs | 1 + .../Security/SignOutAuditEventArgs.cs | 19 +++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 10 files changed, 70 insertions(+), 62 deletions(-) create mode 100644 src/Umbraco.Web/Security/SignOutAuditEventArgs.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/services/externallogininfo.service.js b/src/Umbraco.Web.UI.Client/src/common/services/externallogininfo.service.js index d4a85e9593..90b559b9ea 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/externallogininfo.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/externallogininfo.service.js @@ -47,25 +47,9 @@ function externalLoginInfoService(externalLoginInfo, umbRequestHelper) { } } - /** - * If there is any external login providers with deny local login, check if any have a custom invite link and return it - * @param {any} provider optional to get the invite link directly from the provider, else will return the first one found (if any) - */ - function getUserInviteLink(provider) { - if (!provider) { - var denyLocalLoginProviders = _.filter(externalLoginInfo.providers, x => x.properties.UmbracoBackOfficeExternalLoginOptions.DenyLocalLogin); - var withInviteLink = _.filter(denyLocalLoginProviders, x => x.properties.UmbracoBackOfficeExternalLoginOptions.CustomUserInviteLink); - return withInviteLink.length > 0 ? withInviteLink[0].properties.UmbracoBackOfficeExternalLoginOptions.CustomUserInviteLink : null; - } - else { - return provider.properties.UmbracoBackOfficeExternalLoginOptions.CustomUserInviteLink; - } - } - return { hasDenyLocalLogin: hasDenyLocalLogin, getLoginProviders: getLoginProviders, - getUserInviteLink: getUserInviteLink, getExternalLoginProviderView: getExternalLoginProviderView }; } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js index de6fbaf782..413c7e81e5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js @@ -1,5 +1,5 @@ angular.module('umbraco.services') - .factory('userService', function ($rootScope, eventsService, $q, $location, requestRetryQueue, authResource, emailMarketingResource, $timeout, angularHelper) { + .factory('userService', function ($rootScope, eventsService, $q, $location, $window, requestRetryQueue, authResource, emailMarketingResource, $timeout, angularHelper) { var currentUser = null; var lastUserId = null; @@ -218,8 +218,14 @@ angular.module('umbraco.services') return authResource.performLogout() .then(function (data) { userAuthExpired(); - //done! - return null; + + if (data && data.signOutRedirectUrl) { + $window.location.replace(data.signOutRedirectUrl); + } + else { + //done! + return null; + } }); }, diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js index 91fc3babda..45c3f733c6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js @@ -80,10 +80,9 @@ angular.module("umbraco") //updateTimeout(); authResource.getCurrentUserLinkedLogins().then(function(logins) { + //reset all to be un-linked - for (var provider in $scope.externalLoginProviders) { - $scope.externalLoginProviders[provider].linkedProviderKey = undefined; - } + $scope.externalLoginProviders.forEach(provider => provider.linkedProviderKey = undefined); //set the linked logins for (var login in logins) { diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.controller.js index e991fc5342..e01d99c8b3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.controller.js @@ -71,47 +71,38 @@ vm.denyLocalLogin = externalLoginInfoService.hasDenyLocalLogin(); - // No default buttons with denyLocalLogin - // Don't show the invite button if no email is configured - if (!vm.denyLocalLogin) { - if (Umbraco.Sys.ServerVariables.umbracoSettings.showUserInvite) { - vm.defaultButton = { - labelKey: "user_inviteUser", - handler: function () { - vm.setUsersViewState('inviteUser'); - } - }; - vm.subButtons = [ - { - labelKey: "user_createUser", - handler: function () { - vm.setUsersViewState('createUser'); - } - } - ]; - } - else { - vm.defaultButton = { + // returns the object representing the user create button, returns null if deny local login is true + function getCreateUserButton() { + if (!vm.denyLocalLogin) { + return { labelKey: "user_createUser", handler: function () { vm.setUsersViewState('createUser'); } }; } + return null; } - else { - // if deny local login then check if there's an invite link in the config - var customInviteLink = externalLoginInfoService.getUserInviteLink(); - if (customInviteLink) { - vm.defaultButton = { - type: "link", - labelKey: "user_inviteUser", - href: customInviteLink, - hrefTarget: "_blank" - }; + // No default buttons with denyLocalLogin + // Don't show the invite button if no email is configured + if (Umbraco.Sys.ServerVariables.umbracoSettings.showUserInvite) { + vm.defaultButton = { + labelKey: "user_inviteUser", + handler: function () { + vm.setUsersViewState('inviteUser'); + } + }; + var createUserBtn = getCreateUserButton(); + if (createUserBtn) { + vm.subButtons = [createUserBtn]; } } + else { + vm.defaultButton = getCreateUserButton(); + } + + vm.toggleFilter = toggleFilter; vm.setUsersViewState = setUsersViewState; diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs index 020b30a3e7..17945897e0 100644 --- a/src/Umbraco.Web/Editors/AuthenticationController.cs +++ b/src/Umbraco.Web/Editors/AuthenticationController.cs @@ -210,7 +210,12 @@ namespace Umbraco.Web.Editors public async Task> GetCurrentUserLinkedLogins() { var identityUser = await UserManager.FindByIdAsync(UmbracoContext.Security.GetUserId().ResultOr(0)); - return identityUser.Logins.ToDictionary(x => x.LoginProvider, x => x.ProviderKey); + var result = new Dictionary(); + foreach (var l in identityUser.Logins) + { + result[l.LoginProvider] = l.ProviderKey; + } + return result; } /// @@ -483,7 +488,12 @@ namespace Umbraco.Web.Editors if (UserManager != null) { int.TryParse(User.Identity.GetUserId(), out var userId); - UserManager.RaiseLogoutSuccessEvent(userId); + var args = UserManager.RaiseLogoutSuccessEvent(userId); + if (!args.SignOutRedirectUrl.IsNullOrWhiteSpace()) + return Request.CreateResponse(new + { + signOutRedirectUrl = args.SignOutRedirectUrl + }); } return Request.CreateResponse(HttpStatusCode.OK); diff --git a/src/Umbraco.Web/Security/BackOfficeExternalLoginProviderOptions.cs b/src/Umbraco.Web/Security/BackOfficeExternalLoginProviderOptions.cs index c85f56f562..ecff31e2ca 100644 --- a/src/Umbraco.Web/Security/BackOfficeExternalLoginProviderOptions.cs +++ b/src/Umbraco.Web/Security/BackOfficeExternalLoginProviderOptions.cs @@ -34,11 +34,6 @@ namespace Umbraco.Web.Security /// public bool DenyLocalLogin { get; set; } - /// - /// If specified and is true then the user invite button in the back office will link through to this custom URL - /// - public string CustomUserInviteLink { get; set; } - /// /// When specified this will automatically redirect to the OAuth login provider instead of prompting the user to click on the OAuth button first. /// diff --git a/src/Umbraco.Web/Security/BackOfficeUserManager.cs b/src/Umbraco.Web/Security/BackOfficeUserManager.cs index 6205c1705c..7e5bfbb750 100644 --- a/src/Umbraco.Web/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Web/Security/BackOfficeUserManager.cs @@ -610,9 +610,11 @@ namespace Umbraco.Web.Security OnLoginSuccess(new IdentityAuditEventArgs(AuditEvent.LoginSucces, GetCurrentRequestIpAddress(), affectedUser: userId)); } - internal void RaiseLogoutSuccessEvent(int userId) + internal SignOutAuditEventArgs RaiseLogoutSuccessEvent(int userId) { - OnLogoutSuccess(new IdentityAuditEventArgs(AuditEvent.LogoutSuccess, GetCurrentRequestIpAddress(), affectedUser: userId)); + var args = new SignOutAuditEventArgs(AuditEvent.LogoutSuccess, GetCurrentRequestIpAddress(), affectedUser: userId); + OnLogoutSuccess(args); + return args; } internal void RaisePasswordChangedEvent(int userId) diff --git a/src/Umbraco.Web/Security/IdentityAuditEventArgs.cs b/src/Umbraco.Web/Security/IdentityAuditEventArgs.cs index 81407afe50..ae145b1893 100644 --- a/src/Umbraco.Web/Security/IdentityAuditEventArgs.cs +++ b/src/Umbraco.Web/Security/IdentityAuditEventArgs.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Security; namespace Umbraco.Web.Security { + /// /// This class is used by events raised from the BackofficeUserManager /// diff --git a/src/Umbraco.Web/Security/SignOutAuditEventArgs.cs b/src/Umbraco.Web/Security/SignOutAuditEventArgs.cs new file mode 100644 index 0000000000..e7943f70b6 --- /dev/null +++ b/src/Umbraco.Web/Security/SignOutAuditEventArgs.cs @@ -0,0 +1,19 @@ +namespace Umbraco.Web.Security +{ + /// + /// Event args used when signing out + /// + public class SignOutAuditEventArgs : IdentityAuditEventArgs + { + public SignOutAuditEventArgs(AuditEvent action, string ipAddress, string comment = null, int performingUser = -1, int affectedUser = -1) + : base(action, ipAddress, comment, performingUser, affectedUser) + { + } + + /// + /// Allows event handlers to set a GET absolute URL to be redirected to after successful logout out of the back office. This + /// can be used for external login providers. + /// + public string SignOutRedirectUrl { get; set; } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a410b9c83d..ef4f7a4a53 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -260,6 +260,7 @@ +