Merge pull request #1689 from umbraco/feature/mfa-small-design-update
Chore: Clean up UI around two-factor
This commit is contained in:
@@ -3,151 +3,164 @@ const { rest } = window.MockServiceWorker;
|
||||
import type { PackageManifestResponse } from '../../packages/packages/types.js';
|
||||
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
const privateManifests: PackageManifestResponse = [
|
||||
{
|
||||
name: 'My Package Name',
|
||||
version: '1.0.0',
|
||||
extensions: [
|
||||
{
|
||||
type: 'bundle',
|
||||
alias: 'My.Package.Bundle',
|
||||
name: 'My Package Bundle',
|
||||
js: '/App_Plugins/custom-bundle-package/index.js',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Named Package',
|
||||
version: '1.0.0',
|
||||
extensions: [
|
||||
{
|
||||
type: 'section',
|
||||
alias: 'My.Section.Custom',
|
||||
name: 'Custom Section',
|
||||
js: '/App_Plugins/section.js',
|
||||
elementName: 'my-section-custom',
|
||||
weight: 1,
|
||||
meta: {
|
||||
label: 'Custom',
|
||||
pathname: 'my-custom',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'propertyEditorUi',
|
||||
alias: 'My.PropertyEditorUI.Custom',
|
||||
name: 'My Custom Property Editor UI',
|
||||
js: '/App_Plugins/property-editor.js',
|
||||
elementName: 'my-property-editor-ui-custom',
|
||||
meta: {
|
||||
label: 'My Custom Property',
|
||||
icon: 'document',
|
||||
group: 'Common',
|
||||
propertyEditorSchema: 'Umbraco.TextBox',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Package with an entry point',
|
||||
extensions: [
|
||||
{
|
||||
type: 'backofficeEntryPoint',
|
||||
name: 'My Custom Entry Point',
|
||||
alias: 'My.Entrypoint.Custom',
|
||||
js: '/App_Plugins/custom-entrypoint.js',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'My MFA Package',
|
||||
extensions: [
|
||||
{
|
||||
type: 'mfaLoginProvider',
|
||||
alias: 'My.MfaLoginProvider.Custom.Google',
|
||||
name: 'My Custom Google MFA Provider',
|
||||
forProviderName: 'Google Authenticator',
|
||||
},
|
||||
{
|
||||
type: 'mfaLoginProvider',
|
||||
alias: 'My.MfaLoginProvider.Custom.SMS',
|
||||
name: 'My Custom SMS MFA Provider',
|
||||
forProviderName: 'sms',
|
||||
meta: {
|
||||
label: 'Setup SMS Verification',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Package with a view',
|
||||
extensions: [
|
||||
{
|
||||
type: 'packageView',
|
||||
alias: 'My.PackageView.Custom',
|
||||
name: 'My Custom Package View',
|
||||
js: '/App_Plugins/package-view.js',
|
||||
meta: {
|
||||
packageName: 'Package with a view',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'My MFA Package',
|
||||
extensions: [
|
||||
{
|
||||
type: 'mfaLoginProvider',
|
||||
alias: 'My.MfaLoginProvider.Custom',
|
||||
name: 'My Custom MFA Provider',
|
||||
forProviderName: 'sms',
|
||||
meta: {
|
||||
label: 'Setup SMS Verification',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const publicManifests: PackageManifestResponse = [
|
||||
{
|
||||
name: 'My Auth Package',
|
||||
extensions: [
|
||||
{
|
||||
type: 'authProvider',
|
||||
alias: 'My.AuthProvider.Google',
|
||||
name: 'My Custom Auth Provider',
|
||||
forProviderName: 'Umbraco.Google',
|
||||
meta: {
|
||||
label: 'Google',
|
||||
defaultView: {
|
||||
icon: 'icon-google',
|
||||
},
|
||||
linking: {
|
||||
allowManualLinking: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'authProvider',
|
||||
alias: 'My.AuthProvider.Github',
|
||||
name: 'My Github Auth Provider',
|
||||
forProviderName: 'Umbraco.Github',
|
||||
meta: {
|
||||
label: 'GitHub',
|
||||
defaultView: {
|
||||
look: 'primary',
|
||||
icon: 'icon-github',
|
||||
color: 'success',
|
||||
},
|
||||
linking: {
|
||||
allowManualLinking: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const manifestDevelopmentHandlers = [
|
||||
rest.get(umbracoPath('/manifest/manifest/private'), (_req, res, ctx) => {
|
||||
return res(
|
||||
// Respond with a 200 status code
|
||||
ctx.status(200),
|
||||
ctx.json<PackageManifestResponse>([
|
||||
{
|
||||
name: 'My Package Name',
|
||||
version: '1.0.0',
|
||||
extensions: [
|
||||
{
|
||||
type: 'bundle',
|
||||
alias: 'My.Package.Bundle',
|
||||
name: 'My Package Bundle',
|
||||
js: '/App_Plugins/custom-bundle-package/index.js',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Named Package',
|
||||
version: '1.0.0',
|
||||
extensions: [
|
||||
{
|
||||
type: 'section',
|
||||
alias: 'My.Section.Custom',
|
||||
name: 'Custom Section',
|
||||
js: '/App_Plugins/section.js',
|
||||
elementName: 'my-section-custom',
|
||||
weight: 1,
|
||||
meta: {
|
||||
label: 'Custom',
|
||||
pathname: 'my-custom',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'propertyEditorUi',
|
||||
alias: 'My.PropertyEditorUI.Custom',
|
||||
name: 'My Custom Property Editor UI',
|
||||
js: '/App_Plugins/property-editor.js',
|
||||
elementName: 'my-property-editor-ui-custom',
|
||||
meta: {
|
||||
label: 'My Custom Property',
|
||||
icon: 'document',
|
||||
group: 'Common',
|
||||
propertyEditorSchema: 'Umbraco.TextBox',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Package with an entry point',
|
||||
extensions: [
|
||||
{
|
||||
type: 'backofficeEntryPoint',
|
||||
name: 'My Custom Entry Point',
|
||||
alias: 'My.Entrypoint.Custom',
|
||||
js: '/App_Plugins/custom-entrypoint.js',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'My MFA Package',
|
||||
extensions: [
|
||||
{
|
||||
type: 'mfaLoginProvider',
|
||||
alias: 'My.MfaLoginProvider.Custom.Google',
|
||||
name: 'My Custom Google MFA Provider',
|
||||
forProviderName: 'Google Authenticator',
|
||||
},
|
||||
{
|
||||
type: 'mfaLoginProvider',
|
||||
alias: 'My.MfaLoginProvider.Custom.SMS',
|
||||
name: 'My Custom SMS MFA Provider',
|
||||
forProviderName: 'sms',
|
||||
meta: {
|
||||
label: 'Setup SMS Verification',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Package with a view',
|
||||
extensions: [
|
||||
{
|
||||
type: 'packageView',
|
||||
alias: 'My.PackageView.Custom',
|
||||
name: 'My Custom Package View',
|
||||
js: '/App_Plugins/package-view.js',
|
||||
meta: {
|
||||
packageName: 'Package with a view',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'My MFA Package',
|
||||
extensions: [
|
||||
{
|
||||
type: 'mfaLoginProvider',
|
||||
alias: 'My.MfaLoginProvider.Custom',
|
||||
name: 'My Custom MFA Provider',
|
||||
forProviderName: 'sms',
|
||||
meta: {
|
||||
label: 'Setup SMS Verification',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]),
|
||||
ctx.json<PackageManifestResponse>(privateManifests),
|
||||
);
|
||||
}),
|
||||
rest.get(umbracoPath('/manifest/manifest/public'), (_req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json<PackageManifestResponse>([
|
||||
{
|
||||
name: 'My Auth Package',
|
||||
extensions: [
|
||||
{
|
||||
type: 'authProvider',
|
||||
alias: 'My.AuthProvider.Google',
|
||||
name: 'My Custom Auth Provider',
|
||||
forProviderName: 'Umbraco.Google',
|
||||
meta: {
|
||||
label: 'Sign in with Google',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'authProvider',
|
||||
alias: 'My.AuthProvider.Github',
|
||||
name: 'My Github Auth Provider',
|
||||
forProviderName: 'Umbraco.Github',
|
||||
meta: {
|
||||
label: 'GitHub',
|
||||
defaultView: {
|
||||
look: 'primary',
|
||||
icon: 'icon-github',
|
||||
color: 'success',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]),
|
||||
);
|
||||
return res(ctx.status(200), ctx.json<PackageManifestResponse>(publicManifests));
|
||||
}),
|
||||
rest.get(umbracoPath('/manifest/manifest'), (_req, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json<PackageManifestResponse>([...privateManifests, ...publicManifests]));
|
||||
}),
|
||||
];
|
||||
|
||||
@@ -158,4 +171,7 @@ export const manifestEmptyHandlers = [
|
||||
rest.get(umbracoPath('/manifest/manifest/public'), (_req, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json<PackageManifestResponse>([]));
|
||||
}),
|
||||
rest.get(umbracoPath('/manifest/manifest'), (_req, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json<PackageManifestResponse>([]));
|
||||
}),
|
||||
];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const { rest } = window.MockServiceWorker;
|
||||
import { umbUserMockDb } from '../../data/user/user.db.js';
|
||||
import { UMB_SLUG } from './slug.js';
|
||||
import type { LinkedLoginsRequestModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
export const handlers = [
|
||||
@@ -8,6 +9,19 @@ export const handlers = [
|
||||
const loggedInUser = umbUserMockDb.getCurrentUser();
|
||||
return res(ctx.status(200), ctx.json(loggedInUser));
|
||||
}),
|
||||
rest.get<LinkedLoginsRequestModel>(umbracoPath(`${UMB_SLUG}/current/logins`), (_req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json<LinkedLoginsRequestModel>({
|
||||
linkedLogins: [
|
||||
{
|
||||
providerKey: 'google',
|
||||
providerName: 'Umbraco.Google',
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
}),
|
||||
rest.get(umbracoPath(`${UMB_SLUG}/current/2fa`), (_req, res, ctx) => {
|
||||
const mfaLoginProviders = umbUserMockDb.getMfaLoginProviders();
|
||||
return res(ctx.status(200), ctx.json(mfaLoginProviders));
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import { UMB_CURRENT_USER_MFA_MODAL } from '../modals/current-user-mfa/current-user-mfa-modal.token.js';
|
||||
import { html, customElement, state, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
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-current-user-app')
|
||||
export class UmbMfaProvidersCurrentUserAppElement extends UmbLitElement {
|
||||
@state()
|
||||
_hasProviders = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#init();
|
||||
}
|
||||
|
||||
async #init() {
|
||||
this._hasProviders = (await firstValueFrom(umbExtensionsRegistry.byType('mfaLoginProvider'))).length > 0;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this._hasProviders) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<uui-button
|
||||
type="button"
|
||||
look="primary"
|
||||
label="${this.localize.term('user_configureTwoFactor')}"
|
||||
@click=${this.#onClick}>
|
||||
<uui-icon name="icon-rectangle-ellipsis"></uui-icon>
|
||||
<umb-localize key="user_configureTwoFactor">Configure Two Factor</umb-localize>
|
||||
</uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
async #onClick() {
|
||||
const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
|
||||
await modalManagerContext.open(this, UMB_CURRENT_USER_MFA_MODAL).onSubmit();
|
||||
}
|
||||
|
||||
static styles = [UmbTextStyles];
|
||||
}
|
||||
|
||||
export default UmbMfaProvidersCurrentUserAppElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-mfa-providers-current-user-app': UmbMfaProvidersCurrentUserAppElement;
|
||||
}
|
||||
}
|
||||
@@ -79,9 +79,7 @@ export class UmbCurrentUserMfaModalElement extends UmbLitElement {
|
||||
)}
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<uui-button @click=${this.#close} look="secondary" .label=${this.localize.term('general_close')}>
|
||||
${this.localize.term('general_close')}
|
||||
</uui-button>
|
||||
<uui-button @click=${this.#close} look="secondary" .label=${this.localize.term('general_close')}></uui-button>
|
||||
</div>
|
||||
</umb-body-layout>
|
||||
`;
|
||||
@@ -98,7 +96,6 @@ export class UmbCurrentUserMfaModalElement extends UmbLitElement {
|
||||
() => html`
|
||||
<p style="margin-top:0">
|
||||
<umb-localize key="user_2faProviderIsEnabled">This two-factor provider is enabled</umb-localize>
|
||||
<uui-icon icon="check"></uui-icon>
|
||||
</p>
|
||||
<uui-button
|
||||
type="button"
|
||||
|
||||
@@ -79,9 +79,7 @@ export class UmbUserMfaModalElement extends UmbLitElement {
|
||||
)}
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<uui-button @click=${this.#close} look="secondary" .label=${this.localize.term('general_close')}>
|
||||
${this.localize.term('general_close')}
|
||||
</uui-button>
|
||||
<uui-button @click=${this.#close} look="secondary" .label=${this.localize.term('general_close')}></uui-button>
|
||||
</div>
|
||||
</umb-body-layout>
|
||||
`;
|
||||
@@ -98,7 +96,6 @@ export class UmbUserMfaModalElement extends UmbLitElement {
|
||||
() => html`
|
||||
<p style="margin-top:0">
|
||||
<umb-localize key="user_2faProviderIsEnabled">This two-factor provider is enabled</umb-localize>
|
||||
<uui-icon icon="check"></uui-icon>
|
||||
</p>
|
||||
<uui-button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user