diff --git a/web/src/elements/forms/Form.ts b/web/src/elements/forms/Form.ts index 823520a35..6d3b3ba05 100644 --- a/web/src/elements/forms/Form.ts +++ b/web/src/elements/forms/Form.ts @@ -17,14 +17,14 @@ export class Form extends LitElement { @property() send!: (data: Record) => Promise; - submit(ev: Event): void { + submit(ev: Event): Promise | undefined { ev.preventDefault(); const ironForm = this.shadowRoot?.querySelector("iron-form"); if (!ironForm) { return; } const data = ironForm.serializeForm(); - this.send(data).then(() => { + return this.send(data).then(() => { showMessage({ level_tag: "success", message: this.successMessage diff --git a/web/src/elements/forms/ModalForm.ts b/web/src/elements/forms/ModalForm.ts new file mode 100644 index 000000000..8538036ea --- /dev/null +++ b/web/src/elements/forms/ModalForm.ts @@ -0,0 +1,58 @@ +import { gettext } from "django"; +import { customElement, html, property, TemplateResult } from "lit-element"; +import { ModalButton } from "../buttons/ModalButton"; +import { Form } from "./Form"; + +@customElement("ak-forms-modal") +export class DeleteForm extends ModalButton { + + confirm(): void { + this.querySelectorAll
("ak-form").forEach(form => { + const formPromise = form.submit(new Event("submit")); + if (!formPromise) { + return; + } + formPromise.then(() => { + this.open = false; + }); + }); + } + + renderModalInner(): TemplateResult { + return html`
+
+

+ +

+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ { + this.confirm(); + }} + class="pf-m-primary"> + +   + { + this.open = false; + }} + class="pf-m-secondary"> + ${gettext("Cancel")} + +
`; + } + +} diff --git a/web/src/pages/users/UserSettingsPage.ts b/web/src/pages/users/UserSettingsPage.ts index 75aa14574..d2fb255b0 100644 --- a/web/src/pages/users/UserSettingsPage.ts +++ b/web/src/pages/users/UserSettingsPage.ts @@ -22,7 +22,7 @@ import "./UserDetailsPage"; import "./UserTokenList"; import "./settings/UserSettingsAuthenticatorTOTP"; import "./settings/UserSettingsAuthenticatorStatic"; -import "./settings/UserSettingsAuthenticatorWebAuthnDevices"; +import "./settings/UserSettingsAuthenticatorWebAuthn"; import "./settings/UserSettingsPassword"; import "./settings/SourceSettingsOAuth"; diff --git a/web/src/pages/users/settings/BaseUserSettings.ts b/web/src/pages/users/settings/BaseUserSettings.ts index a4c6d5b5f..5fbd68a99 100644 --- a/web/src/pages/users/settings/BaseUserSettings.ts +++ b/web/src/pages/users/settings/BaseUserSettings.ts @@ -3,6 +3,8 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFCard from "@patternfly/patternfly/components/Card/card.css"; import PFButton from "@patternfly/patternfly/components/Button/button.css"; import AKGlobal from "../../../authentik.css"; +import PFForm from "@patternfly/patternfly/components/Form/form.css"; +import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; export abstract class BaseUserSettings extends LitElement { @@ -10,7 +12,7 @@ export abstract class BaseUserSettings extends LitElement { objectId!: string; static get styles(): CSSResult[] { - return [PFBase, PFCard, PFButton, AKGlobal]; + return [PFBase, PFCard, PFButton, PFForm, PFFormControl, AKGlobal]; } } diff --git a/web/src/pages/users/settings/UserSettingsAuthenticatorWebAuthn.ts b/web/src/pages/users/settings/UserSettingsAuthenticatorWebAuthn.ts new file mode 100644 index 000000000..9a9425777 --- /dev/null +++ b/web/src/pages/users/settings/UserSettingsAuthenticatorWebAuthn.ts @@ -0,0 +1,109 @@ +import { CSSResult, customElement, html, TemplateResult } from "lit-element"; +import { gettext } from "django"; +import { AuthenticatorsApi, StagesApi, WebAuthnDevice } from "authentik-api"; +import { until } from "lit-html/directives/until"; +import { FlowURLManager } from "../../../api/legacy"; +import { DEFAULT_CONFIG } from "../../../api/Config"; +import { BaseUserSettings } from "./BaseUserSettings"; +import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css"; +import "../../../elements/buttons/ModalButton"; +import "../../../elements/buttons/SpinnerButton"; +import "../../../elements/forms/DeleteForm"; +import "../../../elements/forms/Form"; +import "../../../elements/forms/ModalForm"; + +@customElement("ak-user-settings-authenticator-webauthn") +export class UserSettingsAuthenticatorWebAuthn extends BaseUserSettings { + + static get styles(): CSSResult[] { + return super.styles.concat(PFDataList); + } + + renderDelete(device: WebAuthnDevice): TemplateResult { + return html` { + return new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsWebauthnDelete({ + id: device.pk || 0 + }); + }}> + + `; + } + + renderUpdate(device: WebAuthnDevice): TemplateResult { + return html` + + ${gettext("Update")} + + + ${gettext("Update")} + + { + return new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsWebauthnUpdate({ + id: device.pk || 0, + data: data as WebAuthnDevice + }); + }}> + + + + + + +
`; + } + + render(): TemplateResult { + return html`
+
+ ${gettext("WebAuthn Devices")} +
+
+
    + ${until(new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsWebauthnList({}).then((devices) => { + return devices.results.map((device) => { + return html`
  • +
    +
    +
    ${device.name || "-"}
    +
    + ${gettext(`Created ${device.createdOn?.toLocaleString()}`)} +
    +
    + ${this.renderUpdate(device)} + ${this.renderDelete(device)} +
    +
    +
    +
  • `; + }); + }))} +
+
+ +
`; + } + +} diff --git a/web/src/pages/users/settings/UserSettingsAuthenticatorWebAuthnDevices.ts b/web/src/pages/users/settings/UserSettingsAuthenticatorWebAuthnDevices.ts deleted file mode 100644 index e4a306103..000000000 --- a/web/src/pages/users/settings/UserSettingsAuthenticatorWebAuthnDevices.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { customElement, html, TemplateResult } from "lit-element"; -import { gettext } from "django"; -import { AuthenticatorsApi, StagesApi } from "authentik-api"; -import { until } from "lit-html/directives/until"; -import { FlowURLManager, UserURLManager } from "../../../api/legacy"; -import { DEFAULT_CONFIG } from "../../../api/Config"; -import { BaseUserSettings } from "./BaseUserSettings"; -import "../../../elements/buttons/ModalButton"; -import "../../../elements/buttons/SpinnerButton"; -import "../../../elements/forms/DeleteForm"; - -@customElement("ak-user-settings-authenticator-webauthn") -export class UserSettingsAuthenticatorWebAuthnDevices extends BaseUserSettings { - - render(): TemplateResult { - return html`
-
- ${gettext("WebAuthn Devices")} -
-
-
    - ${until(new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsWebauthnList({}).then((devices) => { - return devices.results.map((device) => { - return html`
  • -
    -
    -
    ${device.name || "-"}
    -
    - ${gettext(`Created ${device.createdOn?.toLocaleString()}`)} -
    -
    - - - ${gettext("Update")} - -
    -
    - { - return new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsWebauthnDelete({ - id: device.pk || 0 - }); - }}> - - -
    -
    -
    -
  • `; - }); - }))} -
-
- -
`; - } - -}