From abc3273af731a3658f9b0041898b1d613322f57e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:30:15 +0200 Subject: [PATCH 1/5] convert the 'configure two-factor' button into a currentUserAction and use a condition --- .../configure-mfa-providers-action.ts | 18 ++++++++++++++ .../user/current-user/mfa-login/manifests.ts | 24 ++++++++++++------- ...mfa-providers-current-user-app.element.ts} | 21 +++++++++------- 3 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/configure-mfa-providers-action.ts rename src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/{mfa-providers-user-profile-app.element.ts => mfa-providers-current-user-app.element.ts} (67%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/configure-mfa-providers-action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/configure-mfa-providers-action.ts new file mode 100644 index 0000000000..bc644ec9bd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/configure-mfa-providers-action.ts @@ -0,0 +1,18 @@ +import { UMB_CURRENT_USER_MFA_MODAL } from '../modals/current-user-mfa/current-user-mfa-modal.token.js'; +import { UmbActionBase } from '@umbraco-cms/backoffice/action'; +import type { UmbCurrentUserAction, UmbCurrentUserActionArgs } from '@umbraco-cms/backoffice/extension-registry'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; + +export class UmbConfigureMfaProvidersApi + extends UmbActionBase> + implements UmbCurrentUserAction +{ + async getHref() { + return undefined; + } + + async execute() { + const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + await modalManagerContext.open(this, UMB_CURRENT_USER_MFA_MODAL).onSubmit(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/manifests.ts index 1e922de8a2..e420f02f9e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/manifests.ts @@ -1,16 +1,24 @@ -import type { ManifestUserProfileApp } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbConfigureMfaProvidersApi } from './configure-mfa-providers-action.js'; +import type { ManifestCurrentUserActionDefaultKind } from '@umbraco-cms/backoffice/extension-registry'; -export const userProfileApps: Array = [ +export const userProfileApps: Array = [ { - type: 'userProfileApp', - alias: 'Umb.UserProfileApp.CurrentUser.MfaLoginProviders', - name: 'MFA Login Providers User Profile App', - element: () => import('./mfa-providers-user-profile-app.element.js'), + type: 'currentUserAction', + kind: 'default', + alias: 'Umb.CurrentUser.App.MfaLoginProviders', + name: 'MFA Login Providers Current User App', weight: 800, + api: UmbConfigureMfaProvidersApi, meta: { - label: 'Two-Factor Authentication', - pathname: 'mfa-providers', + label: '#user_configureTwoFactor', + icon: 'lock', + look: 'secondary', }, + conditions: [ + { + alias: 'Umb.Condition.User.AllowMfaAction', + }, + ], }, ]; export const manifests = [...userProfileApps]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/mfa-providers-user-profile-app.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/mfa-providers-current-user-app.element.ts similarity index 67% rename from src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/mfa-providers-user-profile-app.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/mfa-providers-current-user-app.element.ts index 806f63ff9a..f48ab2e4ed 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/mfa-providers-user-profile-app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/mfa-providers-current-user-app.element.ts @@ -6,8 +6,8 @@ import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; -@customElement('umb-mfa-providers-user-profile-app') -export class UmbMfaProvidersUserProfileAppElement extends UmbLitElement { +@customElement('umb-mfa-providers-current-user-app') +export class UmbMfaProvidersCurrentUserAppElement extends UmbLitElement { @state() _hasProviders = false; @@ -26,11 +26,14 @@ export class UmbMfaProvidersUserProfileAppElement extends UmbLitElement { } return html` - - - Configure Two Factor - - + + + Configure Two Factor + `; } @@ -42,10 +45,10 @@ export class UmbMfaProvidersUserProfileAppElement extends UmbLitElement { static styles = [UmbTextStyles]; } -export default UmbMfaProvidersUserProfileAppElement; +export default UmbMfaProvidersCurrentUserAppElement; declare global { interface HTMLElementTagNameMap { - 'umb-mfa-providers-user-profile-app': UmbMfaProvidersUserProfileAppElement; + 'umb-mfa-providers-current-user-app': UmbMfaProvidersCurrentUserAppElement; } } From aace91f08ad1d12a705c463efbee0812d090a629 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:31:01 +0200 Subject: [PATCH 2/5] convert the 'edit' and 'change password' buttons into currentUserAction's --- .../user/current-user/current-user.context.ts | 1 + .../change-password-current-user.action.ts | 43 ++++++++++++++++++ .../profile/edit-current-user.action.ts | 38 ++++++++++++++++ .../user/current-user/profile/manifests.ts | 45 +++++++++++++++++-- 4 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/change-password-current-user.action.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/edit-current-user.action.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts index 5de00944d5..2faa82b6ff 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts @@ -12,6 +12,7 @@ export class UmbCurrentUserContext extends UmbContextBase #currentUser = new UmbObjectState(undefined); readonly currentUser = this.#currentUser.asObservable(); + readonly unique = this.#currentUser.asObservablePart((user) => user?.unique); readonly languageIsoCode = this.#currentUser.asObservablePart((user) => user?.languageIsoCode); #authContext?: typeof UMB_AUTH_CONTEXT.TYPE; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/change-password-current-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/change-password-current-user.action.ts new file mode 100644 index 0000000000..1b24d3e922 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/change-password-current-user.action.ts @@ -0,0 +1,43 @@ +import { UMB_CURRENT_USER_CONTEXT } from '../current-user.context.js'; +import { UmbActionBase } from '@umbraco-cms/backoffice/action'; +import type { UmbCurrentUserAction, UmbCurrentUserActionArgs } from '@umbraco-cms/backoffice/extension-registry'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UMB_CHANGE_PASSWORD_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; + +export class UmbChangePasswordCurrentUserAction + extends UmbActionBase> + implements UmbCurrentUserAction +{ + #unique?: string; + + constructor(host: UmbControllerHost, args: UmbCurrentUserActionArgs) { + super(host, args); + + this.consumeContext(UMB_CURRENT_USER_CONTEXT, (context) => { + this.observe( + context.unique, + (unique) => { + this.#unique = unique; + }, + 'umbEditCurrentUserActionObserver', + ); + }); + } + + async getHref() { + return undefined; + } + + async execute() { + if (!this.#unique) return; + + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + modalManager.open(this, UMB_CHANGE_PASSWORD_MODAL, { + data: { + user: { + unique: this.#unique, + }, + }, + }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/edit-current-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/edit-current-user.action.ts new file mode 100644 index 0000000000..ad02bc5285 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/edit-current-user.action.ts @@ -0,0 +1,38 @@ +import { UMB_CURRENT_USER_CONTEXT } from '../current-user.context.js'; +import { UmbActionBase } from '@umbraco-cms/backoffice/action'; +import type { UmbCurrentUserAction, UmbCurrentUserActionArgs } from '@umbraco-cms/backoffice/extension-registry'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export class UmbEditCurrentUserAction + extends UmbActionBase> + implements UmbCurrentUserAction +{ + #init; + #unique?: string; + + constructor(host: UmbControllerHost, args: UmbCurrentUserActionArgs) { + super(host, args); + + this.#init = new Promise((res) => { + this.consumeContext(UMB_CURRENT_USER_CONTEXT, (context) => { + this.observe( + context.unique, + (unique) => { + this.#unique = unique; + res(); + }, + 'umbEditCurrentUserActionObserver', + ); + }); + }); + } + + async getHref() { + await this.#init; + return `section/user-management/view/users/user/edit/${this.#unique}`; + } + + async execute() { + return; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/manifests.ts index 632490be5f..70cc917a96 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/manifests.ts @@ -1,6 +1,11 @@ -import type { ManifestUserProfileApp } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbChangePasswordCurrentUserAction } from './change-password-current-user.action.js'; +import { UmbEditCurrentUserAction } from './edit-current-user.action.js'; +import type { + ManifestCurrentUserActionDefaultKind, + ManifestUserProfileApp, +} from '@umbraco-cms/backoffice/extension-registry'; -export const userProfileApps: Array = [ +const userProfileApps: Array = [ { type: 'userProfileApp', alias: 'Umb.UserProfileApp.CurrentUser.Profile', @@ -13,4 +18,38 @@ export const userProfileApps: Array = [ }, }, ]; -export const manifests = [...userProfileApps]; + +const currentUserActions: Array = [ + { + type: 'currentUserAction', + kind: 'default', + alias: 'Umb.CurrentUser.Button.Edit', + name: 'Current User Edit Button', + weight: 1000, + api: UmbEditCurrentUserAction, + meta: { + label: '#general_edit', + icon: 'edit', + }, + conditions: [ + { + alias: 'Umb.Condition.SectionUserPermission', + match: 'Umb.Section.Users', + }, + ], + }, + { + type: 'currentUserAction', + kind: 'default', + alias: 'Umb.CurrentUser.Button.ChangePassword', + name: 'Current User Change Password Button', + weight: 900, + api: UmbChangePasswordCurrentUserAction, + meta: { + label: '#general_changePassword', + icon: 'lock', + }, + }, +]; + +export const manifests = [...userProfileApps, ...currentUserActions]; From eb19b0b9afe60b1b7274ce52e0e9dbe33ae0232f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:32:48 +0200 Subject: [PATCH 3/5] remove the hardcoded 'Edit' and 'Change password' buttons --- ...t-user-profile-user-profile-app.element.ts | 55 +------------------ 1 file changed, 1 insertion(+), 54 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/current-user-profile-user-profile-app.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/current-user-profile-user-profile-app.element.ts index 5cc0d8e8f4..cc257c97df 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/current-user-profile-user-profile-app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/current-user-profile-user-profile-app.element.ts @@ -1,65 +1,12 @@ -import { html, customElement, state, css } from '@umbraco-cms/backoffice/external/lit'; +import { html, customElement, css } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_CHANGE_PASSWORD_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; -import { UMB_CURRENT_USER_CONTEXT, type UmbCurrentUserModel } from '@umbraco-cms/backoffice/current-user'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; @customElement('umb-current-user-profile-user-profile-app') export class UmbCurrentUserProfileUserProfileAppElement extends UmbLitElement { - @state() - private _currentUser?: UmbCurrentUserModel; - - #currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE; - - constructor() { - super(); - - this.consumeContext(UMB_CURRENT_USER_CONTEXT, (instance) => { - this.#currentUserContext = instance; - this._observeCurrentUser(); - }); - } - - private async _observeCurrentUser() { - if (!this.#currentUserContext) return; - - this.observe( - this.#currentUserContext.currentUser, - (currentUser) => { - this._currentUser = currentUser; - }, - 'umbCurrentUserObserver', - ); - } - - private _edit() { - if (!this._currentUser) return; - - history.pushState(null, '', 'section/user-management/view/users/user/edit/' + this._currentUser.unique); //TODO Change to a tag with href and make dynamic - //TODO Implement modal routing for the current-user-modal, so that the modal closes when navigating to the edit profile page - } - async #changePassword() { - if (!this._currentUser) throw new Error('Current User not found'); - - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - modalManager.open(this, UMB_CHANGE_PASSWORD_MODAL, { - data: { - user: { - unique: this._currentUser.unique, - }, - }, - }); - } - render() { return html` - - ${this.localize.term('general_edit')} - - - ${this.localize.term('general_changePassword')} - `; From b5e86c7c523aebdf30ba8ead8eb51c0ba19162ac Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:35:54 +0200 Subject: [PATCH 4/5] remove 'external login' until it can be properly implemented --- ...ogin-providers-user-profile-app.element.ts | 25 ------------------- .../current-user/external-login/manifests.ts | 16 ------------ .../packages/user/current-user/manifests.ts | 2 -- 3 files changed, 43 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/external-login-providers-user-profile-app.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/external-login-providers-user-profile-app.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/external-login-providers-user-profile-app.element.ts deleted file mode 100644 index 87e434b2f2..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/external-login-providers-user-profile-app.element.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { html, customElement } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; - -@customElement('umb-external-login-providers-user-profile-app') -export class UmbExternalLoginProvidersUserProfileAppElement extends UmbLitElement { - render() { - return html` - - External login providers - - - `; - } - - static styles = [UmbTextStyles]; -} - -export default UmbExternalLoginProvidersUserProfileAppElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-external-login-providers-user-profile-app': UmbExternalLoginProvidersUserProfileAppElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/manifests.ts deleted file mode 100644 index 0d8cf09bbb..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/manifests.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { ManifestUserProfileApp } from '@umbraco-cms/backoffice/extension-registry'; - -export const userProfileApps: Array = [ - { - type: 'userProfileApp', - alias: 'Umb.UserProfileApp.CurrentUser.ExternalLoginProviders', - name: 'External Login Providers User Profile App', - element: () => import('./external-login-providers-user-profile-app.element.js'), - weight: 700, - meta: { - label: 'External Login Providers User Profile App', - pathname: 'externalLoginProviders', - }, - }, -]; -export const manifests = [...userProfileApps]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/manifests.ts index 65509321ec..e3212adbe1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/manifests.ts @@ -1,6 +1,5 @@ import { manifest as actionDefaultKindManifest } from './action/default.kind.js'; import { manifests as modalManifests } from './modals/manifests.js'; -import { manifests as externalLoginProviderManifests } from './external-login/manifests.js'; import { manifests as historyManifests } from './history/manifests.js'; import { manifests as mfaLoginProviderManifests } from './mfa-login/manifests.js'; import { manifests as profileManifests } from './profile/manifests.js'; @@ -31,7 +30,6 @@ export const headerApps: Array = [ export const manifests = [ actionDefaultKindManifest, - ...externalLoginProviderManifests, ...headerApps, ...historyManifests, ...mfaLoginProviderManifests, From a13d9dbf4b03dfad490b197d041d87b22a85c9dc Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 10 Apr 2024 18:37:06 +0200 Subject: [PATCH 5/5] use different icon for 2fa --- .../src/packages/user/current-user/mfa-login/manifests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/manifests.ts index e420f02f9e..4fec1b55f0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/manifests.ts @@ -11,7 +11,7 @@ export const userProfileApps: Array = [ api: UmbConfigureMfaProvidersApi, meta: { label: '#user_configureTwoFactor', - icon: 'lock', + icon: 'icon-rectangle-ellipsis', look: 'secondary', }, conditions: [