From 56c4ba09da552957d04e3100f504a310fe0d6bd4 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 12 Jun 2023 19:15:42 +0200 Subject: [PATCH 1/8] combine current-user and auth-flow into UmbAuthStore --- .../users/current-user/current-user.store.ts | 10 ---- .../src/shared/auth/auth-flow.ts | 7 ++- .../src/shared/auth/auth.interface.ts | 13 ++++++ .../src/shared/auth/auth.store.ts | 46 +++++++++++++++++++ .../src/shared/auth/index.ts | 3 ++ .../src/shared/auth/types.ts | 1 + 6 files changed, 66 insertions(+), 14 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user.store.ts create mode 100644 src/Umbraco.Web.UI.Client/src/shared/auth/auth.store.ts create mode 100644 src/Umbraco.Web.UI.Client/src/shared/auth/types.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user.store.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user.store.ts deleted file mode 100644 index d188a75ba4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user.store.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { UmbLoggedInUser } from './types.js'; -import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; - -export const UMB_CURRENT_USER_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbCurrentUserStore'); - -export class UmbCurrentUserStore { - #currentUser = new UmbObjectState(undefined); - public readonly currentUser = this.#currentUser.asObservable(); -} diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts index 1820234ff6..37e66d46e1 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -import type { IUmbAuth } from './auth.interface.js'; +import { BehaviorSubject } from '@umbraco-cms/backoffice/external/rxjs'; import { BaseTokenRequestHandler, BasicQueryStringUtils, @@ -81,7 +81,7 @@ class UmbNoHashQueryStringUtils extends BasicQueryStringUtils { * a. This will redirect the user to the authorization endpoint of the server * 4. After login, get the latest token before each request to the server by calling the `performWithFreshTokens` method */ -export class UmbAuthFlow implements IUmbAuth { +export class UmbAuthFlow { // handlers readonly #notifier: AuthorizationNotifier; readonly #authorizationHandler: RedirectRequestHandler; @@ -164,7 +164,6 @@ export class UmbAuthFlow implements IUmbAuth { if (response.isValid()) { this.#accessTokenResponse = response; this.#refreshToken = this.#accessTokenResponse.refreshToken; - return; } } @@ -225,7 +224,7 @@ export class UmbAuthFlow implements IUmbAuth { } /** - * This method will check if the user is logged in by validting the timestamp of the stored token. + * This method will check if the user is logged in by validating the timestamp of the stored token. * If no token is stored, it will return false. * * @returns true if the user is logged in, false otherwise. diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts index a71be30e2f..6a90a3dc0d 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts @@ -1,3 +1,6 @@ +import type { UmbLoggedInUser } from './types.js'; +import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; + export interface IUmbAuth { /** * Get the current user's access token. @@ -10,6 +13,16 @@ export interface IUmbAuth { */ performWithFreshTokens(): Promise; + /** + * Get the current user model of the current user. + */ + get currentUser(): Observable; + + /** + * Make a server request for the current user and save the state + */ + fetchCurrentUser(): Promise; + /** * Sign out the current user. */ diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.store.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.store.ts new file mode 100644 index 0000000000..a2cb3b8db6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.store.ts @@ -0,0 +1,46 @@ +import { IUmbAuth } from './auth.interface.js'; +import { UmbAuthFlow } from './auth-flow.js'; +import { UmbLoggedInUser } from './types.js'; +import { UserResource } from '@umbraco-cms/backoffice/backend-api'; +import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; +import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { ReplaySubject } from '@umbraco-cms/backoffice/external/rxjs'; + +export class UmbAuthStore implements IUmbAuth { + #currentUser = new UmbObjectState(undefined); + readonly currentUser = this.#currentUser.asObservable(); + readonly isLoggedIn = new ReplaySubject(1); + + #host; + #authFlow; + + constructor(host: UmbControllerHostElement, authFlow: UmbAuthFlow) { + this.#host = host; + this.#authFlow = authFlow; + + this.isLoggedIn.subscribe((isLoggedIn) => { + if (isLoggedIn) { + this.fetchCurrentUser(); + } + }); + } + + async fetchCurrentUser(): Promise { + const { data } = await tryExecuteAndNotify(this.#host, UserResource.getUserCurrent()); + + if (!data) return; + + this.#currentUser.next(data); + + return data; + } + + performWithFreshTokens(): Promise { + return this.#authFlow.performWithFreshTokens(); + } + + signOut(): Promise { + return this.#authFlow.signOut(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts index 391382b79c..86c563d9cb 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts @@ -3,6 +3,9 @@ import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export type { IUmbAuth } from './auth.interface.js'; export { UmbAuthFlow } from './auth-flow.js'; +export { UmbAuthStore } from './auth.store.js'; + +export * from './types.js'; export const UMB_AUTH = new UmbContextToken( 'UmbAuth', diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/types.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/types.ts new file mode 100644 index 0000000000..3b481a3c47 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/types.ts @@ -0,0 +1 @@ +export type { CurrentUserResponseModel as UmbLoggedInUser } from '@umbraco-cms/backoffice/backend-api'; From 5fe8aae635a84543d82393a55e90315645ffee44 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 12 Jun 2023 19:15:53 +0200 Subject: [PATCH 2/8] use new auth store to display user information --- .../src/apps/app/app.element.ts | 8 +++++-- .../current-user-header-app.element.ts | 13 +++++------ .../src/packages/users/current-user/index.ts | 2 -- .../current-user-modal.element.ts | 14 +++++------- .../src/packages/users/current-user/types.ts | 22 ------------------- .../user-profile-app-profile.element.ts | 13 +++++------ .../src/packages/users/index.ts | 2 -- .../user-workspace-editor.element.ts | 14 ++++++------ 8 files changed, 31 insertions(+), 57 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/users/current-user/types.ts diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts index 4e0e8a11fb..abde2ed878 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts @@ -1,5 +1,5 @@ import type { UmbAppErrorElement } from './app-error.element.js'; -import { UMB_AUTH, UmbAuthFlow } from '@umbraco-cms/backoffice/auth'; +import { UMB_AUTH, UmbAuthFlow, UmbAuthStore } from '@umbraco-cms/backoffice/auth'; import { UMB_APP, UmbAppContext } from '@umbraco-cms/backoffice/context'; import { css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { UUIIconRegistryEssential } from '@umbraco-cms/backoffice/external/uui'; @@ -81,7 +81,9 @@ export class UmbAppElement extends UmbLitElement { this.#authFlow = new UmbAuthFlow(this.serverUrl, redirectUrl); - this.provideContext(UMB_AUTH, this.#authFlow); + const authStore = new UmbAuthStore(this, this.#authFlow); + + this.provideContext(UMB_AUTH, authStore); this.provideContext(UMB_APP, new UmbAppContext({ backofficePath: this.backofficePath, serverUrl: this.serverUrl })); @@ -99,6 +101,8 @@ export class UmbAppElement extends UmbLitElement { OpenAPI.WITH_CREDENTIALS = true; } + authStore.isLoggedIn.next(true); + // Initialise the router this.#redirect(); } catch (error) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user-header-app.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user-header-app.element.ts index 44ed9f9c36..860e011f32 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user-header-app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user-header-app.element.ts @@ -1,5 +1,3 @@ -import type { UmbLoggedInUser } from './types.js'; -import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from './current-user.store.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, CSSResultGroup, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { @@ -8,13 +6,14 @@ import { UMB_CURRENT_USER_MODAL, } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; +import { UMB_AUTH, type UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; @customElement('umb-current-user-header-app') export class UmbCurrentUserHeaderAppElement extends UmbLitElement { @state() private _currentUser?: UmbLoggedInUser; - private _currentUserStore?: UmbCurrentUserStore; + private _auth?: typeof UMB_AUTH.TYPE; private _modalContext?: UmbModalManagerContext; constructor() { @@ -24,16 +23,16 @@ export class UmbCurrentUserHeaderAppElement extends UmbLitElement { this._modalContext = instance; }); - this.consumeContext(UMB_CURRENT_USER_STORE_CONTEXT_TOKEN, (instance) => { - this._currentUserStore = instance; + this.consumeContext(UMB_AUTH, (instance) => { + this._auth = instance; this._observeCurrentUser(); }); } private async _observeCurrentUser() { - if (!this._currentUserStore) return; + if (!this._auth) return; - this.observe(this._currentUserStore.currentUser, (currentUser) => { + this.observe(this._auth.currentUser, (currentUser) => { this._currentUser = currentUser; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/index.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/index.ts index 30f15ab9c5..0b68da794d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/index.ts @@ -1,4 +1,2 @@ -export * from './types.js'; // TODO:Do not export store, but instead export future repository -export * from './current-user.store.js'; export * from './current-user-history.store.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts index 83e31e00aa..266af7b9a8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts @@ -1,6 +1,4 @@ -import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from '../../current-user.store.js'; -import type { UmbLoggedInUser } from '../../types.js'; -import { UMB_AUTH } from '@umbraco-cms/backoffice/auth'; +import { UMB_AUTH, type UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; import { UMB_APP } from '@umbraco-cms/backoffice/context'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, CSSResultGroup, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; @@ -15,7 +13,7 @@ export class UmbCurrentUserModalElement extends UmbLitElement { @state() private _currentUser?: UmbLoggedInUser; - private _currentUserStore?: UmbCurrentUserStore; + private _auth?: typeof UMB_AUTH.TYPE; #auth?: typeof UMB_AUTH.TYPE; @@ -24,8 +22,8 @@ export class UmbCurrentUserModalElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_CURRENT_USER_STORE_CONTEXT_TOKEN, (instance) => { - this._currentUserStore = instance; + this.consumeContext(UMB_AUTH, (instance) => { + this._auth = instance; this._observeCurrentUser(); }); @@ -41,9 +39,9 @@ export class UmbCurrentUserModalElement extends UmbLitElement { } private async _observeCurrentUser() { - if (!this._currentUserStore) return; + if (!this._auth) return; - this.observe(this._currentUserStore.currentUser, (currentUser) => { + this.observe(this._auth.currentUser, (currentUser) => { this._currentUser = currentUser; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/types.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/types.ts deleted file mode 100644 index adfe453bf4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/types.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { UmbEntityBase } from '@umbraco-cms/backoffice/models'; - -export interface UserEntity extends UmbEntityBase { - type: 'user'; -} - -export type UserStatus = 'enabled' | 'inactive' | 'invited' | 'disabled'; - -export interface UmbLoggedInUser extends UserEntity { - email: string; - status: UserStatus; - language: string; - lastLoginDate?: string; - lastLockoutDate?: string; - lastPasswordChangeDate?: string; - updateDate: string; - createDate: string; - failedLoginAttempts: number; - userGroups: Array; - contentStartNodes: Array; - mediaStartNodes: Array; -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/user-profile-apps/user-profile-app-profile.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/user-profile-apps/user-profile-app-profile.element.ts index e449d86358..7bc4c986fc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/user-profile-apps/user-profile-app-profile.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/user-profile-apps/user-profile-app-profile.element.ts @@ -1,5 +1,3 @@ -import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from '../current-user.store.js'; -import type { UmbLoggedInUser } from '../types.js'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -8,6 +6,7 @@ import { UMB_CHANGE_PASSWORD_MODAL, UMB_MODAL_MANAGER_CONTEXT_TOKEN, } from '@umbraco-cms/backoffice/modal'; +import { UMB_AUTH, type UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; @customElement('umb-user-profile-app-profile') export class UmbUserProfileAppProfileElement extends UmbLitElement { @@ -15,7 +14,7 @@ export class UmbUserProfileAppProfileElement extends UmbLitElement { private _currentUser?: UmbLoggedInUser; private _modalContext?: UmbModalManagerContext; - private _currentUserStore?: UmbCurrentUserStore; + private _auth?: typeof UMB_AUTH.TYPE; constructor() { super(); @@ -24,8 +23,8 @@ export class UmbUserProfileAppProfileElement extends UmbLitElement { this._modalContext = instance; }); - this.consumeContext(UMB_CURRENT_USER_STORE_CONTEXT_TOKEN, (instance) => { - this._currentUserStore = instance; + this.consumeContext(UMB_AUTH, (instance) => { + this._auth = instance; this._observeCurrentUser(); }); @@ -33,9 +32,9 @@ export class UmbUserProfileAppProfileElement extends UmbLitElement { } private async _observeCurrentUser() { - if (!this._currentUserStore) return; + if (!this._auth) return; - this.observe(this._currentUserStore.currentUser, (currentUser) => { + this.observe(this._auth.currentUser, (currentUser) => { this._currentUser = currentUser; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/index.ts b/src/Umbraco.Web.UI.Client/src/packages/users/index.ts index 2fe1593f81..eb363b6357 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/index.ts @@ -3,7 +3,6 @@ import { manifests as userManifests } from './users/manifests.js'; import { manifests as userSectionManifests } from './user-section/manifests.js'; import { manifests as currentUserManifests } from './current-user/manifests.js'; -import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from './current-user/current-user.store.js'; import { UmbCurrentUserHistoryStore, UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN, @@ -21,7 +20,6 @@ export const manifests = [...userGroupManifests, ...userManifests, ...userSectio export const onInit: UmbEntryPointOnInit = (host, extensionRegistry) => { extensionRegistry.registerMany(manifests); - new UmbContextProviderController(host, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN, new UmbCurrentUserStore()); new UmbUserItemStore(host); new UmbUserGroupItemStore(host); new UmbContextProviderController( diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/user-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/user-workspace-editor.element.ts index 1ec1477af1..1fb55ddaac 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/user-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/user-workspace-editor.element.ts @@ -1,4 +1,3 @@ -import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from '../../current-user/current-user.store.js'; import { getLookAndColorFromUserStatus } from '../../utils.js'; import { UmbUserRepository } from '../repository/user.repository.js'; import { UmbUserGroupInputElement } from '../../user-groups/components/input-user-group/user-group-input.element.js'; @@ -24,16 +23,17 @@ import { UserResponseModel, UserStateModel } from '@umbraco-cms/backoffice/backe import { createExtensionClass } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; +import { UMB_AUTH, UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; @customElement('umb-user-workspace-editor') export class UmbUserWorkspaceEditorElement extends UmbLitElement { @state() - private _currentUser?: any; + private _currentUser?: UmbLoggedInUser; @state() private _user?: UserResponseModel; - #currentUserStore?: UmbCurrentUserStore; + #auth?: typeof UMB_AUTH.TYPE; #modalContext?: UmbModalManagerContext; #languages = []; //TODO Add languages #workspaceContext?: UmbUserWorkspaceContext; @@ -43,8 +43,8 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_CURRENT_USER_STORE_CONTEXT_TOKEN, (store) => { - this.#currentUserStore = store; + this.consumeContext(UMB_AUTH, (instance) => { + this.#auth = instance; this.#observeCurrentUser(); }); @@ -76,8 +76,8 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { } #observeCurrentUser() { - if (!this.#currentUserStore) return; - this.observe(this.#currentUserStore.currentUser, (currentUser) => (this._currentUser = currentUser)); + if (!this.#auth) return; + this.observe(this.#auth.currentUser, (currentUser) => (this._currentUser = currentUser)); } #onUserStatusChange() { From 186e884d92b420cec09406b096ac084453a6a71a Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 12 Jun 2023 19:17:12 +0200 Subject: [PATCH 3/8] cleanup imports --- src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts index 37e66d46e1..251bdea185 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ -import { BehaviorSubject } from '@umbraco-cms/backoffice/external/rxjs'; import { BaseTokenRequestHandler, BasicQueryStringUtils, From b72d2e961e72ad0eb40c6d810e705cab178de499 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 12 Jun 2023 19:27:28 +0200 Subject: [PATCH 4/8] update news welcome dashboard to remove POC gibberish and test out the auth store --- .../umbraco-news-dashboard.element.ts | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts index 9596b05cf3..24932d17fd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts @@ -1,12 +1,40 @@ -import { css, html, LitElement, customElement } from '@umbraco-cms/backoffice/external/lit'; +import { UMB_AUTH } from '@umbraco-cms/backoffice/auth'; +import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-umbraco-news-dashboard') -export class UmbUmbracoNewsDashboardElement extends LitElement { +export class UmbUmbracoNewsDashboardElement extends UmbLitElement { + #auth?: typeof UMB_AUTH.TYPE; + + @state() + private name = ''; + + constructor() { + super(); + this.consumeContext(UMB_AUTH, (instance) => { + this.#auth = instance; + this.#observeCurrentUser(); + }); + } + + #observeCurrentUser(): void { + if (!this.#auth) return; + this.observe(this.#auth?.currentUser, (user) => { + this.name = user?.name ?? ''; + }); + } + render() { return html` -

Welcome

-

You can find details about the POC in the readme.md file.

+

Welcome, ${this.name}

+

This is a preview version of Umbraco, where you can have a first-hand look at the new Backoffice.

+

There is currently very limited functionality.

+

+ Please refer to the + documentation to learn more about + what is possible. +

`; } From d7a56a14733335388d7bb26bf5b1326d0ea96571 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 12 Jun 2023 19:28:18 +0200 Subject: [PATCH 5/8] remove unneeded undefined check --- .../src/packages/umbraco-news/umbraco-news-dashboard.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts index 24932d17fd..ae5609b1fb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts @@ -19,7 +19,7 @@ export class UmbUmbracoNewsDashboardElement extends UmbLitElement { #observeCurrentUser(): void { if (!this.#auth) return; - this.observe(this.#auth?.currentUser, (user) => { + this.observe(this.#auth.currentUser, (user) => { this.name = user?.name ?? ''; }); } From 90461d94b1d99170a44acf30b5efa23086ec24c2 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 13 Jun 2023 10:59:55 +0200 Subject: [PATCH 6/8] remove duplicate symbol --- .../modals/current-user/current-user-modal.element.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts index 266af7b9a8..eb2012cd16 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts @@ -13,8 +13,6 @@ export class UmbCurrentUserModalElement extends UmbLitElement { @state() private _currentUser?: UmbLoggedInUser; - private _auth?: typeof UMB_AUTH.TYPE; - #auth?: typeof UMB_AUTH.TYPE; #appContext?: typeof UMB_APP.TYPE; @@ -23,7 +21,7 @@ export class UmbCurrentUserModalElement extends UmbLitElement { super(); this.consumeContext(UMB_AUTH, (instance) => { - this._auth = instance; + this.#auth = instance; this._observeCurrentUser(); }); @@ -39,9 +37,9 @@ export class UmbCurrentUserModalElement extends UmbLitElement { } private async _observeCurrentUser() { - if (!this._auth) return; + if (!this.#auth) return; - this.observe(this._auth.currentUser, (currentUser) => { + this.observe(this.#auth.currentUser, (currentUser) => { this._currentUser = currentUser; }); } From 8256d7827dde2c6cd1300b59d1f606d42df30756 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 13 Jun 2023 11:20:20 +0200 Subject: [PATCH 7/8] implement mock endpoints for current user --- .../src/mocks/data/users.data.ts | 26 ++++++- .../src/mocks/handlers/user.handlers.ts | 68 +++++-------------- 2 files changed, 40 insertions(+), 54 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/users.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/users.data.ts index b55f498667..beda25493a 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/users.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/users.data.ts @@ -1,3 +1,4 @@ +import { UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; import { UmbData } from './data.js'; import { PagedUserResponseModel, UserResponseModel, UserStateModel } from '@umbraco-cms/backoffice/backend-api'; @@ -18,6 +19,25 @@ class UmbUsersData extends UmbData { return this.data.find((user) => user.id === id); } + getCurrentUser(): UmbLoggedInUser { + const firstUser = this.data[0]; + + return { + $type: 'CurrentUserResponseModel', + id: firstUser.id, + name: firstUser.name, + email: firstUser.email, + userName: firstUser.email, + avatarUrls: [], + hasAccessToAllLanguages: true, + languageIsoCode: firstUser.languageIsoCode, + languages: [], + contentStartNodeIds: firstUser.contentStartNodeIds, + mediaStartNodeIds: firstUser.mediaStartNodeIds, + permissions: [], + }; + } + save(id: string, saveItem: UserResponseModel) { const foundIndex = this.data.findIndex((item) => item.id === id); if (foundIndex !== -1) { @@ -96,9 +116,9 @@ export const data: Array = [ $type: 'UserResponseModel', contentStartNodeIds: [], mediaStartNodeIds: [], - name: 'Erny Baptista', - email: 'ebaptista1@csmonitor.com', - languageIsoCode: 'Kannada', + name: 'Umbraco User', + email: 'noreply@umbraco.com', + languageIsoCode: 'en-US', state: UserStateModel.ACTIVE, lastLoginDate: '9/10/2022', lastLockoutDate: '11/23/2021', diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/user.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/user.handlers.ts index 3d9ceee1b0..9da37812be 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/user.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/user.handlers.ts @@ -3,18 +3,31 @@ const { rest } = window.MockServiceWorker; import { umbUsersData } from '../data/users.data.js'; import { umbracoPath } from '@umbraco-cms/backoffice/utils'; -let isAuthenticated = true; const slug = '/user'; export const handlers = [ - rest.get(umbracoPath(`${slug}`), (req, res, ctx) => { + rest.get(umbracoPath(`${slug}/filter`), (req, res, ctx) => { + //TODO: Implementer filter const response = umbUsersData.getAll(); return res(ctx.status(200), ctx.json(response)); }), - rest.get(umbracoPath(`${slug}/filter`), (req, res, ctx) => { - //TODO: Implementer filter + rest.get(umbracoPath(`${slug}/current`), (_req, res, ctx) => { + const loggedInUser = umbUsersData.getCurrentUser(); + return res(ctx.status(200), ctx.json(loggedInUser)); + }), + + rest.get(umbracoPath(`${slug}/sections`), (_req, res, ctx) => { + return res( + ctx.status(200), + ctx.json({ + sections: ['Umb.Section.Content', 'Umb.Section.Media', 'Umb.Section.Settings', 'My.Section.Custom'], + }) + ); + }), + + rest.get(umbracoPath(`${slug}`), (req, res, ctx) => { const response = umbUsersData.getAll(); return res(ctx.status(200), ctx.json(response)); @@ -40,51 +53,4 @@ export const handlers = [ return res(ctx.status(200), ctx.json(saved)); }), - rest.post(umbracoPath('/user/login'), (_req, res, ctx) => { - // Persist user's authentication in the session - isAuthenticated = true; - return res( - // Respond with a 200 status code - ctx.status(201) - ); - }), - - rest.post(umbracoPath('/user/logout'), (_req, res, ctx) => { - // Persist user's authentication in the session - isAuthenticated = false; - return res( - // Respond with a 200 status code - ctx.status(201) - ); - }), - - rest.get(umbracoPath('/user'), (_req, res, ctx) => { - // Check if the user is authenticated in this session - if (!isAuthenticated) { - // If not authenticated, respond with a 403 error - return res( - ctx.status(403), - ctx.json({ - errorMessage: 'Not authorized', - }) - ); - } - // If authenticated, return a mocked user details - return res( - ctx.status(200), - ctx.json({ - username: 'admin', - role: 'administrator', - }) - ); - }), - - rest.get(umbracoPath('/user/sections'), (_req, res, ctx) => { - return res( - ctx.status(200), - ctx.json({ - sections: ['Umb.Section.Content', 'Umb.Section.Media', 'Umb.Section.Settings', 'My.Section.Custom'], - }) - ); - }), ]; From 5f31529ba2720200f41f012658f73886557d296d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Jun 2023 11:43:26 +0200 Subject: [PATCH 8/8] rename auth store to context --- src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts | 8 ++++---- .../shared/auth/{auth.store.ts => auth.context.ts} | 2 +- .../src/shared/auth/auth.token.ts | 7 +++++++ src/Umbraco.Web.UI.Client/src/shared/auth/index.ts | 11 ++--------- 4 files changed, 14 insertions(+), 14 deletions(-) rename src/Umbraco.Web.UI.Client/src/shared/auth/{auth.store.ts => auth.context.ts} (96%) create mode 100644 src/Umbraco.Web.UI.Client/src/shared/auth/auth.token.ts diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts index abde2ed878..b07b5e9b5f 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts @@ -1,5 +1,5 @@ import type { UmbAppErrorElement } from './app-error.element.js'; -import { UMB_AUTH, UmbAuthFlow, UmbAuthStore } from '@umbraco-cms/backoffice/auth'; +import { UMB_AUTH, UmbAuthFlow, UmbAuthContext } from '@umbraco-cms/backoffice/auth'; import { UMB_APP, UmbAppContext } from '@umbraco-cms/backoffice/context'; import { css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { UUIIconRegistryEssential } from '@umbraco-cms/backoffice/external/uui'; @@ -81,9 +81,9 @@ export class UmbAppElement extends UmbLitElement { this.#authFlow = new UmbAuthFlow(this.serverUrl, redirectUrl); - const authStore = new UmbAuthStore(this, this.#authFlow); + const authContext = new UmbAuthContext(this, this.#authFlow); - this.provideContext(UMB_AUTH, authStore); + this.provideContext(UMB_AUTH, authContext); this.provideContext(UMB_APP, new UmbAppContext({ backofficePath: this.backofficePath, serverUrl: this.serverUrl })); @@ -101,7 +101,7 @@ export class UmbAppElement extends UmbLitElement { OpenAPI.WITH_CREDENTIALS = true; } - authStore.isLoggedIn.next(true); + authContext.isLoggedIn.next(true); // Initialise the router this.#redirect(); diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.store.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts similarity index 96% rename from src/Umbraco.Web.UI.Client/src/shared/auth/auth.store.ts rename to src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts index a2cb3b8db6..ce31ca56cc 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.store.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts @@ -7,7 +7,7 @@ import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; import { ReplaySubject } from '@umbraco-cms/backoffice/external/rxjs'; -export class UmbAuthStore implements IUmbAuth { +export class UmbAuthContext implements IUmbAuth { #currentUser = new UmbObjectState(undefined); readonly currentUser = this.#currentUser.asObservable(); readonly isLoggedIn = new ReplaySubject(1); diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.token.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.token.ts new file mode 100644 index 0000000000..36a5862575 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.token.ts @@ -0,0 +1,7 @@ +import { IUmbAuth } from './auth.interface.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_AUTH = new UmbContextToken( + 'UmbAuth', + 'An instance of UmbAuthFlow that should be shared across the app.' +); diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts index 86c563d9cb..e32f1d152b 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts @@ -1,13 +1,6 @@ -import { IUmbAuth } from './auth.interface.js'; -import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; - export type { IUmbAuth } from './auth.interface.js'; export { UmbAuthFlow } from './auth-flow.js'; -export { UmbAuthStore } from './auth.store.js'; +export { UmbAuthContext } from './auth.context.js'; export * from './types.js'; - -export const UMB_AUTH = new UmbContextToken( - 'UmbAuth', - 'An instance of UmbAuthFlow that should be shared across the app.' -); +export * from './auth.token.js';