
* web: fix esbuild issue with style sheets Getting ESBuild, Lit, and Storybook to all agree on how to read and parse stylesheets is a serious pain. This fix better identifies the value types (instances) being passed from various sources in the repo to the three *different* kinds of style processors we're using (the native one, the polyfill one, and whatever the heck Storybook does internally). Falling back to using older CSS instantiating techniques one era at a time seems to do the trick. It's ugly, but in the face of the aggressive styling we use to avoid Flashes of Unstyled Content (FLoUC), it's the logic with which we're left. In standard mode, the following warning appears on the console when running a Flow: ``` Autofocus processing was blocked because a document already has a focused element. ``` In compatibility mode, the following **error** appears on the console when running a Flow: ``` crawler-inject.js:1106 Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'. at initDomMutationObservers (crawler-inject.js:1106:18) at crawler-inject.js:1114:24 at Array.forEach (<anonymous>) at initDomMutationObservers (crawler-inject.js:1114:10) at crawler-inject.js:1549:1 initDomMutationObservers @ crawler-inject.js:1106 (anonymous) @ crawler-inject.js:1114 initDomMutationObservers @ crawler-inject.js:1114 (anonymous) @ crawler-inject.js:1549 ``` Despite this error, nothing seems to be broken and flows work as anticipated. * web: add more linting * A reliable test for the extra code needed in analyzer, passing shellcheck * web: re-enable custom-element-manifest and enable component checking in Typescript This commit includes a monkeypatch to allow custom-element-manifest (CEM) to work correctly again despite our rich collection of mixins, reactive controllers, symbol-oriented event handlers, and the like. With that monkeypatch in place, we can now create the CEM manifest file and then exploit it so that IDEs and the Typescript compilation pass can tell when a component is being used incorrectly; when the wrong types are being passed to it, or when a required attribute is not initialized. * Added building the manifest to the build process, rather than storing it. It is not appreciably slow. * web: the most boring PR in the universe: Add HTMLTagNameElementMap to everyhing This commit adds HTMLTagNameElementMap entries to every web component in the front end. Activating and associating the HTMLTagNamElementMap with its class has enabled [LitAnalyzer](https://github.com/runem/lit-analyzer/tree/master/packages/lit-analyzer) to reveal a *lot* of basic problems within the UI, the most popular of which is "missing import." We usually get away with it because the object being imported was already registered with the browser elsewhere, but it still surprises me that we haven't gotten any complaints over things like: ``` ./src/flow/stages/base.ts Missing import for <ak-form-static> 96: <ak-form-static no-missing-import ``` Given how early and fundamental that seems to be in our code, I'd have expected to hear _something_ about it. I have not enabled most of the possible checks because, well, there are just a ton of warnings when I do. I'd like to get in and fix those. Aside from this, I have also _removed_ `customElement` declarations from anything declared as an `abstract class`. It makes no sense to try and instantiate something that cannot, by definition, be instantiated. If the class is capable of running on its own, it's not abstract, it just needs to be overridden in child classes. Before removing the declaration I did check to make sure no other piece of code was even *trying* to instantiate it, and so far I have detected no failures. Those elements were: - elements/forms/Form.ts - element-/wizard/WizardFormPage.ts The one that blows my mind, though, is this: ``` src/elements/forms/ProxyForm.ts 6-@customElement("ak-proxy-form") 7:export abstract class ProxyForm extends Form<unknown> { ``` Which, despite being `abstract`, is somehow instantiable? ``` src/admin/outposts/ServiceConnectionListPage.ts: <ak-proxy-form src/admin/providers/ProviderListPage.ts: <ak-proxy-form src/admin/sources/SourceWizard.ts: <ak-proxy-form src/admin/sources/SourceListPage.ts: <ak-proxy-form src/admin/providers/ProviderWizard.ts: <ak-proxy-form type=${type.component}></ak-proxy-form> src/admin/stages/StageListPage.ts: <ak-proxy-form ``` I've made a note to investigate. I've started a new folder where all of my one-off tools for *how* a certain PR was run. It has a README describing what it's for, and the first tool, `add-htmlelementtagnamemaps-to-everything`, is its first entry. That tool is also documented internally. ``` Gilbert & Sullivan I've got a little list, I've got a little list, Of all the code that would never be missed, The duplicate code of cute-and-paste, The weak abstractions that lead to waste, The embedded templates-- you get the gist, There ain't none of 'em that will ever be missed, And that's why I've got them on my list! ```
223 lines
9.8 KiB
TypeScript
223 lines
9.8 KiB
TypeScript
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
|
import { first } from "@goauthentik/common/utils";
|
|
import "@goauthentik/components/ak-number-input";
|
|
import "@goauthentik/components/ak-switch-input";
|
|
import "@goauthentik/components/ak-text-input";
|
|
import "@goauthentik/elements/CodeMirror";
|
|
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
|
import { Form } from "@goauthentik/elements/forms/Form";
|
|
import "@goauthentik/elements/forms/FormGroup";
|
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
|
import "@goauthentik/elements/forms/Radio";
|
|
import "@goauthentik/elements/forms/SearchSelect";
|
|
import "@goauthentik/elements/utils/TimeDeltaHelp";
|
|
|
|
import { msg } from "@lit/localize";
|
|
import { CSSResult, TemplateResult, html } from "lit";
|
|
import { customElement, property } from "lit/decorators.js";
|
|
import { ifDefined } from "lit/directives/if-defined.js";
|
|
|
|
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 Form<SettingsRequest> {
|
|
//
|
|
// Custom property accessors in Lit 2 require a manual call to requestUpdate(). See:
|
|
// https://lit.dev/docs/v2/components/properties/#accessors-custom
|
|
//
|
|
set settings(value: Settings | undefined) {
|
|
this._settings = value;
|
|
this.requestUpdate();
|
|
}
|
|
|
|
@property({ type: Object })
|
|
get settings() {
|
|
return this._settings;
|
|
}
|
|
|
|
private _settings?: Settings;
|
|
|
|
static get styles(): CSSResult[] {
|
|
return super.styles.concat(PFList);
|
|
}
|
|
|
|
getSuccessMessage(): string {
|
|
return msg("Successfully updated settings.");
|
|
}
|
|
|
|
async send(data: SettingsRequest): Promise<Settings> {
|
|
const result = await new AdminApi(DEFAULT_CONFIG).adminSettingsUpdate({
|
|
settingsRequest: data,
|
|
});
|
|
this.dispatchEvent(new CustomEvent("ak-admin-setting-changed"));
|
|
return result;
|
|
}
|
|
|
|
renderForm(): TemplateResult {
|
|
return html`
|
|
<ak-text-input
|
|
name="avatars"
|
|
label=${msg("Avatars")}
|
|
value="${ifDefined(this._settings?.avatars)}"
|
|
.bighelp=${html`
|
|
<p class="pf-c-form__helper-text">
|
|
${msg(
|
|
"Configure how authentik should show avatars for users. The following values can be set:",
|
|
)}
|
|
</p>
|
|
<ul class="pf-c-list">
|
|
<li class="pf-c-form__helper-text">
|
|
<code>none</code>:
|
|
${msg(
|
|
"Disables per-user avatars and just shows a 1x1 pixel transparent picture",
|
|
)}
|
|
</li>
|
|
<li class="pf-c-form__helper-text">
|
|
<code>gravatar</code>:
|
|
${msg("Uses gravatar with the user's email address")}
|
|
</li>
|
|
<li class="pf-c-form__helper-text">
|
|
<code>initials</code>:
|
|
${msg("Generated avatars based on the user's name")}
|
|
</li>
|
|
<li class="pf-c-form__helper-text">
|
|
${msg(
|
|
"Any URL: If you want to use images hosted on another server, you can set any URL. Additionally, these placeholders can be used:",
|
|
)}
|
|
<ul class="pf-c-list">
|
|
<li class="pf-c-form__helper-text">
|
|
<code>%(username)s</code>: ${msg("The user's username")}
|
|
</li>
|
|
<li class="pf-c-form__helper-text">
|
|
<code>%(mail_hash)s</code>:
|
|
${msg("The email address, md5 hashed")}
|
|
</li>
|
|
<li class="pf-c-form__helper-text">
|
|
<code>%(upn)s</code>:
|
|
${msg("The user's UPN, if set (otherwise an empty string)")}
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li class="pf-c-form__helper-text">
|
|
${msg(
|
|
html`An attribute path like
|
|
<code>attributes.something.avatar</code>, which can be used in
|
|
combination with the file field to allow users to upload custom
|
|
avatars for themselves.`,
|
|
)}
|
|
</li>
|
|
</ul>
|
|
<p class="pf-c-form__helper-text">
|
|
${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 <code>gravatar,initials</code> will
|
|
attempt to get an avatar from Gravatar, and if the user has not
|
|
configured on there, it will fallback to a generated avatar.`,
|
|
)}
|
|
</p>
|
|
`}
|
|
required
|
|
>
|
|
</ak-text-input>
|
|
<ak-switch-input
|
|
name="defaultUserChangeName"
|
|
label=${msg("Allow users to change name")}
|
|
?checked="${this._settings?.defaultUserChangeName}"
|
|
help=${msg("Enable the ability for users to change their name.")}
|
|
>
|
|
</ak-switch-input>
|
|
<ak-switch-input
|
|
name="defaultUserChangeEmail"
|
|
label=${msg("Allow users to change email")}
|
|
?checked="${this._settings?.defaultUserChangeEmail}"
|
|
help=${msg("Enable the ability for users to change their email.")}
|
|
>
|
|
</ak-switch-input>
|
|
<ak-switch-input
|
|
name="defaultUserChangeUsername"
|
|
label=${msg("Allow users to change username")}
|
|
?checked="${this._settings?.defaultUserChangeUsername}"
|
|
help=${msg("Enable the ability for users to change their username.")}
|
|
>
|
|
</ak-switch-input>
|
|
<ak-text-input
|
|
name="eventRetention"
|
|
label=${msg("Event retention")}
|
|
required
|
|
value="${ifDefined(this._settings?.eventRetention)}"
|
|
.bighelp=${html`<p class="pf-c-form__helper-text">
|
|
${msg("Duration after which events will be deleted from the database.")}
|
|
</p>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg(
|
|
'When using an external logging solution for archiving, this can be set to "minutes=5".',
|
|
)}
|
|
</p>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg(
|
|
"This setting only affects new Events, as the expiration is saved per-event.",
|
|
)}
|
|
</p>
|
|
<ak-utils-time-delta-help></ak-utils-time-delta-help>`}
|
|
>
|
|
</ak-text-input>
|
|
<ak-form-element-horizontal label=${msg("Footer links")} name="footerLinks">
|
|
<ak-codemirror
|
|
mode=${CodeMirrorMode.YAML}
|
|
.value="${first(this._settings?.footerLinks, [])}"
|
|
></ak-codemirror>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg(
|
|
"This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:",
|
|
)}
|
|
<code>[{"name": "Link Name","href":"https://goauthentik.io"}]</code>
|
|
</p>
|
|
</ak-form-element-horizontal>
|
|
<ak-switch-input
|
|
name="gdprCompliance"
|
|
label=${msg("GDPR compliance")}
|
|
?checked="${this._settings?.gdprCompliance}"
|
|
help=${msg(
|
|
"When enabled, all the events caused by a user will be deleted upon the user's deletion.",
|
|
)}
|
|
>
|
|
</ak-switch-input>
|
|
<ak-switch-input
|
|
name="impersonation"
|
|
label=${msg("Impersonation")}
|
|
?checked="${this._settings?.impersonation}"
|
|
help=${msg("Globally enable/disable impersonation.")}
|
|
>
|
|
</ak-switch-input>
|
|
<ak-text-input
|
|
name="defaultTokenDuration"
|
|
label=${msg("Default token duration")}
|
|
required
|
|
value="${ifDefined(this._settings?.defaultTokenDuration)}"
|
|
.bighelp=${html`<p class="pf-c-form__helper-text">
|
|
${msg("Default duration for generated tokens")}
|
|
</p>
|
|
<ak-utils-time-delta-help></ak-utils-time-delta-help>`}
|
|
>
|
|
</ak-text-input>
|
|
<ak-number-input
|
|
label=${msg("Default token length")}
|
|
required
|
|
name="defaultTokenLength"
|
|
value="${first(this._settings?.defaultTokenLength, 60)}"
|
|
help=${msg("Default length of generated tokens")}
|
|
></ak-number-input>
|
|
`;
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"ak-admin-settings-form": AdminSettingsForm;
|
|
}
|
|
}
|