import { AndNext, DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { SentryIgnoredError } from "@goauthentik/common/errors"; import { globalAK } from "@goauthentik/common/global"; import { deviceTypeName } from "@goauthentik/common/labels"; import { getRelativeTime } from "@goauthentik/common/utils"; import "@goauthentik/elements/buttons/Dropdown"; import "@goauthentik/elements/buttons/ModalButton"; import "@goauthentik/elements/buttons/TokenCopyButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; import "@goauthentik/user/user-settings/mfa/MFADeviceForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { msg, str } from "@lit/localize"; import { TemplateResult, html } from "lit"; import { customElement, property } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; import { AuthenticatorsApi, Device, UserSetting } from "@goauthentik/api"; export const stageToAuthenticatorName = (stage: UserSetting) => stage.title ?? `Invalid stage component ${stage.component}`; @customElement("ak-user-settings-mfa") export class MFADevicesPage extends Table { @property({ attribute: false }) userSettings?: UserSetting[]; checkbox = true; clearOnRefresh = true; async apiEndpoint(): Promise> { const devices = await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsAllList(); return { pagination: { current: 0, count: devices.length, totalPages: 1, startIndex: 1, endIndex: devices.length, next: 0, previous: 0, }, results: devices, }; } columns(): TableColumn[] { // prettier-ignore return [ msg("Name"), msg("Type"), msg("Created at"), msg("Last used at"), "" ].map((th) => new TableColumn(th, "")); } renderToolbar(): TemplateResult { const settings = (this.userSettings || []).filter((stage) => { if (stage.component === "ak-user-settings-password") { return false; } return stage.configureUrl; }); return html` ${super.renderToolbar()}`; } async deleteWrapper(device: Device) { const api = new AuthenticatorsApi(DEFAULT_CONFIG); const id = { id: parseInt(device.pk, 10) }; switch (device.type) { case "authentik_stages_authenticator_duo.DuoDevice": return api.authenticatorsDuoDestroy(id); case "authentik_stages_authenticator_email.EmailDevice": return api.authenticatorsEmailDestroy(id); case "authentik_stages_authenticator_sms.SMSDevice": return api.authenticatorsSmsDestroy(id); case "authentik_stages_authenticator_totp.TOTPDevice": return api.authenticatorsTotpDestroy(id); case "authentik_stages_authenticator_static.StaticDevice": return api.authenticatorsStaticDestroy(id); case "authentik_stages_authenticator_webauthn.WebAuthnDevice": return api.authenticatorsWebauthnDestroy(id); default: throw new SentryIgnoredError( msg(str`Device type ${device.verboseName} cannot be deleted`), ); } } renderToolbarSelected(): TemplateResult { const disabled = this.selectedElements.length < 1; return html` { return this.deleteWrapper(item); }} > `; } row(item: Device): TemplateResult[] { return [ html`${item.name}`, html`${deviceTypeName(item)} ${item.extraDescription ? ` - ${item.extraDescription}` : ""}`, html`${item.created.getTime() > 0 ? html`
${getRelativeTime(item.created)}
${item.created.toLocaleString()}` : html`-`}`, html`${item.lastUsed ? html`
${getRelativeTime(item.lastUsed)}
${item.lastUsed.toLocaleString()}` : html`-`}`, html` ${msg("Update")} ${msg("Update Device")} `, ]; } } declare global { interface HTMLElementTagNameMap { "ak-user-settings-mfa": MFADevicesPage; } }