diff --git a/web/src/admin/Routes.ts b/web/src/admin/Routes.ts index d7f9792c9..f6df48cbb 100644 --- a/web/src/admin/Routes.ts +++ b/web/src/admin/Routes.ts @@ -136,9 +136,9 @@ export const ROUTES: Route[] = [ await import("@goauthentik/admin/crypto/CertificateKeyPairListPage"); return html``; }), - new Route(new RegExp("^/admin/settings$"), async() => { - await import("@goauthentik/admin/admin-settings/AdminSettingsViewPage"); - return html``; + new Route(new RegExp("^/admin/settings$"), async () => { + await import("@goauthentik/admin/admin-settings/AdminSettingsPage"); + return html``; }), new Route(new RegExp("^/blueprints/instances$"), async () => { await import("@goauthentik/admin/blueprints/BlueprintListPage"); diff --git a/web/src/admin/admin-settings/AdminSettingsForm.ts b/web/src/admin/admin-settings/AdminSettingsForm.ts index b63964f8e..0a5d55118 100644 --- a/web/src/admin/admin-settings/AdminSettingsForm.ts +++ b/web/src/admin/admin-settings/AdminSettingsForm.ts @@ -1,49 +1,156 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { dateTimeLocal, first } from "@goauthentik/common/utils"; +import "@goauthentik/components/ak-switch-input"; +import "@goauthentik/components/ak-text-input"; +import "@goauthentik/components/ak-textarea-input"; +import { Form } from "@goauthentik/elements/forms/Form"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; -import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; import "@goauthentik/elements/forms/Radio"; import "@goauthentik/elements/forms/SearchSelect"; import { msg } from "@lit/localize"; import { TemplateResult, html } from "lit"; -import { customElement, state } from "lit/decorators.js"; +import { customElement, property, state } from "lit/decorators.js"; -import { AdminApi, Settings } from "@goauthentik/api"; +import PFList from "@patternfly/patternfly/components/List/list.css"; + +import { AdminApi, Settings, SettingsRequest } from "@goauthentik/api"; @customElement("ak-admin-settings-form") -export class AdminSettingsForm extends ModelForm { - - async loadInstance(pk: string): Promise { - return await new AdminApi(DEFAULT_CONFIG).adminSettingsRetrieve(); +export class AdminSettingsForm extends Form { + @property({ attribute: false }) + set settings(value: Settings) { + this._settings = value; } + private _settings?: Settings; + getSuccessMessage(): string { return msg("Successfully updated settings."); } - async send(data: Settings): Promise { + async send(data: SettingsRequest): Promise { return new AdminApi(DEFAULT_CONFIG).adminSettingsUpdate({ - settingsRequest: data + settingsRequest: data, }); } + static get styles(): CSSResult[] { + return super.styles.concat(PFList); + } + renderForm(): TemplateResult { - return html` + ${msg( + "Configure how authentik should show avatars for users. The following values can be set:", + )} +

+

+

    +
  • none: ${msg( + "Disables per-user avatars and just shows a 1x1 pixel transparent picture", + )}
  • +
  • gravatar: ${msg( + "Uses gravatar with the user's email address", + )}
  • +
  • initials: ${msg( + "Generated avatars based on the user's name", + )}
  • +
  • ${msg( + "Any URL: If you want to use images hosted on another server, you can set any URL. Additionally, these placeholders can be used:", + )} +
      +
    • %(username)s: ${msg( + "The user's username", + )}
    • +
    • %(mail_hash)s: ${msg( + "The email address, md5 hashed", + )}
    • +
    • %(upn)s: ${msg( + "The user's UPN, if set (otherwise an empty string)", + )}
    • +
    +
  • +
  • ${msg( + html`An attribute path like + attributes.something.avatar, which can be used in + combination with the file field to allow users to upload custom + avatars for themselves.`, + )}
  • +
+

+

+ ${msg( + "Multiple values can be set, comma-separated, and authentik will fallback to the next mode when no avatar could be found.", + )} + ${msg( + html`For example, setting this to gravatar,initials will + attempt to get an avatar from Gravatar, and if the user has not + configured on there, it will fallback to a generated avatar.`, + )} +

+ `} + required > - -

- ${msg("Configure how authentik should show avatars for users.")} -

-
`; + + + + + + + + + + + + + ${msg( + "This option configures the footer links on the flow executor pages. It must be a valid JSON list and can be used as follows:", + )} + [{"name": "Link Name","href":"https://goauthentik.io"}] +

+ `} + > +
+ `; } } diff --git a/web/src/admin/admin-settings/AdminSettingsPage.ts b/web/src/admin/admin-settings/AdminSettingsPage.ts new file mode 100644 index 000000000..680b72a0f --- /dev/null +++ b/web/src/admin/admin-settings/AdminSettingsPage.ts @@ -0,0 +1,110 @@ +import "@goauthentik/admin/admin-settings/AdminSettingsForm"; +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { EVENT_REFRESH } from "@goauthentik/common/constants"; +import { convertToTitle } from "@goauthentik/common/utils"; +import "@goauthentik/components/events/ObjectChangelog"; +import { AKElement } from "@goauthentik/elements/Base"; +import "@goauthentik/elements/CodeMirror"; +import "@goauthentik/elements/EmptyState"; +import "@goauthentik/elements/Markdown"; +import "@goauthentik/elements/PageHeader"; +import "@goauthentik/elements/Tabs"; +import "@goauthentik/elements/buttons/ModalButton"; +import "@goauthentik/elements/buttons/SpinnerButton"; +import "@goauthentik/elements/buttons/SpinnerButton"; +import "@goauthentik/elements/forms/ModalForm"; + +import { msg } from "@lit/localize"; +import { CSSResult, TemplateResult, html } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; + +import PFBanner from "@patternfly/patternfly/components/Banner/banner.css"; +import PFButton from "@patternfly/patternfly/components/Button/button.css"; +import PFCard from "@patternfly/patternfly/components/Card/card.css"; +import PFContent from "@patternfly/patternfly/components/Content/content.css"; +import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; +import PFForm from "@patternfly/patternfly/components/Form/form.css"; +import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; +import PFPage from "@patternfly/patternfly/components/Page/page.css"; +import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; + +import { AdminApi, Settings } from "@goauthentik/api"; + +@customElement("ak-admin-settings") +export class AdminSettingsPage extends AKElement { + static get styles(): CSSResult[] { + return [ + PFBase, + PFButton, + PFPage, + PFGrid, + PFContent, + PFCard, + PFDescriptionList, + PFForm, + PFFormControl, + PFBanner, + ]; + } + @property({ attribute: false }) + settings?: Settings; + + loadSettings(): void { + new AdminApi(DEFAULT_CONFIG).adminSettingsRetrieve().then((settings) => { + this.settings = settings; + }); + } + + firstUpdated(): void { + this.loadSettings(); + } + + async save(): void { + const form = this.shadowRoot?.querySelector("ak-admin-settings-form"); + if (!form) { + return; + } + await form.submit(new Event("submit")); + this.resetForm(); + } + + resetForm(): void { + const form = this.shadowRoot?.querySelector("ak-admin-settings-form"); + if (!form) { + return; + } + this.loadSettings(); + form.settings = this.settings; + form.resetForm(); + } + + render(): TemplateResult { + if (!this.settings) { + return html``; + } + return html` + + ${msg("System settings")} + +
+ + + { + await this.save(); + }} + class="pf-m-primary" + >${msg("Save")} + { + this.resetForm(); + }} + class="pf-m-secondary" + >${msg("Cancel")} +
+ `; + } +} diff --git a/web/src/admin/admin-settings/AdminSettingsViewPage.ts b/web/src/admin/admin-settings/AdminSettingsViewPage.ts deleted file mode 100644 index 2b39d7eeb..000000000 --- a/web/src/admin/admin-settings/AdminSettingsViewPage.ts +++ /dev/null @@ -1,184 +0,0 @@ -import "@goauthentik/admin/admin-settings/AdminSettingsForm"; -import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { EVENT_REFRESH } from "@goauthentik/common/constants"; -import { convertToTitle } from "@goauthentik/common/utils"; -import "@goauthentik/components/events/ObjectChangelog"; -import { AKElement } from "@goauthentik/elements/Base"; -import "@goauthentik/elements/CodeMirror"; -import "@goauthentik/elements/EmptyState"; -import "@goauthentik/elements/Markdown"; -import "@goauthentik/elements/PageHeader"; -import "@goauthentik/elements/Tabs"; -import "@goauthentik/elements/buttons/ModalButton"; -import "@goauthentik/elements/buttons/SpinnerButton"; -import "@goauthentik/elements/forms/ModalForm"; - -import { msg } from "@lit/localize"; -import { CSSResult, TemplateResult, html } from "lit"; -import { customElement, property, state } from "lit/decorators.js"; - -import PFBanner from "@patternfly/patternfly/components/Banner/banner.css"; -import PFButton from "@patternfly/patternfly/components/Button/button.css"; -import PFCard from "@patternfly/patternfly/components/Card/card.css"; -import PFContent from "@patternfly/patternfly/components/Content/content.css"; -import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; -import PFForm from "@patternfly/patternfly/components/Form/form.css"; -import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; -import PFPage from "@patternfly/patternfly/components/Page/page.css"; -import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; -import PFBase from "@patternfly/patternfly/patternfly-base.css"; - -import { AdminApi, Settings } from "@goauthentik/api"; - -@customElement("ak-admin-settings-view") -export class AdminSettingsViewPage extends AKElement { - @property({ attribute: false }) - settings?: Settings; - - firstUpdated(): void { - new AdminApi(DEFAULT_CONFIG).adminSettingsRetrieve().then((settings) => { - this.settings = settings; - }); - } - - static get styles(): CSSResult[] { - return [ - PFBase, - PFButton, - PFPage, - PFGrid, - PFContent, - PFCard, - PFDescriptionList, - PFForm, - PFFormControl, - PFBanner, - ]; - } - - render(): TemplateResult { - if (!this.settings) { - return html``; - } - // TODO: someone else than Marc, make this look ok, perhaps by directly embedding the form - // with Save/Cancel buttons - // TODO: add descriptive text about what each of these do, as is currently presented in - // https://goauthentik.io/docs/installation/configuration - return html` - ${msg("System settings")} - -
-
-
-
-
-
- ${msg("Avatars")} -
-
-
- ${this.settings.avatars} -
-
-
-
-
- ${msg("Default user change name")} -
-
-
- ${this.settings.defaultUserChangeName - ? msg("Allowed") - : msg("Disallowed")} -
-
-
-
-
- ${msg("Default user change email")} -
-
-
- ${this.settings.defaultUserChangeEmail - ? msg("Allowed") - : msg("Disallowed")} -
-
-
-
-
- ${msg("Default user change username")} -
-
-
- ${this.settings.defaultUserChangeUsername - ? msg("Allowed") - : msg("Disallowed")} -
-
-
-
-
- ${msg("GDPR compliance")} -
-
-
- ${this.settings.defaultUserChangeUsername - ? msg("Enabled") - : msg("Disabled")} -
-
-
-
-
- ${msg("Impersonation")} -
-
-
- ${this.settings.defaultUserChangeUsername - ? msg("Enabled") - : msg("Disabled")} -
-
-
-
-
- ${msg("Footer links")} -
-
-
- ${this.settings.footerLinks} -
-
-
-
-
- -
-
`; - } -}