web/elements: only render form once instance is loaded (#5049)
* web/elements: only render form once instance is loaded Signed-off-by: Jens Langhammer <jens@goauthentik.io> * use radio for transport Signed-off-by: Jens Langhammer <jens@goauthentik.io> * only wait for instance to be loaded if set Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add hook to load additional data in form Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make send an abstract function instead of attribute Signed-off-by: Jens Langhammer <jens@goauthentik.io> * ensure form is updated after data is loaded Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove until for select and multi-selects in forms Signed-off-by: Jens Langhammer <jens@goauthentik.io> * don't use until for file uploads Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove last until from form Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove deprecated import Signed-off-by: Jens Langhammer <jens@goauthentik.io> * prevent form double load, add error handling for PreventFormSubmit Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix double creation of inner element in proxy form Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make PreventFormSubmit work correctly Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		@ -1,5 +1,6 @@
 | 
			
		||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
 | 
			
		||||
import { first, groupBy } from "@goauthentik/common/utils";
 | 
			
		||||
import { rootInterface } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@goauthentik/elements/forms/FormGroup";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
import "@goauthentik/elements/forms/ModalForm";
 | 
			
		||||
@ -13,7 +14,6 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    Application,
 | 
			
		||||
@ -195,70 +195,58 @@ export class ApplicationForm extends ModelForm<Application, string> {
 | 
			
		||||
                            ${t`If checked, the launch URL will open in a new browser tab or window from the user's application library.`}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    ${until(
 | 
			
		||||
                        config().then((c) => {
 | 
			
		||||
                            if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
 | 
			
		||||
                                return html`<ak-form-element-horizontal
 | 
			
		||||
                                        label=${t`Icon`}
 | 
			
		||||
                                        name="metaIcon"
 | 
			
		||||
                                    >
 | 
			
		||||
                                        <input type="file" value="" class="pf-c-form-control" />
 | 
			
		||||
                                        ${this.instance?.metaIcon
 | 
			
		||||
                                            ? html`
 | 
			
		||||
                                                  <p class="pf-c-form__helper-text">
 | 
			
		||||
                                                      ${t`Currently set to:`}
 | 
			
		||||
                                                      ${this.instance?.metaIcon}
 | 
			
		||||
                                                  </p>
 | 
			
		||||
                                              `
 | 
			
		||||
                                            : html``}
 | 
			
		||||
                                    </ak-form-element-horizontal>
 | 
			
		||||
                                    ${this.instance?.metaIcon
 | 
			
		||||
                                        ? html`
 | 
			
		||||
                                              <ak-form-element-horizontal>
 | 
			
		||||
                                                  <label class="pf-c-switch">
 | 
			
		||||
                                                      <input
 | 
			
		||||
                                                          class="pf-c-switch__input"
 | 
			
		||||
                                                          type="checkbox"
 | 
			
		||||
                                                          @change=${(ev: Event) => {
 | 
			
		||||
                                                              const target =
 | 
			
		||||
                                                                  ev.target as HTMLInputElement;
 | 
			
		||||
                                                              this.clearIcon = target.checked;
 | 
			
		||||
                                                          }}
 | 
			
		||||
                                                      />
 | 
			
		||||
                                                      <span class="pf-c-switch__toggle">
 | 
			
		||||
                                                          <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                              <i
 | 
			
		||||
                                                                  class="fas fa-check"
 | 
			
		||||
                                                                  aria-hidden="true"
 | 
			
		||||
                                                              ></i>
 | 
			
		||||
                                                          </span>
 | 
			
		||||
                                                      </span>
 | 
			
		||||
                                                      <span class="pf-c-switch__label">
 | 
			
		||||
                                                          ${t`Clear icon`}
 | 
			
		||||
                                                      </span>
 | 
			
		||||
                                                  </label>
 | 
			
		||||
                                                  <p class="pf-c-form__helper-text">
 | 
			
		||||
                                                      ${t`Delete currently set icon.`}
 | 
			
		||||
                                                  </p>
 | 
			
		||||
                                              </ak-form-element-horizontal>
 | 
			
		||||
                                          `
 | 
			
		||||
                                        : html``}`;
 | 
			
		||||
                            }
 | 
			
		||||
                            return html`<ak-form-element-horizontal
 | 
			
		||||
                                label=${t`Icon`}
 | 
			
		||||
                                name="metaIcon"
 | 
			
		||||
                            >
 | 
			
		||||
                                <input
 | 
			
		||||
                                    type="text"
 | 
			
		||||
                                    value="${first(this.instance?.metaIcon, "")}"
 | 
			
		||||
                                    class="pf-c-form-control"
 | 
			
		||||
                                />
 | 
			
		||||
                                <p class="pf-c-form__helper-text">
 | 
			
		||||
                                    ${t`Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".`}
 | 
			
		||||
                                </p>
 | 
			
		||||
                            </ak-form-element-horizontal>`;
 | 
			
		||||
                        }),
 | 
			
		||||
                    )}
 | 
			
		||||
                    ${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.SaveMedia)
 | 
			
		||||
                        ? html`<ak-form-element-horizontal label=${t`Icon`} name="metaIcon">
 | 
			
		||||
                                  <input type="file" value="" class="pf-c-form-control" />
 | 
			
		||||
                                  ${this.instance?.metaIcon
 | 
			
		||||
                                      ? html`
 | 
			
		||||
                                            <p class="pf-c-form__helper-text">
 | 
			
		||||
                                                ${t`Currently set to:`} ${this.instance?.metaIcon}
 | 
			
		||||
                                            </p>
 | 
			
		||||
                                        `
 | 
			
		||||
                                      : html``}
 | 
			
		||||
                              </ak-form-element-horizontal>
 | 
			
		||||
                              ${this.instance?.metaIcon
 | 
			
		||||
                                  ? html`
 | 
			
		||||
                                        <ak-form-element-horizontal>
 | 
			
		||||
                                            <label class="pf-c-switch">
 | 
			
		||||
                                                <input
 | 
			
		||||
                                                    class="pf-c-switch__input"
 | 
			
		||||
                                                    type="checkbox"
 | 
			
		||||
                                                    @change=${(ev: Event) => {
 | 
			
		||||
                                                        const target =
 | 
			
		||||
                                                            ev.target as HTMLInputElement;
 | 
			
		||||
                                                        this.clearIcon = target.checked;
 | 
			
		||||
                                                    }}
 | 
			
		||||
                                                />
 | 
			
		||||
                                                <span class="pf-c-switch__toggle">
 | 
			
		||||
                                                    <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                        <i
 | 
			
		||||
                                                            class="fas fa-check"
 | 
			
		||||
                                                            aria-hidden="true"
 | 
			
		||||
                                                        ></i>
 | 
			
		||||
                                                    </span>
 | 
			
		||||
                                                </span>
 | 
			
		||||
                                                <span class="pf-c-switch__label">
 | 
			
		||||
                                                    ${t`Clear icon`}
 | 
			
		||||
                                                </span>
 | 
			
		||||
                                            </label>
 | 
			
		||||
                                            <p class="pf-c-form__helper-text">
 | 
			
		||||
                                                ${t`Delete currently set icon.`}
 | 
			
		||||
                                            </p>
 | 
			
		||||
                                        </ak-form-element-horizontal>
 | 
			
		||||
                                    `
 | 
			
		||||
                                  : html``}`
 | 
			
		||||
                        : html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                              <input
 | 
			
		||||
                                  type="text"
 | 
			
		||||
                                  value="${first(this.instance?.metaIcon, "")}"
 | 
			
		||||
                                  class="pf-c-form-control"
 | 
			
		||||
                              />
 | 
			
		||||
                              <p class="pf-c-form__helper-text">
 | 
			
		||||
                                  ${t`Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".`}
 | 
			
		||||
                              </p>
 | 
			
		||||
                          </ak-form-element-horizontal>`}
 | 
			
		||||
                    <ak-form-element-horizontal label=${t`Publisher`} name="metaPublisher">
 | 
			
		||||
                        <input
 | 
			
		||||
                            type="text"
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,6 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    CoreApi,
 | 
			
		||||
@ -17,17 +16,26 @@ import {
 | 
			
		||||
    EventsApi,
 | 
			
		||||
    Group,
 | 
			
		||||
    NotificationRule,
 | 
			
		||||
    PaginatedNotificationTransportList,
 | 
			
		||||
    SeverityEnum,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-event-rule-form")
 | 
			
		||||
export class RuleForm extends ModelForm<NotificationRule, string> {
 | 
			
		||||
    eventTransports?: PaginatedNotificationTransportList;
 | 
			
		||||
 | 
			
		||||
    loadInstance(pk: string): Promise<NotificationRule> {
 | 
			
		||||
        return new EventsApi(DEFAULT_CONFIG).eventsRulesRetrieve({
 | 
			
		||||
            pbmUuid: pk,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async load(): Promise<void> {
 | 
			
		||||
        this.eventTransports = await new EventsApi(DEFAULT_CONFIG).eventsTransportsList({
 | 
			
		||||
            ordering: "name",
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getSuccessMessage(): string {
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
            return t`Successfully updated rule.`;
 | 
			
		||||
@ -86,28 +94,14 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            <ak-form-element-horizontal label=${t`Transports`} ?required=${true} name="transports">
 | 
			
		||||
                <select class="pf-c-form-control" multiple>
 | 
			
		||||
                    ${until(
 | 
			
		||||
                        new EventsApi(DEFAULT_CONFIG)
 | 
			
		||||
                            .eventsTransportsList({
 | 
			
		||||
                                ordering: "name",
 | 
			
		||||
                            })
 | 
			
		||||
                            .then((transports) => {
 | 
			
		||||
                                return transports.results.map((transport) => {
 | 
			
		||||
                                    const selected = Array.from(
 | 
			
		||||
                                        this.instance?.transports || [],
 | 
			
		||||
                                    ).some((su) => {
 | 
			
		||||
                                        return su == transport.pk;
 | 
			
		||||
                                    });
 | 
			
		||||
                                    return html`<option
 | 
			
		||||
                                        value=${ifDefined(transport.pk)}
 | 
			
		||||
                                        ?selected=${selected}
 | 
			
		||||
                                    >
 | 
			
		||||
                                        ${transport.name}
 | 
			
		||||
                                    </option>`;
 | 
			
		||||
                                });
 | 
			
		||||
                            }),
 | 
			
		||||
                        html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                    )}
 | 
			
		||||
                    ${this.eventTransports?.results.map((transport) => {
 | 
			
		||||
                        const selected = Array.from(this.instance?.transports || []).some((su) => {
 | 
			
		||||
                            return su == transport.pk;
 | 
			
		||||
                        });
 | 
			
		||||
                        return html`<option value=${ifDefined(transport.pk)} ?selected=${selected}>
 | 
			
		||||
                            ${transport.name}
 | 
			
		||||
                        </option>`;
 | 
			
		||||
                    })}
 | 
			
		||||
                </select>
 | 
			
		||||
                <p class="pf-c-form__helper-text">
 | 
			
		||||
                    ${t`Select which transports should be used to notify the user. If none are selected, the notification will only be shown in the authentik UI.`}
 | 
			
		||||
@ -120,7 +114,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
 | 
			
		||||
                <ak-radio
 | 
			
		||||
                    .options=${[
 | 
			
		||||
                        {
 | 
			
		||||
                            label: "Alert",
 | 
			
		||||
                            label: t`Alert`,
 | 
			
		||||
                            value: SeverityEnum.Alert,
 | 
			
		||||
                            default: true,
 | 
			
		||||
                        },
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { first } from "@goauthentik/common/utils";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
 | 
			
		||||
import "@goauthentik/elements/forms/Radio";
 | 
			
		||||
import "@goauthentik/elements/forms/SearchSelect";
 | 
			
		||||
 | 
			
		||||
import { t } from "@lingui/macro";
 | 
			
		||||
@ -56,35 +57,6 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    renderTransportModes(): TemplateResult {
 | 
			
		||||
        return html`
 | 
			
		||||
            <option
 | 
			
		||||
                value=${NotificationTransportModeEnum.Local}
 | 
			
		||||
                ?selected=${this.instance?.mode === NotificationTransportModeEnum.Local}
 | 
			
		||||
            >
 | 
			
		||||
                ${t`Local (notifications will be created within authentik)`}
 | 
			
		||||
            </option>
 | 
			
		||||
            <option
 | 
			
		||||
                value=${NotificationTransportModeEnum.Email}
 | 
			
		||||
                ?selected=${this.instance?.mode === NotificationTransportModeEnum.Email}
 | 
			
		||||
            >
 | 
			
		||||
                ${t`Email`}
 | 
			
		||||
            </option>
 | 
			
		||||
            <option
 | 
			
		||||
                value=${NotificationTransportModeEnum.Webhook}
 | 
			
		||||
                ?selected=${this.instance?.mode === NotificationTransportModeEnum.Webhook}
 | 
			
		||||
            >
 | 
			
		||||
                ${t`Webhook (generic)`}
 | 
			
		||||
            </option>
 | 
			
		||||
            <option
 | 
			
		||||
                value=${NotificationTransportModeEnum.WebhookSlack}
 | 
			
		||||
                ?selected=${this.instance?.mode === NotificationTransportModeEnum.WebhookSlack}
 | 
			
		||||
            >
 | 
			
		||||
                ${t`Webhook (Slack/Discord)`}
 | 
			
		||||
            </option>
 | 
			
		||||
        `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onModeChange(mode: string | undefined): void {
 | 
			
		||||
        if (
 | 
			
		||||
            mode === NotificationTransportModeEnum.Webhook ||
 | 
			
		||||
@ -107,15 +79,32 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
 | 
			
		||||
                />
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            <ak-form-element-horizontal label=${t`Mode`} ?required=${true} name="mode">
 | 
			
		||||
                <select
 | 
			
		||||
                    class="pf-c-form-control"
 | 
			
		||||
                    @change=${(ev: Event) => {
 | 
			
		||||
                        const current = (ev.target as HTMLInputElement).value;
 | 
			
		||||
                        this.onModeChange(current);
 | 
			
		||||
                <ak-radio
 | 
			
		||||
                    @change=${(ev: CustomEvent<NotificationTransportModeEnum>) => {
 | 
			
		||||
                        this.onModeChange(ev.detail);
 | 
			
		||||
                    }}
 | 
			
		||||
                    .options=${[
 | 
			
		||||
                        {
 | 
			
		||||
                            label: t`Local (notifications will be created within authentik)`,
 | 
			
		||||
                            value: NotificationTransportModeEnum.Local,
 | 
			
		||||
                            default: true,
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: t`Email`,
 | 
			
		||||
                            value: NotificationTransportModeEnum.Email,
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: t`Webhook (generic)`,
 | 
			
		||||
                            value: NotificationTransportModeEnum.Webhook,
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: t`Webhook (Slack/Discord)`,
 | 
			
		||||
                            value: NotificationTransportModeEnum.WebhookSlack,
 | 
			
		||||
                        },
 | 
			
		||||
                    ]}
 | 
			
		||||
                    .value=${this.instance?.mode}
 | 
			
		||||
                >
 | 
			
		||||
                    ${this.renderTransportModes()}
 | 
			
		||||
                </select>
 | 
			
		||||
                </ak-radio>
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            <ak-form-element-horizontal
 | 
			
		||||
                ?hidden=${!this.showWebhook}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import { DesignationToLabel, LayoutToLabel } from "@goauthentik/admin/flows/util
 | 
			
		||||
import { AuthenticationEnum } from "@goauthentik/api/dist/models/AuthenticationEnum";
 | 
			
		||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
 | 
			
		||||
import { first } from "@goauthentik/common/utils";
 | 
			
		||||
import { rootInterface } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@goauthentik/elements/forms/FormGroup";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
 | 
			
		||||
@ -12,7 +13,6 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    CapabilitiesEnum,
 | 
			
		||||
@ -315,73 +315,62 @@ export class FlowForm extends ModelForm<Flow, string> {
 | 
			
		||||
                            </option>
 | 
			
		||||
                        </select>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    ${until(
 | 
			
		||||
                        config().then((c) => {
 | 
			
		||||
                            if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
 | 
			
		||||
                                return html`<ak-form-element-horizontal
 | 
			
		||||
                                        label=${t`Background`}
 | 
			
		||||
                                        name="background"
 | 
			
		||||
                                    >
 | 
			
		||||
                                        <input type="file" value="" class="pf-c-form-control" />
 | 
			
		||||
                                        ${this.instance?.background
 | 
			
		||||
                                            ? html`
 | 
			
		||||
                                                  <p class="pf-c-form__helper-text">
 | 
			
		||||
                                                      ${t`Currently set to:`}
 | 
			
		||||
                                                      ${this.instance?.background}
 | 
			
		||||
                                                  </p>
 | 
			
		||||
                                              `
 | 
			
		||||
                                            : html``}
 | 
			
		||||
                                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                                            ${t`Background shown during execution.`}
 | 
			
		||||
                                        </p>
 | 
			
		||||
                                    </ak-form-element-horizontal>
 | 
			
		||||
                                    ${this.instance?.background
 | 
			
		||||
                                        ? html`
 | 
			
		||||
                                              <ak-form-element-horizontal>
 | 
			
		||||
                                                  <label class="pf-c-switch">
 | 
			
		||||
                                                      <input
 | 
			
		||||
                                                          class="pf-c-switch__input"
 | 
			
		||||
                                                          type="checkbox"
 | 
			
		||||
                                                          @change=${(ev: Event) => {
 | 
			
		||||
                                                              const target =
 | 
			
		||||
                                                                  ev.target as HTMLInputElement;
 | 
			
		||||
                                                              this.clearBackground = target.checked;
 | 
			
		||||
                                                          }}
 | 
			
		||||
                                                      />
 | 
			
		||||
                                                      <span class="pf-c-switch__toggle">
 | 
			
		||||
                                                          <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                              <i
 | 
			
		||||
                                                                  class="fas fa-check"
 | 
			
		||||
                                                                  aria-hidden="true"
 | 
			
		||||
                                                              ></i>
 | 
			
		||||
                                                          </span>
 | 
			
		||||
                                                      </span>
 | 
			
		||||
                                                      <span class="pf-c-switch__label">
 | 
			
		||||
                                                          ${t`Clear icon`}
 | 
			
		||||
                                                      </span>
 | 
			
		||||
                                                  </label>
 | 
			
		||||
                                                  <p class="pf-c-form__helper-text">
 | 
			
		||||
                                                      ${t`Delete currently set background image.`}
 | 
			
		||||
                                                  </p>
 | 
			
		||||
                                              </ak-form-element-horizontal>
 | 
			
		||||
                                          `
 | 
			
		||||
                                        : html``}`;
 | 
			
		||||
                            }
 | 
			
		||||
                            return html`<ak-form-element-horizontal
 | 
			
		||||
                                label=${t`Background`}
 | 
			
		||||
                                name="background"
 | 
			
		||||
                            >
 | 
			
		||||
                                <input
 | 
			
		||||
                                    type="text"
 | 
			
		||||
                                    value="${first(this.instance?.background, "")}"
 | 
			
		||||
                                    class="pf-c-form-control"
 | 
			
		||||
                                />
 | 
			
		||||
                                <p class="pf-c-form__helper-text">
 | 
			
		||||
                                    ${t`Background shown during execution.`}
 | 
			
		||||
                                </p>
 | 
			
		||||
                            </ak-form-element-horizontal>`;
 | 
			
		||||
                        }),
 | 
			
		||||
                    )}
 | 
			
		||||
                    ${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.SaveMedia)
 | 
			
		||||
                        ? html`<ak-form-element-horizontal label=${t`Background`} name="background">
 | 
			
		||||
                                  <input type="file" value="" class="pf-c-form-control" />
 | 
			
		||||
                                  ${this.instance?.background
 | 
			
		||||
                                      ? html`
 | 
			
		||||
                                            <p class="pf-c-form__helper-text">
 | 
			
		||||
                                                ${t`Currently set to:`} ${this.instance?.background}
 | 
			
		||||
                                            </p>
 | 
			
		||||
                                        `
 | 
			
		||||
                                      : html``}
 | 
			
		||||
 | 
			
		||||
                                  <p class="pf-c-form__helper-text">
 | 
			
		||||
                                      ${t`Background shown during execution.`}
 | 
			
		||||
                                  </p>
 | 
			
		||||
                              </ak-form-element-horizontal>
 | 
			
		||||
                              ${this.instance?.background
 | 
			
		||||
                                  ? html`
 | 
			
		||||
                                        <ak-form-element-horizontal>
 | 
			
		||||
                                            <label class="pf-c-switch">
 | 
			
		||||
                                                <input
 | 
			
		||||
                                                    class="pf-c-switch__input"
 | 
			
		||||
                                                    type="checkbox"
 | 
			
		||||
                                                    @change=${(ev: Event) => {
 | 
			
		||||
                                                        const target =
 | 
			
		||||
                                                            ev.target as HTMLInputElement;
 | 
			
		||||
                                                        this.clearBackground = target.checked;
 | 
			
		||||
                                                    }}
 | 
			
		||||
                                                />
 | 
			
		||||
                                                <span class="pf-c-switch__toggle">
 | 
			
		||||
                                                    <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                        <i
 | 
			
		||||
                                                            class="fas fa-check"
 | 
			
		||||
                                                            aria-hidden="true"
 | 
			
		||||
                                                        ></i>
 | 
			
		||||
                                                    </span>
 | 
			
		||||
                                                </span>
 | 
			
		||||
                                                <span class="pf-c-switch__label">
 | 
			
		||||
                                                    ${t`Clear background`}
 | 
			
		||||
                                                </span>
 | 
			
		||||
                                            </label>
 | 
			
		||||
                                            <p class="pf-c-form__helper-text">
 | 
			
		||||
                                                ${t`Delete currently set background image.`}
 | 
			
		||||
                                            </p>
 | 
			
		||||
                                        </ak-form-element-horizontal>
 | 
			
		||||
                                    `
 | 
			
		||||
                                  : html``}`
 | 
			
		||||
                        : html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                              <input
 | 
			
		||||
                                  type="text"
 | 
			
		||||
                                  value="${first(this.instance?.background, "")}"
 | 
			
		||||
                                  class="pf-c-form-control"
 | 
			
		||||
                              />
 | 
			
		||||
                              <p class="pf-c-form__helper-text">
 | 
			
		||||
                                  ${t`Background shown during execution.`}
 | 
			
		||||
                              </p>
 | 
			
		||||
                          </ak-form-element-horizontal>`}
 | 
			
		||||
                </div>
 | 
			
		||||
            </ak-form-group>
 | 
			
		||||
        </form>`;
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,4 @@
 | 
			
		||||
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { first, groupBy } from "@goauthentik/common/utils";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
@ -10,11 +11,13 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    Flow,
 | 
			
		||||
    FlowStageBinding,
 | 
			
		||||
    FlowsApi,
 | 
			
		||||
    FlowsInstancesListDesignationEnum,
 | 
			
		||||
    FlowsInstancesListRequest,
 | 
			
		||||
    InvalidResponseActionEnum,
 | 
			
		||||
    PolicyEngineMode,
 | 
			
		||||
    Stage,
 | 
			
		||||
@ -85,23 +88,32 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
 | 
			
		||||
            `;
 | 
			
		||||
        }
 | 
			
		||||
        return html`<ak-form-element-horizontal label=${t`Target`} ?required=${true} name="target">
 | 
			
		||||
            <select class="pf-c-form-control">
 | 
			
		||||
                ${until(
 | 
			
		||||
                    new FlowsApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .flowsInstancesList({
 | 
			
		||||
                            ordering: "slug",
 | 
			
		||||
                        })
 | 
			
		||||
                        .then((flows) => {
 | 
			
		||||
                            return flows.results.map((flow) => {
 | 
			
		||||
                                // No ?selected check here, as this input isn't shown on update forms
 | 
			
		||||
                                return html`<option value=${ifDefined(flow.pk)}>
 | 
			
		||||
                                    ${flow.name} (${flow.slug})
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            });
 | 
			
		||||
                        }),
 | 
			
		||||
                    html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                )}
 | 
			
		||||
            </select>
 | 
			
		||||
            <ak-search-select
 | 
			
		||||
                .fetchObjects=${async (query?: string): Promise<Flow[]> => {
 | 
			
		||||
                    const args: FlowsInstancesListRequest = {
 | 
			
		||||
                        ordering: "slug",
 | 
			
		||||
                        designation: FlowsInstancesListDesignationEnum.Authorization,
 | 
			
		||||
                    };
 | 
			
		||||
                    if (query !== undefined) {
 | 
			
		||||
                        args.search = query;
 | 
			
		||||
                    }
 | 
			
		||||
                    const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(args);
 | 
			
		||||
                    return flows.results;
 | 
			
		||||
                }}
 | 
			
		||||
                .renderElement=${(flow: Flow): string => {
 | 
			
		||||
                    return RenderFlowOption(flow);
 | 
			
		||||
                }}
 | 
			
		||||
                .renderDescription=${(flow: Flow): TemplateResult => {
 | 
			
		||||
                    return html`${flow.name}`;
 | 
			
		||||
                }}
 | 
			
		||||
                .value=${(flow: Flow | undefined): string | undefined => {
 | 
			
		||||
                    return flow?.pk;
 | 
			
		||||
                }}
 | 
			
		||||
                .selected=${(flow: Flow): boolean => {
 | 
			
		||||
                    return flow.pk === this.instance?.target;
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
            </ak-search-select>
 | 
			
		||||
        </ak-form-element-horizontal>`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -10,15 +10,18 @@ import YAML from "yaml";
 | 
			
		||||
import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    Outpost,
 | 
			
		||||
    OutpostDefaultConfig,
 | 
			
		||||
    OutpostTypeEnum,
 | 
			
		||||
    OutpostsApi,
 | 
			
		||||
    OutpostsServiceConnectionsAllListRequest,
 | 
			
		||||
    PaginatedLDAPProviderList,
 | 
			
		||||
    PaginatedProxyProviderList,
 | 
			
		||||
    PaginatedRadiusProviderList,
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
    ServiceConnection,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
@ -31,6 +34,14 @@ export class OutpostForm extends ModelForm<Outpost, string> {
 | 
			
		||||
    @property({ type: Boolean })
 | 
			
		||||
    embedded = false;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    providers?:
 | 
			
		||||
        | PaginatedProxyProviderList
 | 
			
		||||
        | PaginatedLDAPProviderList
 | 
			
		||||
        | PaginatedRadiusProviderList;
 | 
			
		||||
 | 
			
		||||
    defaultConfig?: OutpostDefaultConfig;
 | 
			
		||||
 | 
			
		||||
    async loadInstance(pk: string): Promise<Outpost> {
 | 
			
		||||
        const o = await new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesRetrieve({
 | 
			
		||||
            uuid: pk,
 | 
			
		||||
@ -39,6 +50,34 @@ export class OutpostForm extends ModelForm<Outpost, string> {
 | 
			
		||||
        return o;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async load(): Promise<void> {
 | 
			
		||||
        this.defaultConfig = await new OutpostsApi(
 | 
			
		||||
            DEFAULT_CONFIG,
 | 
			
		||||
        ).outpostsInstancesDefaultSettingsRetrieve();
 | 
			
		||||
        switch (this.type) {
 | 
			
		||||
            case OutpostTypeEnum.Proxy:
 | 
			
		||||
                this.providers = await new ProvidersApi(DEFAULT_CONFIG).providersProxyList({
 | 
			
		||||
                    ordering: "name",
 | 
			
		||||
                    applicationIsnull: false,
 | 
			
		||||
                });
 | 
			
		||||
                break;
 | 
			
		||||
            case OutpostTypeEnum.Ldap:
 | 
			
		||||
                this.providers = await new ProvidersApi(DEFAULT_CONFIG).providersLdapList({
 | 
			
		||||
                    ordering: "name",
 | 
			
		||||
                    applicationIsnull: false,
 | 
			
		||||
                });
 | 
			
		||||
                break;
 | 
			
		||||
            case OutpostTypeEnum.Radius:
 | 
			
		||||
                this.providers = await new ProvidersApi(DEFAULT_CONFIG).providersRadiusList({
 | 
			
		||||
                    ordering: "name",
 | 
			
		||||
                    applicationIsnull: false,
 | 
			
		||||
                });
 | 
			
		||||
                break;
 | 
			
		||||
            case OutpostTypeEnum.UnknownDefaultOpenApi:
 | 
			
		||||
                this.providers = undefined;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getSuccessMessage(): string {
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
            return t`Successfully updated outpost.`;
 | 
			
		||||
@ -60,78 +99,6 @@ export class OutpostForm extends ModelForm<Outpost, string> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    renderProviders(): Promise<TemplateResult[]> {
 | 
			
		||||
        switch (this.type) {
 | 
			
		||||
            case OutpostTypeEnum.Proxy:
 | 
			
		||||
                return new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                    .providersProxyList({
 | 
			
		||||
                        ordering: "name",
 | 
			
		||||
                        applicationIsnull: false,
 | 
			
		||||
                    })
 | 
			
		||||
                    .then((providers) => {
 | 
			
		||||
                        return providers.results.map((provider) => {
 | 
			
		||||
                            const selected = Array.from(this.instance?.providers || []).some(
 | 
			
		||||
                                (sp) => {
 | 
			
		||||
                                    return sp == provider.pk;
 | 
			
		||||
                                },
 | 
			
		||||
                            );
 | 
			
		||||
                            return html`<option
 | 
			
		||||
                                value=${ifDefined(provider.pk)}
 | 
			
		||||
                                ?selected=${selected}
 | 
			
		||||
                            >
 | 
			
		||||
                                ${provider.assignedApplicationName} (${provider.externalHost})
 | 
			
		||||
                            </option>`;
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
            case OutpostTypeEnum.Ldap:
 | 
			
		||||
                return new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                    .providersLdapList({
 | 
			
		||||
                        ordering: "name",
 | 
			
		||||
                        applicationIsnull: false,
 | 
			
		||||
                    })
 | 
			
		||||
                    .then((providers) => {
 | 
			
		||||
                        return providers.results.map((provider) => {
 | 
			
		||||
                            const selected = Array.from(this.instance?.providers || []).some(
 | 
			
		||||
                                (sp) => {
 | 
			
		||||
                                    return sp == provider.pk;
 | 
			
		||||
                                },
 | 
			
		||||
                            );
 | 
			
		||||
                            return html`<option
 | 
			
		||||
                                value=${ifDefined(provider.pk)}
 | 
			
		||||
                                ?selected=${selected}
 | 
			
		||||
                            >
 | 
			
		||||
                                ${provider.assignedApplicationName} (${provider.name})
 | 
			
		||||
                            </option>`;
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
            case OutpostTypeEnum.Radius:
 | 
			
		||||
                return new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                    .providersRadiusList({
 | 
			
		||||
                        ordering: "name",
 | 
			
		||||
                        applicationIsnull: false,
 | 
			
		||||
                    })
 | 
			
		||||
                    .then((providers) => {
 | 
			
		||||
                        return providers.results.map((provider) => {
 | 
			
		||||
                            const selected = Array.from(this.instance?.providers || []).some(
 | 
			
		||||
                                (sp) => {
 | 
			
		||||
                                    return sp == provider.pk;
 | 
			
		||||
                                },
 | 
			
		||||
                            );
 | 
			
		||||
                            return html`<option
 | 
			
		||||
                                value=${ifDefined(provider.pk)}
 | 
			
		||||
                                ?selected=${selected}
 | 
			
		||||
                            >
 | 
			
		||||
                                ${provider.assignedApplicationName} (${provider.name})
 | 
			
		||||
                            </option>`;
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
            case OutpostTypeEnum.UnknownDefaultOpenApi:
 | 
			
		||||
                return Promise.resolve([
 | 
			
		||||
                    html` <option value="">${t`Unknown outpost type`}</option>`,
 | 
			
		||||
                ]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    renderForm(): TemplateResult {
 | 
			
		||||
        return html`<form class="pf-c-form pf-m-horizontal">
 | 
			
		||||
            <ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
 | 
			
		||||
@ -148,6 +115,7 @@ export class OutpostForm extends ModelForm<Outpost, string> {
 | 
			
		||||
                    @change=${(ev: Event) => {
 | 
			
		||||
                        const target = ev.target as HTMLSelectElement;
 | 
			
		||||
                        this.type = target.selectedOptions[0].value as OutpostTypeEnum;
 | 
			
		||||
                        this.load();
 | 
			
		||||
                    }}
 | 
			
		||||
                >
 | 
			
		||||
                    <option
 | 
			
		||||
@ -162,6 +130,12 @@ export class OutpostForm extends ModelForm<Outpost, string> {
 | 
			
		||||
                    >
 | 
			
		||||
                        ${t`LDAP`}
 | 
			
		||||
                    </option>
 | 
			
		||||
                    <option
 | 
			
		||||
                        value=${OutpostTypeEnum.Radius}
 | 
			
		||||
                        ?selected=${this.instance?.type === OutpostTypeEnum.Radius}
 | 
			
		||||
                    >
 | 
			
		||||
                        ${t`Radius`}
 | 
			
		||||
                    </option>
 | 
			
		||||
                </select>
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            <ak-form-element-horizontal label=${t`Integration`} name="serviceConnection">
 | 
			
		||||
@ -213,7 +187,14 @@ export class OutpostForm extends ModelForm<Outpost, string> {
 | 
			
		||||
                name="providers"
 | 
			
		||||
            >
 | 
			
		||||
                <select class="pf-c-form-control" multiple>
 | 
			
		||||
                    ${until(this.renderProviders(), html`<option>${t`Loading...`}</option>`)}
 | 
			
		||||
                    ${this.providers?.results.map((provider) => {
 | 
			
		||||
                        const selected = Array.from(this.instance?.providers || []).some((sp) => {
 | 
			
		||||
                            return sp == provider.pk;
 | 
			
		||||
                        });
 | 
			
		||||
                        return html`<option value=${ifDefined(provider.pk)} ?selected=${selected}>
 | 
			
		||||
                            ${provider.assignedApplicationName} (${provider.name})
 | 
			
		||||
                        </option>`;
 | 
			
		||||
                    })}
 | 
			
		||||
                </select>
 | 
			
		||||
                <p class="pf-c-form__helper-text">
 | 
			
		||||
                    ${t`You can only select providers that match the type of the outpost.`}
 | 
			
		||||
@ -223,19 +204,10 @@ export class OutpostForm extends ModelForm<Outpost, string> {
 | 
			
		||||
                </p>
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            <ak-form-element-horizontal label=${t`Configuration`} name="config">
 | 
			
		||||
                <!-- @ts-ignore -->
 | 
			
		||||
                <ak-codemirror
 | 
			
		||||
                    mode="yaml"
 | 
			
		||||
                    value="${until(
 | 
			
		||||
                        new OutpostsApi(DEFAULT_CONFIG)
 | 
			
		||||
                            .outpostsInstancesDefaultSettingsRetrieve()
 | 
			
		||||
                            .then((config) => {
 | 
			
		||||
                                let fc = config.config;
 | 
			
		||||
                                if (this.instance) {
 | 
			
		||||
                                    fc = this.instance.config;
 | 
			
		||||
                                }
 | 
			
		||||
                                return YAML.stringify(fc);
 | 
			
		||||
                            }),
 | 
			
		||||
                    value="${YAML.stringify(
 | 
			
		||||
                        this.instance ? this.instance.config : this.defaultConfig?.config,
 | 
			
		||||
                    )}"
 | 
			
		||||
                ></ak-codemirror>
 | 
			
		||||
                <p class="pf-c-form__helper-text">
 | 
			
		||||
 | 
			
		||||
@ -10,9 +10,15 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import { AdminApi, EventMatcherPolicy, EventsApi, PoliciesApi, TypeCreate } from "@goauthentik/api";
 | 
			
		||||
import {
 | 
			
		||||
    AdminApi,
 | 
			
		||||
    App,
 | 
			
		||||
    EventMatcherPolicy,
 | 
			
		||||
    EventsApi,
 | 
			
		||||
    PoliciesApi,
 | 
			
		||||
    TypeCreate,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-policy-event-matcher-form")
 | 
			
		||||
export class EventMatcherPolicyForm extends ModelForm<EventMatcherPolicy, string> {
 | 
			
		||||
@ -22,6 +28,12 @@ export class EventMatcherPolicyForm extends ModelForm<EventMatcherPolicy, string
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async load(): Promise<void> {
 | 
			
		||||
        this.apps = await new AdminApi(DEFAULT_CONFIG).adminAppsList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    apps?: App[];
 | 
			
		||||
 | 
			
		||||
    getSuccessMessage(): string {
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
            return t`Successfully updated policy.`;
 | 
			
		||||
@ -118,19 +130,14 @@ export class EventMatcherPolicyForm extends ModelForm<EventMatcherPolicy, string
 | 
			
		||||
                            <option value="" ?selected=${this.instance?.app === undefined}>
 | 
			
		||||
                                ---------
 | 
			
		||||
                            </option>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new AdminApi(DEFAULT_CONFIG).adminAppsList().then((apps) => {
 | 
			
		||||
                                    return apps.map((app) => {
 | 
			
		||||
                                        return html`<option
 | 
			
		||||
                                            value=${app.name}
 | 
			
		||||
                                            ?selected=${this.instance?.app === app.name}
 | 
			
		||||
                                        >
 | 
			
		||||
                                            ${app.label}
 | 
			
		||||
                                        </option>`;
 | 
			
		||||
                                    });
 | 
			
		||||
                                }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.apps?.map((app) => {
 | 
			
		||||
                                return html`<option
 | 
			
		||||
                                    value=${app.name}
 | 
			
		||||
                                    ?selected=${this.instance?.app === app.name}
 | 
			
		||||
                                >
 | 
			
		||||
                                    ${app.label}
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Match events created by selected application. When left empty, all applications are matched.`}
 | 
			
		||||
 | 
			
		||||
@ -11,9 +11,8 @@ import "@goauthentik/elements/utils/TimeDeltaHelp";
 | 
			
		||||
import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
import { customElement, state } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    CertificateKeyPair,
 | 
			
		||||
@ -26,6 +25,8 @@ import {
 | 
			
		||||
    FlowsInstancesListRequest,
 | 
			
		||||
    IssuerModeEnum,
 | 
			
		||||
    OAuth2Provider,
 | 
			
		||||
    PaginatedOAuthSourceList,
 | 
			
		||||
    PaginatedScopeMappingList,
 | 
			
		||||
    PropertymappingsApi,
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
    SourcesApi,
 | 
			
		||||
@ -34,19 +35,31 @@ import {
 | 
			
		||||
 | 
			
		||||
@customElement("ak-provider-oauth2-form")
 | 
			
		||||
export class OAuth2ProviderFormPage extends ModelForm<OAuth2Provider, number> {
 | 
			
		||||
    loadInstance(pk: number): Promise<OAuth2Provider> {
 | 
			
		||||
        return new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
            .providersOauth2Retrieve({
 | 
			
		||||
                id: pk,
 | 
			
		||||
            })
 | 
			
		||||
            .then((provider) => {
 | 
			
		||||
                this.showClientSecret = provider.clientType === ClientTypeEnum.Confidential;
 | 
			
		||||
                return provider;
 | 
			
		||||
            });
 | 
			
		||||
    propertyMappings?: PaginatedScopeMappingList;
 | 
			
		||||
    oauthSources?: PaginatedOAuthSourceList;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    showClientSecret = true;
 | 
			
		||||
 | 
			
		||||
    async loadInstance(pk: number): Promise<OAuth2Provider> {
 | 
			
		||||
        const provider = await new ProvidersApi(DEFAULT_CONFIG).providersOauth2Retrieve({
 | 
			
		||||
            id: pk,
 | 
			
		||||
        });
 | 
			
		||||
        this.showClientSecret = provider.clientType === ClientTypeEnum.Confidential;
 | 
			
		||||
        return provider;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @property({ type: Boolean })
 | 
			
		||||
    showClientSecret = true;
 | 
			
		||||
    async load(): Promise<void> {
 | 
			
		||||
        this.propertyMappings = await new PropertymappingsApi(
 | 
			
		||||
            DEFAULT_CONFIG,
 | 
			
		||||
        ).propertymappingsScopeList({
 | 
			
		||||
            ordering: "scope_name",
 | 
			
		||||
        });
 | 
			
		||||
        this.oauthSources = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthList({
 | 
			
		||||
            ordering: "name",
 | 
			
		||||
            hasJwks: true,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getSuccessMessage(): string {
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
@ -287,36 +300,27 @@ ${this.instance?.redirectUris}</textarea
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal label=${t`Scopes`} name="propertyMappings">
 | 
			
		||||
                        <select class="pf-c-form-control" multiple>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new PropertymappingsApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .propertymappingsScopeList({
 | 
			
		||||
                                        ordering: "scope_name",
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((scopes) => {
 | 
			
		||||
                                        return scopes.results.map((scope) => {
 | 
			
		||||
                                            let selected = false;
 | 
			
		||||
                                            if (!this.instance?.propertyMappings) {
 | 
			
		||||
                                                selected =
 | 
			
		||||
                                                    scope.managed?.startsWith(
 | 
			
		||||
                                                        "goauthentik.io/providers/oauth2/scope-",
 | 
			
		||||
                                                    ) || false;
 | 
			
		||||
                                            } else {
 | 
			
		||||
                                                selected = Array.from(
 | 
			
		||||
                                                    this.instance?.propertyMappings,
 | 
			
		||||
                                                ).some((su) => {
 | 
			
		||||
                                                    return su == scope.pk;
 | 
			
		||||
                                                });
 | 
			
		||||
                                            }
 | 
			
		||||
                                            return html`<option
 | 
			
		||||
                                                value=${ifDefined(scope.pk)}
 | 
			
		||||
                                                ?selected=${selected}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${scope.name}
 | 
			
		||||
                                            </option>`;
 | 
			
		||||
                                        });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.propertyMappings?.results.map((scope) => {
 | 
			
		||||
                                let selected = false;
 | 
			
		||||
                                if (!this.instance?.propertyMappings) {
 | 
			
		||||
                                    selected =
 | 
			
		||||
                                        scope.managed?.startsWith(
 | 
			
		||||
                                            "goauthentik.io/providers/oauth2/scope-",
 | 
			
		||||
                                        ) || false;
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    selected = Array.from(this.instance?.propertyMappings).some(
 | 
			
		||||
                                        (su) => {
 | 
			
		||||
                                            return su == scope.pk;
 | 
			
		||||
                                        },
 | 
			
		||||
                                    );
 | 
			
		||||
                                }
 | 
			
		||||
                                return html`<option
 | 
			
		||||
                                    value=${ifDefined(scope.pk)}
 | 
			
		||||
                                    ?selected=${selected}
 | 
			
		||||
                                >
 | 
			
		||||
                                    ${scope.name}
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Select which scopes can be used by the client. The client still has to specify the scope to access the data.`}
 | 
			
		||||
@ -413,29 +417,14 @@ ${this.instance?.redirectUris}</textarea
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-form-element-horizontal label=${t`Trusted OIDC Sources`} name="jwksSources">
 | 
			
		||||
                        <select class="pf-c-form-control" multiple>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new SourcesApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .sourcesOauthList({
 | 
			
		||||
                                        ordering: "name",
 | 
			
		||||
                                        hasJwks: true,
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((sources) => {
 | 
			
		||||
                                        return sources.results.map((source) => {
 | 
			
		||||
                                            const selected = (
 | 
			
		||||
                                                this.instance?.jwksSources || []
 | 
			
		||||
                                            ).some((su) => {
 | 
			
		||||
                                                return su == source.pk;
 | 
			
		||||
                                            });
 | 
			
		||||
                                            return html`<option
 | 
			
		||||
                                                value=${source.pk}
 | 
			
		||||
                                                ?selected=${selected}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${source.name} (${source.slug})
 | 
			
		||||
                                            </option>`;
 | 
			
		||||
                                        });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.oauthSources?.results.map((source) => {
 | 
			
		||||
                                const selected = (this.instance?.jwksSources || []).some((su) => {
 | 
			
		||||
                                    return su == source.pk;
 | 
			
		||||
                                });
 | 
			
		||||
                                return html`<option value=${source.pk} ?selected=${selected}>
 | 
			
		||||
                                    ${source.name} (${source.slug})
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.`}
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,6 @@ import { CSSResult, css } from "lit";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, state } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
 | 
			
		||||
import PFList from "@patternfly/patternfly/components/List/list.css";
 | 
			
		||||
@ -28,6 +27,8 @@ import {
 | 
			
		||||
    FlowsApi,
 | 
			
		||||
    FlowsInstancesListDesignationEnum,
 | 
			
		||||
    FlowsInstancesListRequest,
 | 
			
		||||
    PaginatedOAuthSourceList,
 | 
			
		||||
    PaginatedScopeMappingList,
 | 
			
		||||
    PropertymappingsApi,
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
    ProxyMode,
 | 
			
		||||
@ -51,18 +52,30 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loadInstance(pk: number): Promise<ProxyProvider> {
 | 
			
		||||
        return new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
            .providersProxyRetrieve({
 | 
			
		||||
                id: pk,
 | 
			
		||||
            })
 | 
			
		||||
            .then((provider) => {
 | 
			
		||||
                this.showHttpBasic = first(provider.basicAuthEnabled, true);
 | 
			
		||||
                this.mode = first(provider.mode, ProxyMode.Proxy);
 | 
			
		||||
                return provider;
 | 
			
		||||
            });
 | 
			
		||||
    async loadInstance(pk: number): Promise<ProxyProvider> {
 | 
			
		||||
        const provider = await new ProvidersApi(DEFAULT_CONFIG).providersProxyRetrieve({
 | 
			
		||||
            id: pk,
 | 
			
		||||
        });
 | 
			
		||||
        this.showHttpBasic = first(provider.basicAuthEnabled, true);
 | 
			
		||||
        this.mode = first(provider.mode, ProxyMode.Proxy);
 | 
			
		||||
        return provider;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async load(): Promise<void> {
 | 
			
		||||
        this.propertyMappings = await new PropertymappingsApi(
 | 
			
		||||
            DEFAULT_CONFIG,
 | 
			
		||||
        ).propertymappingsScopeList({
 | 
			
		||||
            ordering: "scope_name",
 | 
			
		||||
        });
 | 
			
		||||
        this.oauthSources = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthList({
 | 
			
		||||
            ordering: "name",
 | 
			
		||||
            hasJwks: true,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    propertyMappings?: PaginatedScopeMappingList;
 | 
			
		||||
    oauthSources?: PaginatedOAuthSourceList;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    showHttpBasic = true;
 | 
			
		||||
 | 
			
		||||
@ -392,34 +405,23 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
 | 
			
		||||
                        name="propertyMappings"
 | 
			
		||||
                    >
 | 
			
		||||
                        <select class="pf-c-form-control" multiple>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new PropertymappingsApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .propertymappingsScopeList({
 | 
			
		||||
                                        ordering: "scope_name",
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((scopes) => {
 | 
			
		||||
                                        return scopes.results
 | 
			
		||||
                                            .filter((scope) => {
 | 
			
		||||
                                                return !scope.managed?.startsWith(
 | 
			
		||||
                                                    "goauthentik.io/providers",
 | 
			
		||||
                                                );
 | 
			
		||||
                                            })
 | 
			
		||||
                                            .map((scope) => {
 | 
			
		||||
                                                const selected = (
 | 
			
		||||
                                                    this.instance?.propertyMappings || []
 | 
			
		||||
                                                ).some((su) => {
 | 
			
		||||
                                                    return su == scope.pk;
 | 
			
		||||
                                                });
 | 
			
		||||
                                                return html`<option
 | 
			
		||||
                                                    value=${ifDefined(scope.pk)}
 | 
			
		||||
                                                    ?selected=${selected}
 | 
			
		||||
                                                >
 | 
			
		||||
                                                    ${scope.name}
 | 
			
		||||
                                                </option>`;
 | 
			
		||||
                                            });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.propertyMappings?.results
 | 
			
		||||
                                .filter((scope) => {
 | 
			
		||||
                                    return !scope.managed?.startsWith("goauthentik.io/providers");
 | 
			
		||||
                                })
 | 
			
		||||
                                .map((scope) => {
 | 
			
		||||
                                    const selected = (this.instance?.propertyMappings || []).some(
 | 
			
		||||
                                        (su) => {
 | 
			
		||||
                                            return su == scope.pk;
 | 
			
		||||
                                        },
 | 
			
		||||
                                    );
 | 
			
		||||
                                    return html`<option
 | 
			
		||||
                                        value=${ifDefined(scope.pk)}
 | 
			
		||||
                                        ?selected=${selected}
 | 
			
		||||
                                    >
 | 
			
		||||
                                        ${scope.name}
 | 
			
		||||
                                    </option>`;
 | 
			
		||||
                                })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Additional scope mappings, which are passed to the proxy.`}
 | 
			
		||||
@ -497,29 +499,14 @@ ${this.instance?.skipPathRegex}</textarea
 | 
			
		||||
                    ${this.showHttpBasic ? this.renderHttpBasic() : html``}
 | 
			
		||||
                    <ak-form-element-horizontal label=${t`Trusted OIDC Sources`} name="jwksSources">
 | 
			
		||||
                        <select class="pf-c-form-control" multiple>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new SourcesApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .sourcesOauthList({
 | 
			
		||||
                                        ordering: "name",
 | 
			
		||||
                                        hasJwks: true,
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((sources) => {
 | 
			
		||||
                                        return sources.results.map((source) => {
 | 
			
		||||
                                            const selected = (
 | 
			
		||||
                                                this.instance?.jwksSources || []
 | 
			
		||||
                                            ).some((su) => {
 | 
			
		||||
                                                return su == source.pk;
 | 
			
		||||
                                            });
 | 
			
		||||
                                            return html`<option
 | 
			
		||||
                                                value=${source.pk}
 | 
			
		||||
                                                ?selected=${selected}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${source.name} (${source.slug})
 | 
			
		||||
                                            </option>`;
 | 
			
		||||
                                        });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.oauthSources?.results.map((source) => {
 | 
			
		||||
                                const selected = (this.instance?.jwksSources || []).some((su) => {
 | 
			
		||||
                                    return su == source.pk;
 | 
			
		||||
                                });
 | 
			
		||||
                                return html`<option value=${source.pk} ?selected=${selected}>
 | 
			
		||||
                                    ${source.name} (${source.slug})
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.`}
 | 
			
		||||
 | 
			
		||||
@ -9,9 +9,9 @@ import "@goauthentik/elements/forms/SearchSelect";
 | 
			
		||||
 | 
			
		||||
import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { customElement } from "lit-element";
 | 
			
		||||
import { TemplateResult, html } from "lit-html";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { ifDefined } from "lit-html/directives/if-defined.js";
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    Flow,
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,8 @@ import "@goauthentik/elements/events/ObjectChangelog";
 | 
			
		||||
 | 
			
		||||
import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { CSSResult, TemplateResult, customElement, html, property } from "lit-element";
 | 
			
		||||
import { CSSResult, TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
 | 
			
		||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,6 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    CertificateKeyPair,
 | 
			
		||||
@ -23,6 +22,7 @@ import {
 | 
			
		||||
    FlowsApi,
 | 
			
		||||
    FlowsInstancesListDesignationEnum,
 | 
			
		||||
    FlowsInstancesListRequest,
 | 
			
		||||
    PaginatedSAMLPropertyMappingList,
 | 
			
		||||
    PropertymappingsApi,
 | 
			
		||||
    PropertymappingsSamlListRequest,
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
@ -40,6 +40,16 @@ export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async load(): Promise<void> {
 | 
			
		||||
        this.propertyMappings = await new PropertymappingsApi(
 | 
			
		||||
            DEFAULT_CONFIG,
 | 
			
		||||
        ).propertymappingsSamlList({
 | 
			
		||||
            ordering: "saml_name",
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    propertyMappings?: PaginatedSAMLPropertyMappingList;
 | 
			
		||||
 | 
			
		||||
    getSuccessMessage(): string {
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
            return t`Successfully updated provider.`;
 | 
			
		||||
@ -241,36 +251,27 @@ export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
 | 
			
		||||
                        name="propertyMappings"
 | 
			
		||||
                    >
 | 
			
		||||
                        <select class="pf-c-form-control" multiple>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new PropertymappingsApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .propertymappingsSamlList({
 | 
			
		||||
                                        ordering: "saml_name",
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((mappings) => {
 | 
			
		||||
                                        return mappings.results.map((mapping) => {
 | 
			
		||||
                                            let selected = false;
 | 
			
		||||
                                            if (!this.instance?.propertyMappings) {
 | 
			
		||||
                                                selected =
 | 
			
		||||
                                                    mapping.managed?.startsWith(
 | 
			
		||||
                                                        "goauthentik.io/providers/saml",
 | 
			
		||||
                                                    ) || false;
 | 
			
		||||
                                            } else {
 | 
			
		||||
                                                selected = Array.from(
 | 
			
		||||
                                                    this.instance?.propertyMappings,
 | 
			
		||||
                                                ).some((su) => {
 | 
			
		||||
                                                    return su == mapping.pk;
 | 
			
		||||
                                                });
 | 
			
		||||
                                            }
 | 
			
		||||
                                            return html`<option
 | 
			
		||||
                                                value=${ifDefined(mapping.pk)}
 | 
			
		||||
                                                ?selected=${selected}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${mapping.name}
 | 
			
		||||
                                            </option>`;
 | 
			
		||||
                                        });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.propertyMappings?.results.map((mapping) => {
 | 
			
		||||
                                let selected = false;
 | 
			
		||||
                                if (!this.instance?.propertyMappings) {
 | 
			
		||||
                                    selected =
 | 
			
		||||
                                        mapping.managed?.startsWith(
 | 
			
		||||
                                            "goauthentik.io/providers/saml",
 | 
			
		||||
                                        ) || false;
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    selected = Array.from(this.instance?.propertyMappings).some(
 | 
			
		||||
                                        (su) => {
 | 
			
		||||
                                            return su == mapping.pk;
 | 
			
		||||
                                        },
 | 
			
		||||
                                    );
 | 
			
		||||
                                }
 | 
			
		||||
                                return html`<option
 | 
			
		||||
                                    value=${ifDefined(mapping.pk)}
 | 
			
		||||
                                    ?selected=${selected}
 | 
			
		||||
                                >
 | 
			
		||||
                                    ${mapping.name}
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Hold control/command to select multiple items.`}
 | 
			
		||||
 | 
			
		||||
@ -11,12 +11,12 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    CoreApi,
 | 
			
		||||
    CoreGroupsListRequest,
 | 
			
		||||
    Group,
 | 
			
		||||
    PaginatedSCIMMappingList,
 | 
			
		||||
    PropertymappingsApi,
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
    SCIMProvider,
 | 
			
		||||
@ -30,6 +30,16 @@ export class SCIMProviderFormPage extends ModelForm<SCIMProvider, number> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async load(): Promise<void> {
 | 
			
		||||
        this.propertyMappings = await new PropertymappingsApi(
 | 
			
		||||
            DEFAULT_CONFIG,
 | 
			
		||||
        ).propertymappingsScimList({
 | 
			
		||||
            ordering: "managed",
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    propertyMappings?: PaginatedSCIMMappingList;
 | 
			
		||||
 | 
			
		||||
    getSuccessMessage(): string {
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
            return t`Successfully updated provider.`;
 | 
			
		||||
@ -147,36 +157,26 @@ export class SCIMProviderFormPage extends ModelForm<SCIMProvider, number> {
 | 
			
		||||
                        name="propertyMappings"
 | 
			
		||||
                    >
 | 
			
		||||
                        <select class="pf-c-form-control" multiple>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new PropertymappingsApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .propertymappingsScimList({
 | 
			
		||||
                                        ordering: "managed",
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((mappings) => {
 | 
			
		||||
                                        return mappings.results.map((mapping) => {
 | 
			
		||||
                                            let selected = false;
 | 
			
		||||
                                            if (!this.instance?.propertyMappings) {
 | 
			
		||||
                                                selected =
 | 
			
		||||
                                                    mapping.managed ===
 | 
			
		||||
                                                        "goauthentik.io/providers/scim/user" ||
 | 
			
		||||
                                                    false;
 | 
			
		||||
                                            } else {
 | 
			
		||||
                                                selected = Array.from(
 | 
			
		||||
                                                    this.instance?.propertyMappings,
 | 
			
		||||
                                                ).some((su) => {
 | 
			
		||||
                                                    return su == mapping.pk;
 | 
			
		||||
                                                });
 | 
			
		||||
                                            }
 | 
			
		||||
                                            return html`<option
 | 
			
		||||
                                                value=${ifDefined(mapping.pk)}
 | 
			
		||||
                                                ?selected=${selected}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${mapping.name}
 | 
			
		||||
                                            </option>`;
 | 
			
		||||
                                        });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.propertyMappings?.results.map((mapping) => {
 | 
			
		||||
                                let selected = false;
 | 
			
		||||
                                if (!this.instance?.propertyMappings) {
 | 
			
		||||
                                    selected =
 | 
			
		||||
                                        mapping.managed === "goauthentik.io/providers/scim/user" ||
 | 
			
		||||
                                        false;
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    selected = Array.from(this.instance?.propertyMappings).some(
 | 
			
		||||
                                        (su) => {
 | 
			
		||||
                                            return su == mapping.pk;
 | 
			
		||||
                                        },
 | 
			
		||||
                                    );
 | 
			
		||||
                                }
 | 
			
		||||
                                return html`<option
 | 
			
		||||
                                    value=${ifDefined(mapping.pk)}
 | 
			
		||||
                                    ?selected=${selected}
 | 
			
		||||
                                >
 | 
			
		||||
                                    ${mapping.name}
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Property mappings used to user mapping.`}
 | 
			
		||||
@ -191,35 +191,25 @@ export class SCIMProviderFormPage extends ModelForm<SCIMProvider, number> {
 | 
			
		||||
                        name="propertyMappingsGroup"
 | 
			
		||||
                    >
 | 
			
		||||
                        <select class="pf-c-form-control" multiple>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new PropertymappingsApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .propertymappingsScimList({
 | 
			
		||||
                                        ordering: "managed",
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((mappings) => {
 | 
			
		||||
                                        return mappings.results.map((mapping) => {
 | 
			
		||||
                                            let selected = false;
 | 
			
		||||
                                            if (!this.instance?.propertyMappingsGroup) {
 | 
			
		||||
                                                selected =
 | 
			
		||||
                                                    mapping.managed ===
 | 
			
		||||
                                                    "goauthentik.io/providers/scim/group";
 | 
			
		||||
                                            } else {
 | 
			
		||||
                                                selected = Array.from(
 | 
			
		||||
                                                    this.instance?.propertyMappingsGroup,
 | 
			
		||||
                                                ).some((su) => {
 | 
			
		||||
                                                    return su == mapping.pk;
 | 
			
		||||
                                                });
 | 
			
		||||
                                            }
 | 
			
		||||
                                            return html`<option
 | 
			
		||||
                                                value=${ifDefined(mapping.pk)}
 | 
			
		||||
                                                ?selected=${selected}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${mapping.name}
 | 
			
		||||
                                            </option>`;
 | 
			
		||||
                                        });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.propertyMappings?.results.map((mapping) => {
 | 
			
		||||
                                let selected = false;
 | 
			
		||||
                                if (!this.instance?.propertyMappingsGroup) {
 | 
			
		||||
                                    selected =
 | 
			
		||||
                                        mapping.managed === "goauthentik.io/providers/scim/group";
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    selected = Array.from(
 | 
			
		||||
                                        this.instance?.propertyMappingsGroup,
 | 
			
		||||
                                    ).some((su) => {
 | 
			
		||||
                                        return su == mapping.pk;
 | 
			
		||||
                                    });
 | 
			
		||||
                                }
 | 
			
		||||
                                return html`<option
 | 
			
		||||
                                    value=${ifDefined(mapping.pk)}
 | 
			
		||||
                                    ?selected=${selected}
 | 
			
		||||
                                >
 | 
			
		||||
                                    ${mapping.name}
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Property mappings used to group creation.`}
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,6 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    CertificateKeyPair,
 | 
			
		||||
@ -21,6 +20,7 @@ import {
 | 
			
		||||
    Group,
 | 
			
		||||
    LDAPSource,
 | 
			
		||||
    LDAPSourceRequest,
 | 
			
		||||
    PaginatedLDAPPropertyMappingList,
 | 
			
		||||
    PropertymappingsApi,
 | 
			
		||||
    SourcesApi,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
@ -33,6 +33,16 @@ export class LDAPSourceForm extends ModelForm<LDAPSource, string> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async load(): Promise<void> {
 | 
			
		||||
        this.propertyMappings = await new PropertymappingsApi(
 | 
			
		||||
            DEFAULT_CONFIG,
 | 
			
		||||
        ).propertymappingsLdapList({
 | 
			
		||||
            ordering: "managed,object_field",
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    propertyMappings?: PaginatedLDAPPropertyMappingList;
 | 
			
		||||
 | 
			
		||||
    getSuccessMessage(): string {
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
            return t`Successfully updated source.`;
 | 
			
		||||
@ -241,40 +251,31 @@ export class LDAPSourceForm extends ModelForm<LDAPSource, string> {
 | 
			
		||||
                        name="propertyMappings"
 | 
			
		||||
                    >
 | 
			
		||||
                        <select class="pf-c-form-control" multiple>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new PropertymappingsApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .propertymappingsLdapList({
 | 
			
		||||
                                        ordering: "managed,object_field",
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((mappings) => {
 | 
			
		||||
                                        return mappings.results.map((mapping) => {
 | 
			
		||||
                                            let selected = false;
 | 
			
		||||
                                            if (!this.instance?.propertyMappings) {
 | 
			
		||||
                                                selected =
 | 
			
		||||
                                                    mapping.managed?.startsWith(
 | 
			
		||||
                                                        "goauthentik.io/sources/ldap/default",
 | 
			
		||||
                                                    ) ||
 | 
			
		||||
                                                    mapping.managed?.startsWith(
 | 
			
		||||
                                                        "goauthentik.io/sources/ldap/ms",
 | 
			
		||||
                                                    ) ||
 | 
			
		||||
                                                    false;
 | 
			
		||||
                                            } else {
 | 
			
		||||
                                                selected = Array.from(
 | 
			
		||||
                                                    this.instance?.propertyMappings,
 | 
			
		||||
                                                ).some((su) => {
 | 
			
		||||
                                                    return su == mapping.pk;
 | 
			
		||||
                                                });
 | 
			
		||||
                                            }
 | 
			
		||||
                                            return html`<option
 | 
			
		||||
                                                value=${ifDefined(mapping.pk)}
 | 
			
		||||
                                                ?selected=${selected}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${mapping.name}
 | 
			
		||||
                                            </option>`;
 | 
			
		||||
                                        });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.propertyMappings?.results.map((mapping) => {
 | 
			
		||||
                                let selected = false;
 | 
			
		||||
                                if (!this.instance?.propertyMappings) {
 | 
			
		||||
                                    selected =
 | 
			
		||||
                                        mapping.managed?.startsWith(
 | 
			
		||||
                                            "goauthentik.io/sources/ldap/default",
 | 
			
		||||
                                        ) ||
 | 
			
		||||
                                        mapping.managed?.startsWith(
 | 
			
		||||
                                            "goauthentik.io/sources/ldap/ms",
 | 
			
		||||
                                        ) ||
 | 
			
		||||
                                        false;
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    selected = Array.from(this.instance?.propertyMappings).some(
 | 
			
		||||
                                        (su) => {
 | 
			
		||||
                                            return su == mapping.pk;
 | 
			
		||||
                                        },
 | 
			
		||||
                                    );
 | 
			
		||||
                                }
 | 
			
		||||
                                return html`<option
 | 
			
		||||
                                    value=${ifDefined(mapping.pk)}
 | 
			
		||||
                                    ?selected=${selected}
 | 
			
		||||
                                >
 | 
			
		||||
                                    ${mapping.name}
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Property mappings used to user creation.`}
 | 
			
		||||
@ -289,35 +290,26 @@ export class LDAPSourceForm extends ModelForm<LDAPSource, string> {
 | 
			
		||||
                        name="propertyMappingsGroup"
 | 
			
		||||
                    >
 | 
			
		||||
                        <select class="pf-c-form-control" multiple>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new PropertymappingsApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .propertymappingsLdapList({
 | 
			
		||||
                                        ordering: "managed,object_field",
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((mappings) => {
 | 
			
		||||
                                        return mappings.results.map((mapping) => {
 | 
			
		||||
                                            let selected = false;
 | 
			
		||||
                                            if (!this.instance?.propertyMappingsGroup) {
 | 
			
		||||
                                                selected =
 | 
			
		||||
                                                    mapping.managed ===
 | 
			
		||||
                                                    "goauthentik.io/sources/ldap/default-name";
 | 
			
		||||
                                            } else {
 | 
			
		||||
                                                selected = Array.from(
 | 
			
		||||
                                                    this.instance?.propertyMappingsGroup,
 | 
			
		||||
                                                ).some((su) => {
 | 
			
		||||
                                                    return su == mapping.pk;
 | 
			
		||||
                                                });
 | 
			
		||||
                                            }
 | 
			
		||||
                                            return html`<option
 | 
			
		||||
                                                value=${ifDefined(mapping.pk)}
 | 
			
		||||
                                                ?selected=${selected}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${mapping.name}
 | 
			
		||||
                                            </option>`;
 | 
			
		||||
                                        });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.propertyMappings?.results.map((mapping) => {
 | 
			
		||||
                                let selected = false;
 | 
			
		||||
                                if (!this.instance?.propertyMappingsGroup) {
 | 
			
		||||
                                    selected =
 | 
			
		||||
                                        mapping.managed ===
 | 
			
		||||
                                        "goauthentik.io/sources/ldap/default-name";
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    selected = Array.from(
 | 
			
		||||
                                        this.instance?.propertyMappingsGroup,
 | 
			
		||||
                                    ).some((su) => {
 | 
			
		||||
                                        return su == mapping.pk;
 | 
			
		||||
                                    });
 | 
			
		||||
                                }
 | 
			
		||||
                                return html`<option
 | 
			
		||||
                                    value=${ifDefined(mapping.pk)}
 | 
			
		||||
                                    ?selected=${selected}
 | 
			
		||||
                                >
 | 
			
		||||
                                    ${mapping.name}
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Property mappings used to group creation.`}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
 | 
			
		||||
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
 | 
			
		||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
 | 
			
		||||
import { first } from "@goauthentik/common/utils";
 | 
			
		||||
import { rootInterface } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@goauthentik/elements/CodeMirror";
 | 
			
		||||
import "@goauthentik/elements/forms/FormGroup";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
@ -13,7 +14,6 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    CapabilitiesEnum,
 | 
			
		||||
@ -315,63 +315,52 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
 | 
			
		||||
                    ${t`Path template for users created. Use placeholders like \`%(slug)s\` to insert the source slug.`}
 | 
			
		||||
                </p>
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            ${until(
 | 
			
		||||
                config().then((c) => {
 | 
			
		||||
                    if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
 | 
			
		||||
                        return html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                                <input type="file" value="" class="pf-c-form-control" />
 | 
			
		||||
                                ${this.instance?.icon
 | 
			
		||||
                                    ? html`
 | 
			
		||||
                                          <p class="pf-c-form__helper-text">
 | 
			
		||||
                                              ${t`Currently set to:`} ${this.instance?.icon}
 | 
			
		||||
                                          </p>
 | 
			
		||||
                                      `
 | 
			
		||||
                                    : html``}
 | 
			
		||||
                            </ak-form-element-horizontal>
 | 
			
		||||
                            ${this.instance?.icon
 | 
			
		||||
                                ? html`
 | 
			
		||||
                                      <ak-form-element-horizontal>
 | 
			
		||||
                                          <label class="pf-c-switch">
 | 
			
		||||
                                              <input
 | 
			
		||||
                                                  class="pf-c-switch__input"
 | 
			
		||||
                                                  type="checkbox"
 | 
			
		||||
                                                  @change=${(ev: Event) => {
 | 
			
		||||
                                                      const target = ev.target as HTMLInputElement;
 | 
			
		||||
                                                      this.clearIcon = target.checked;
 | 
			
		||||
                                                  }}
 | 
			
		||||
                                              />
 | 
			
		||||
                                              <span class="pf-c-switch__toggle">
 | 
			
		||||
                                                  <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                      <i
 | 
			
		||||
                                                          class="fas fa-check"
 | 
			
		||||
                                                          aria-hidden="true"
 | 
			
		||||
                                                      ></i>
 | 
			
		||||
                                                  </span>
 | 
			
		||||
                                              </span>
 | 
			
		||||
                                              <span class="pf-c-switch__label">
 | 
			
		||||
                                                  ${t`Clear icon`}
 | 
			
		||||
                                              </span>
 | 
			
		||||
                                          </label>
 | 
			
		||||
 | 
			
		||||
                                          <p class="pf-c-form__helper-text">
 | 
			
		||||
                                              ${t`Delete currently set icon.`}
 | 
			
		||||
                                          </p>
 | 
			
		||||
                                      </ak-form-element-horizontal>
 | 
			
		||||
                                  `
 | 
			
		||||
                                : html``}`;
 | 
			
		||||
                    }
 | 
			
		||||
                    return html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                        <input
 | 
			
		||||
                            type="text"
 | 
			
		||||
                            value="${first(this.instance?.icon, "")}"
 | 
			
		||||
                            class="pf-c-form-control"
 | 
			
		||||
                        />
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".`}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>`;
 | 
			
		||||
                }),
 | 
			
		||||
            )}
 | 
			
		||||
            ${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.SaveMedia)
 | 
			
		||||
                ? html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                          <input type="file" value="" class="pf-c-form-control" />
 | 
			
		||||
                          ${this.instance?.icon
 | 
			
		||||
                              ? html`
 | 
			
		||||
                                    <p class="pf-c-form__helper-text">
 | 
			
		||||
                                        ${t`Currently set to:`} ${this.instance?.icon}
 | 
			
		||||
                                    </p>
 | 
			
		||||
                                `
 | 
			
		||||
                              : html``}
 | 
			
		||||
                      </ak-form-element-horizontal>
 | 
			
		||||
                      ${this.instance?.icon
 | 
			
		||||
                          ? html`
 | 
			
		||||
                                <ak-form-element-horizontal>
 | 
			
		||||
                                    <label class="pf-c-switch">
 | 
			
		||||
                                        <input
 | 
			
		||||
                                            class="pf-c-switch__input"
 | 
			
		||||
                                            type="checkbox"
 | 
			
		||||
                                            @change=${(ev: Event) => {
 | 
			
		||||
                                                const target = ev.target as HTMLInputElement;
 | 
			
		||||
                                                this.clearIcon = target.checked;
 | 
			
		||||
                                            }}
 | 
			
		||||
                                        />
 | 
			
		||||
                                        <span class="pf-c-switch__toggle">
 | 
			
		||||
                                            <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                <i class="fas fa-check" aria-hidden="true"></i>
 | 
			
		||||
                                            </span>
 | 
			
		||||
                                        </span>
 | 
			
		||||
                                        <span class="pf-c-switch__label"> ${t`Clear icon`} </span>
 | 
			
		||||
                                    </label>
 | 
			
		||||
                                    <p class="pf-c-form__helper-text">
 | 
			
		||||
                                        ${t`Delete currently set icon.`}
 | 
			
		||||
                                    </p>
 | 
			
		||||
                                </ak-form-element-horizontal>
 | 
			
		||||
                            `
 | 
			
		||||
                          : html``}`
 | 
			
		||||
                : html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                      <input
 | 
			
		||||
                          type="text"
 | 
			
		||||
                          value="${first(this.instance?.icon, "")}"
 | 
			
		||||
                          class="pf-c-form-control"
 | 
			
		||||
                      />
 | 
			
		||||
                      <p class="pf-c-form__helper-text">
 | 
			
		||||
                          ${t`Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".`}
 | 
			
		||||
                      </p>
 | 
			
		||||
                  </ak-form-element-horizontal>`}
 | 
			
		||||
 | 
			
		||||
            <ak-form-group .expanded=${true}>
 | 
			
		||||
                <span slot="header"> ${t`Protocol settings`} </span>
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils"
 | 
			
		||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
 | 
			
		||||
import { PlexAPIClient, PlexResource, popupCenterScreen } from "@goauthentik/common/helpers/plex";
 | 
			
		||||
import { first, randomString } from "@goauthentik/common/utils";
 | 
			
		||||
import { rootInterface } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@goauthentik/elements/forms/FormGroup";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
 | 
			
		||||
@ -13,7 +14,6 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    CapabilitiesEnum,
 | 
			
		||||
@ -267,63 +267,52 @@ export class PlexSourceForm extends ModelForm<PlexSource, string> {
 | 
			
		||||
                    ${t`Path template for users created. Use placeholders like \`%(slug)s\` to insert the source slug.`}
 | 
			
		||||
                </p>
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            ${until(
 | 
			
		||||
                config().then((c) => {
 | 
			
		||||
                    if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
 | 
			
		||||
                        return html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                                <input type="file" value="" class="pf-c-form-control" />
 | 
			
		||||
                                ${this.instance?.icon
 | 
			
		||||
                                    ? html`
 | 
			
		||||
                                          <p class="pf-c-form__helper-text">
 | 
			
		||||
                                              ${t`Currently set to:`} ${this.instance?.icon}
 | 
			
		||||
                                          </p>
 | 
			
		||||
                                      `
 | 
			
		||||
                                    : html``}
 | 
			
		||||
                            </ak-form-element-horizontal>
 | 
			
		||||
                            ${this.instance?.icon
 | 
			
		||||
                                ? html`
 | 
			
		||||
                                      <ak-form-element-horizontal>
 | 
			
		||||
                                          <label class="pf-c-switch">
 | 
			
		||||
                                              <input
 | 
			
		||||
                                                  class="pf-c-switch__input"
 | 
			
		||||
                                                  type="checkbox"
 | 
			
		||||
                                                  @change=${(ev: Event) => {
 | 
			
		||||
                                                      const target = ev.target as HTMLInputElement;
 | 
			
		||||
                                                      this.clearIcon = target.checked;
 | 
			
		||||
                                                  }}
 | 
			
		||||
                                              />
 | 
			
		||||
                                              <span class="pf-c-switch__toggle">
 | 
			
		||||
                                                  <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                      <i
 | 
			
		||||
                                                          class="fas fa-check"
 | 
			
		||||
                                                          aria-hidden="true"
 | 
			
		||||
                                                      ></i>
 | 
			
		||||
                                                  </span>
 | 
			
		||||
                                              </span>
 | 
			
		||||
                                              <span class="pf-c-switch__label">
 | 
			
		||||
                                                  ${t`Clear icon`}
 | 
			
		||||
                                              </span>
 | 
			
		||||
                                          </label>
 | 
			
		||||
                                          <p class="pf-c-form__helper-text">
 | 
			
		||||
                                              ${t`Delete currently set icon.`}
 | 
			
		||||
                                          </p>
 | 
			
		||||
                                      </ak-form-element-horizontal>
 | 
			
		||||
                                  `
 | 
			
		||||
                                : html``}`;
 | 
			
		||||
                    }
 | 
			
		||||
                    return html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                        <input
 | 
			
		||||
                            type="text"
 | 
			
		||||
                            value="${first(this.instance?.icon, "")}"
 | 
			
		||||
                            class="pf-c-form-control"
 | 
			
		||||
                        />
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".`}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>`;
 | 
			
		||||
                }),
 | 
			
		||||
            )}
 | 
			
		||||
 | 
			
		||||
            ${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.SaveMedia)
 | 
			
		||||
                ? html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                          <input type="file" value="" class="pf-c-form-control" />
 | 
			
		||||
                          ${this.instance?.icon
 | 
			
		||||
                              ? html`
 | 
			
		||||
                                    <p class="pf-c-form__helper-text">
 | 
			
		||||
                                        ${t`Currently set to:`} ${this.instance?.icon}
 | 
			
		||||
                                    </p>
 | 
			
		||||
                                `
 | 
			
		||||
                              : html``}
 | 
			
		||||
                      </ak-form-element-horizontal>
 | 
			
		||||
                      ${this.instance?.icon
 | 
			
		||||
                          ? html`
 | 
			
		||||
                                <ak-form-element-horizontal>
 | 
			
		||||
                                    <label class="pf-c-switch">
 | 
			
		||||
                                        <input
 | 
			
		||||
                                            class="pf-c-switch__input"
 | 
			
		||||
                                            type="checkbox"
 | 
			
		||||
                                            @change=${(ev: Event) => {
 | 
			
		||||
                                                const target = ev.target as HTMLInputElement;
 | 
			
		||||
                                                this.clearIcon = target.checked;
 | 
			
		||||
                                            }}
 | 
			
		||||
                                        />
 | 
			
		||||
                                        <span class="pf-c-switch__toggle">
 | 
			
		||||
                                            <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                <i class="fas fa-check" aria-hidden="true"></i>
 | 
			
		||||
                                            </span>
 | 
			
		||||
                                        </span>
 | 
			
		||||
                                        <span class="pf-c-switch__label"> ${t`Clear icon`} </span>
 | 
			
		||||
                                    </label>
 | 
			
		||||
                                    <p class="pf-c-form__helper-text">
 | 
			
		||||
                                        ${t`Delete currently set icon.`}
 | 
			
		||||
                                    </p>
 | 
			
		||||
                                </ak-form-element-horizontal>
 | 
			
		||||
                            `
 | 
			
		||||
                          : html``}`
 | 
			
		||||
                : html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                      <input
 | 
			
		||||
                          type="text"
 | 
			
		||||
                          value="${first(this.instance?.icon, "")}"
 | 
			
		||||
                          class="pf-c-form-control"
 | 
			
		||||
                      />
 | 
			
		||||
                      <p class="pf-c-form__helper-text">
 | 
			
		||||
                          ${t`Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".`}
 | 
			
		||||
                      </p>
 | 
			
		||||
                  </ak-form-element-horizontal>`}
 | 
			
		||||
            <ak-form-group .expanded=${true}>
 | 
			
		||||
                <span slot="header"> ${t`Protocol settings`} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
 | 
			
		||||
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
 | 
			
		||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
 | 
			
		||||
import { first } from "@goauthentik/common/utils";
 | 
			
		||||
import { rootInterface } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@goauthentik/elements/forms/FormGroup";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
 | 
			
		||||
@ -13,7 +14,6 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, state } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    BindingTypeEnum,
 | 
			
		||||
@ -161,62 +161,52 @@ export class SAMLSourceForm extends ModelForm<SAMLSource, string> {
 | 
			
		||||
                    </option>
 | 
			
		||||
                </select>
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            ${until(
 | 
			
		||||
                config().then((c) => {
 | 
			
		||||
                    if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
 | 
			
		||||
                        return html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                                <input type="file" value="" class="pf-c-form-control" />
 | 
			
		||||
                                ${this.instance?.icon
 | 
			
		||||
                                    ? html`
 | 
			
		||||
                                          <p class="pf-c-form__helper-text">
 | 
			
		||||
                                              ${t`Currently set to:`} ${this.instance?.icon}
 | 
			
		||||
                                          </p>
 | 
			
		||||
                                      `
 | 
			
		||||
                                    : html``}
 | 
			
		||||
                            </ak-form-element-horizontal>
 | 
			
		||||
                            ${this.instance?.icon
 | 
			
		||||
                                ? html`
 | 
			
		||||
                                      <ak-form-element-horizontal>
 | 
			
		||||
                                          <label class="pf-c-switch">
 | 
			
		||||
                                              <input
 | 
			
		||||
                                                  class="pf-c-switch__input"
 | 
			
		||||
                                                  type="checkbox"
 | 
			
		||||
                                                  @change=${(ev: Event) => {
 | 
			
		||||
                                                      const target = ev.target as HTMLInputElement;
 | 
			
		||||
                                                      this.clearIcon = target.checked;
 | 
			
		||||
                                                  }}
 | 
			
		||||
                                              />
 | 
			
		||||
                                              <span class="pf-c-switch__toggle">
 | 
			
		||||
                                                  <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                      <i
 | 
			
		||||
                                                          class="fas fa-check"
 | 
			
		||||
                                                          aria-hidden="true"
 | 
			
		||||
                                                      ></i>
 | 
			
		||||
                                                  </span>
 | 
			
		||||
                                              </span>
 | 
			
		||||
                                              <span class="pf-c-switch__label">
 | 
			
		||||
                                                  ${t`Clear icon`}
 | 
			
		||||
                                              </span>
 | 
			
		||||
                                          </label>
 | 
			
		||||
                                          <p class="pf-c-form__helper-text">
 | 
			
		||||
                                              ${t`Delete currently set icon.`}
 | 
			
		||||
                                          </p>
 | 
			
		||||
                                      </ak-form-element-horizontal>
 | 
			
		||||
                                  `
 | 
			
		||||
                                : html``}`;
 | 
			
		||||
                    }
 | 
			
		||||
                    return html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                        <input
 | 
			
		||||
                            type="text"
 | 
			
		||||
                            value="${first(this.instance?.icon, "")}"
 | 
			
		||||
                            class="pf-c-form-control"
 | 
			
		||||
                        />
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".`}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>`;
 | 
			
		||||
                }),
 | 
			
		||||
            )}
 | 
			
		||||
            ${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.SaveMedia)
 | 
			
		||||
                ? html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                          <input type="file" value="" class="pf-c-form-control" />
 | 
			
		||||
                          ${this.instance?.icon
 | 
			
		||||
                              ? html`
 | 
			
		||||
                                    <p class="pf-c-form__helper-text">
 | 
			
		||||
                                        ${t`Currently set to:`} ${this.instance?.icon}
 | 
			
		||||
                                    </p>
 | 
			
		||||
                                `
 | 
			
		||||
                              : html``}
 | 
			
		||||
                      </ak-form-element-horizontal>
 | 
			
		||||
                      ${this.instance?.icon
 | 
			
		||||
                          ? html`
 | 
			
		||||
                                <ak-form-element-horizontal>
 | 
			
		||||
                                    <label class="pf-c-switch">
 | 
			
		||||
                                        <input
 | 
			
		||||
                                            class="pf-c-switch__input"
 | 
			
		||||
                                            type="checkbox"
 | 
			
		||||
                                            @change=${(ev: Event) => {
 | 
			
		||||
                                                const target = ev.target as HTMLInputElement;
 | 
			
		||||
                                                this.clearIcon = target.checked;
 | 
			
		||||
                                            }}
 | 
			
		||||
                                        />
 | 
			
		||||
                                        <span class="pf-c-switch__toggle">
 | 
			
		||||
                                            <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                <i class="fas fa-check" aria-hidden="true"></i>
 | 
			
		||||
                                            </span>
 | 
			
		||||
                                        </span>
 | 
			
		||||
                                        <span class="pf-c-switch__label"> ${t`Clear icon`} </span>
 | 
			
		||||
                                    </label>
 | 
			
		||||
                                    <p class="pf-c-form__helper-text">
 | 
			
		||||
                                        ${t`Delete currently set icon.`}
 | 
			
		||||
                                    </p>
 | 
			
		||||
                                </ak-form-element-horizontal>
 | 
			
		||||
                            `
 | 
			
		||||
                          : html``}`
 | 
			
		||||
                : html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
 | 
			
		||||
                      <input
 | 
			
		||||
                          type="text"
 | 
			
		||||
                          value="${first(this.instance?.icon, "")}"
 | 
			
		||||
                          class="pf-c-form-control"
 | 
			
		||||
                      />
 | 
			
		||||
                      <p class="pf-c-form__helper-text">
 | 
			
		||||
                          ${t`Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".`}
 | 
			
		||||
                      </p>
 | 
			
		||||
                  </ak-form-element-horizontal>`}
 | 
			
		||||
 | 
			
		||||
            <ak-form-group .expanded=${true}>
 | 
			
		||||
                <span slot="header"> ${t`Protocol settings`} </span>
 | 
			
		||||
 | 
			
		||||
@ -10,30 +10,35 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    AuthenticatorValidateStage,
 | 
			
		||||
    DeviceClassesEnum,
 | 
			
		||||
    NotConfiguredActionEnum,
 | 
			
		||||
    PaginatedStageList,
 | 
			
		||||
    StagesApi,
 | 
			
		||||
    UserVerificationEnum,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-stage-authenticator-validate-form")
 | 
			
		||||
export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValidateStage, string> {
 | 
			
		||||
    loadInstance(pk: string): Promise<AuthenticatorValidateStage> {
 | 
			
		||||
        return new StagesApi(DEFAULT_CONFIG)
 | 
			
		||||
            .stagesAuthenticatorValidateRetrieve({
 | 
			
		||||
                stageUuid: pk,
 | 
			
		||||
            })
 | 
			
		||||
            .then((stage) => {
 | 
			
		||||
                this.showConfigurationStages =
 | 
			
		||||
                    stage.notConfiguredAction === NotConfiguredActionEnum.Configure;
 | 
			
		||||
                return stage;
 | 
			
		||||
            });
 | 
			
		||||
    async loadInstance(pk: string): Promise<AuthenticatorValidateStage> {
 | 
			
		||||
        const stage = await new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorValidateRetrieve({
 | 
			
		||||
            stageUuid: pk,
 | 
			
		||||
        });
 | 
			
		||||
        this.showConfigurationStages =
 | 
			
		||||
            stage.notConfiguredAction === NotConfiguredActionEnum.Configure;
 | 
			
		||||
        return stage;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async load(): Promise<void> {
 | 
			
		||||
        this.stages = await new StagesApi(DEFAULT_CONFIG).stagesAllList({
 | 
			
		||||
            ordering: "name",
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    stages?: PaginatedStageList;
 | 
			
		||||
 | 
			
		||||
    @property({ type: Boolean })
 | 
			
		||||
    showConfigurationStages = true;
 | 
			
		||||
 | 
			
		||||
@ -216,28 +221,19 @@ export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValid
 | 
			
		||||
                                  name="configurationStages"
 | 
			
		||||
                              >
 | 
			
		||||
                                  <select class="pf-c-form-control" multiple>
 | 
			
		||||
                                      ${until(
 | 
			
		||||
                                          new StagesApi(DEFAULT_CONFIG)
 | 
			
		||||
                                              .stagesAllList({
 | 
			
		||||
                                                  ordering: "name",
 | 
			
		||||
                                              })
 | 
			
		||||
                                              .then((stages) => {
 | 
			
		||||
                                                  return stages.results.map((stage) => {
 | 
			
		||||
                                                      const selected = Array.from(
 | 
			
		||||
                                                          this.instance?.configurationStages || [],
 | 
			
		||||
                                                      ).some((su) => {
 | 
			
		||||
                                                          return su == stage.pk;
 | 
			
		||||
                                                      });
 | 
			
		||||
                                                      return html`<option
 | 
			
		||||
                                                          value=${ifDefined(stage.pk)}
 | 
			
		||||
                                                          ?selected=${selected}
 | 
			
		||||
                                                      >
 | 
			
		||||
                                                          ${stage.name} (${stage.verboseName})
 | 
			
		||||
                                                      </option>`;
 | 
			
		||||
                                                  });
 | 
			
		||||
                                              }),
 | 
			
		||||
                                          html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                                      )}
 | 
			
		||||
                                      ${this.stages?.results.map((stage) => {
 | 
			
		||||
                                          const selected = Array.from(
 | 
			
		||||
                                              this.instance?.configurationStages || [],
 | 
			
		||||
                                          ).some((su) => {
 | 
			
		||||
                                              return su == stage.pk;
 | 
			
		||||
                                          });
 | 
			
		||||
                                          return html`<option
 | 
			
		||||
                                              value=${ifDefined(stage.pk)}
 | 
			
		||||
                                              ?selected=${selected}
 | 
			
		||||
                                          >
 | 
			
		||||
                                              ${stage.name} (${stage.verboseName})
 | 
			
		||||
                                          </option>`;
 | 
			
		||||
                                      })}
 | 
			
		||||
                                  </select>
 | 
			
		||||
                                  <p class="pf-c-form__helper-text">
 | 
			
		||||
                                      ${t`Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again.`}
 | 
			
		||||
 | 
			
		||||
@ -9,23 +9,25 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import { EmailStage, StagesApi } from "@goauthentik/api";
 | 
			
		||||
import { EmailStage, StagesApi, TypeCreate } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-stage-email-form")
 | 
			
		||||
export class EmailStageForm extends ModelForm<EmailStage, string> {
 | 
			
		||||
    loadInstance(pk: string): Promise<EmailStage> {
 | 
			
		||||
        return new StagesApi(DEFAULT_CONFIG)
 | 
			
		||||
            .stagesEmailRetrieve({
 | 
			
		||||
                stageUuid: pk,
 | 
			
		||||
            })
 | 
			
		||||
            .then((stage) => {
 | 
			
		||||
                this.showConnectionSettings = !stage.useGlobalSettings;
 | 
			
		||||
                return stage;
 | 
			
		||||
            });
 | 
			
		||||
    async loadInstance(pk: string): Promise<EmailStage> {
 | 
			
		||||
        const stage = await new StagesApi(DEFAULT_CONFIG).stagesEmailRetrieve({
 | 
			
		||||
            stageUuid: pk,
 | 
			
		||||
        });
 | 
			
		||||
        this.showConnectionSettings = !stage.useGlobalSettings;
 | 
			
		||||
        return stage;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async load(): Promise<void> {
 | 
			
		||||
        this.templates = await new StagesApi(DEFAULT_CONFIG).stagesEmailTemplatesList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    templates?: TypeCreate[];
 | 
			
		||||
 | 
			
		||||
    @property({ type: Boolean })
 | 
			
		||||
    showConnectionSettings = false;
 | 
			
		||||
 | 
			
		||||
@ -232,23 +234,15 @@ export class EmailStageForm extends ModelForm<EmailStage, string> {
 | 
			
		||||
                        name="template"
 | 
			
		||||
                    >
 | 
			
		||||
                        <select name="users" class="pf-c-form-control">
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new StagesApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .stagesEmailTemplatesList()
 | 
			
		||||
                                    .then((templates) => {
 | 
			
		||||
                                        return templates.map((template) => {
 | 
			
		||||
                                            const selected =
 | 
			
		||||
                                                this.instance?.template === template.name;
 | 
			
		||||
                                            return html`<option
 | 
			
		||||
                                                value=${ifDefined(template.name)}
 | 
			
		||||
                                                ?selected=${selected}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${template.description}
 | 
			
		||||
                                            </option>`;
 | 
			
		||||
                                        });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.templates?.map((template) => {
 | 
			
		||||
                                const selected = this.instance?.template === template.name;
 | 
			
		||||
                                return html`<option
 | 
			
		||||
                                    value=${ifDefined(template.name)}
 | 
			
		||||
                                    ?selected=${selected}
 | 
			
		||||
                                >
 | 
			
		||||
                                    ${template.description}
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,6 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    Flow,
 | 
			
		||||
@ -19,6 +18,7 @@ import {
 | 
			
		||||
    FlowsInstancesListDesignationEnum,
 | 
			
		||||
    FlowsInstancesListRequest,
 | 
			
		||||
    IdentificationStage,
 | 
			
		||||
    PaginatedSourceList,
 | 
			
		||||
    SourcesApi,
 | 
			
		||||
    Stage,
 | 
			
		||||
    StagesApi,
 | 
			
		||||
@ -34,6 +34,14 @@ export class IdentificationStageForm extends ModelForm<IdentificationStage, stri
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async load(): Promise<void> {
 | 
			
		||||
        this.sources = await new SourcesApi(DEFAULT_CONFIG).sourcesAllList({
 | 
			
		||||
            ordering: "slug",
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sources?: PaginatedSourceList;
 | 
			
		||||
 | 
			
		||||
    getSuccessMessage(): string {
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
            return t`Successfully updated stage.`;
 | 
			
		||||
@ -80,7 +88,7 @@ export class IdentificationStageForm extends ModelForm<IdentificationStage, stri
 | 
			
		||||
                <span slot="header"> ${t`Stage-specific settings`} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-form-element-horizontal label=${t`User fields`} name="userFields">
 | 
			
		||||
                        <select name="users" class="pf-c-form-control" multiple>
 | 
			
		||||
                        <select class="pf-c-form-control" multiple>
 | 
			
		||||
                            <option
 | 
			
		||||
                                value=${UserFieldsEnum.Username}
 | 
			
		||||
                                ?selected=${this.isUserFieldSelected(UserFieldsEnum.Username)}
 | 
			
		||||
@ -187,35 +195,28 @@ export class IdentificationStageForm extends ModelForm<IdentificationStage, stri
 | 
			
		||||
                        name="sources"
 | 
			
		||||
                    >
 | 
			
		||||
                        <select class="pf-c-form-control" multiple>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new SourcesApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .sourcesAllList({})
 | 
			
		||||
                                    .then((sources) => {
 | 
			
		||||
                                        return sources.results.map((source) => {
 | 
			
		||||
                                            let selected = Array.from(
 | 
			
		||||
                                                this.instance?.sources || [],
 | 
			
		||||
                                            ).some((su) => {
 | 
			
		||||
                                                return su == source.pk;
 | 
			
		||||
                                            });
 | 
			
		||||
                                            // Creating a new instance, auto-select built-in source
 | 
			
		||||
                                            // Only when no other sources exist
 | 
			
		||||
                                            if (
 | 
			
		||||
                                                !this.instance &&
 | 
			
		||||
                                                source.component === "" &&
 | 
			
		||||
                                                sources.results.length < 2
 | 
			
		||||
                                            ) {
 | 
			
		||||
                                                selected = true;
 | 
			
		||||
                                            }
 | 
			
		||||
                                            return html`<option
 | 
			
		||||
                                                value=${ifDefined(source.pk)}
 | 
			
		||||
                                                ?selected=${selected}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${source.name}
 | 
			
		||||
                                            </option>`;
 | 
			
		||||
                                        });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.sources?.results.map((source) => {
 | 
			
		||||
                                let selected = Array.from(this.instance?.sources || []).some(
 | 
			
		||||
                                    (su) => {
 | 
			
		||||
                                        return su == source.pk;
 | 
			
		||||
                                    },
 | 
			
		||||
                                );
 | 
			
		||||
                                // Creating a new instance, auto-select built-in source
 | 
			
		||||
                                // Only when no other sources exist
 | 
			
		||||
                                if (
 | 
			
		||||
                                    !this.instance &&
 | 
			
		||||
                                    source.component === "" &&
 | 
			
		||||
                                    (this.sources?.results || []).length < 2
 | 
			
		||||
                                ) {
 | 
			
		||||
                                    selected = true;
 | 
			
		||||
                                }
 | 
			
		||||
                                return html`<option
 | 
			
		||||
                                    value=${ifDefined(source.pk)}
 | 
			
		||||
                                    ?selected=${selected}
 | 
			
		||||
                                >
 | 
			
		||||
                                    ${source.name}
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Select sources should be shown for users to authenticate with. This only affects web-based sources, not LDAP.`}
 | 
			
		||||
 | 
			
		||||
@ -10,9 +10,14 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import { PoliciesApi, PromptStage, StagesApi } from "@goauthentik/api";
 | 
			
		||||
import {
 | 
			
		||||
    PaginatedPolicyList,
 | 
			
		||||
    PaginatedPromptList,
 | 
			
		||||
    PoliciesApi,
 | 
			
		||||
    PromptStage,
 | 
			
		||||
    StagesApi,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-stage-prompt-form")
 | 
			
		||||
export class PromptStageForm extends ModelForm<PromptStage, string> {
 | 
			
		||||
@ -22,6 +27,18 @@ export class PromptStageForm extends ModelForm<PromptStage, string> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async load(): Promise<void> {
 | 
			
		||||
        this.prompts = await new StagesApi(DEFAULT_CONFIG).stagesPromptPromptsList({
 | 
			
		||||
            ordering: "field_name",
 | 
			
		||||
        });
 | 
			
		||||
        this.policies = await new PoliciesApi(DEFAULT_CONFIG).policiesAllList({
 | 
			
		||||
            ordering: "name",
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    prompts?: PaginatedPromptList;
 | 
			
		||||
    policies?: PaginatedPolicyList;
 | 
			
		||||
 | 
			
		||||
    getSuccessMessage(): string {
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
            return t`Successfully updated stage.`;
 | 
			
		||||
@ -61,28 +78,19 @@ export class PromptStageForm extends ModelForm<PromptStage, string> {
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-form-element-horizontal label=${t`Fields`} ?required=${true} name="fields">
 | 
			
		||||
                        <select name="users" class="pf-c-form-control" multiple>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new StagesApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .stagesPromptPromptsList({
 | 
			
		||||
                                        ordering: "field_name",
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((prompts) => {
 | 
			
		||||
                                        return prompts.results.map((prompt) => {
 | 
			
		||||
                                            const selected = Array.from(
 | 
			
		||||
                                                this.instance?.fields || [],
 | 
			
		||||
                                            ).some((su) => {
 | 
			
		||||
                                                return su == prompt.pk;
 | 
			
		||||
                                            });
 | 
			
		||||
                                            return html`<option
 | 
			
		||||
                                                value=${ifDefined(prompt.pk)}
 | 
			
		||||
                                                ?selected=${selected}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${t`${prompt.name} ("${prompt.fieldKey}", of type ${prompt.type})`}
 | 
			
		||||
                                            </option>`;
 | 
			
		||||
                                        });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.prompts?.results.map((prompt) => {
 | 
			
		||||
                                const selected = Array.from(this.instance?.fields || []).some(
 | 
			
		||||
                                    (su) => {
 | 
			
		||||
                                        return su == prompt.pk;
 | 
			
		||||
                                    },
 | 
			
		||||
                                );
 | 
			
		||||
                                return html`<option
 | 
			
		||||
                                    value=${ifDefined(prompt.pk)}
 | 
			
		||||
                                    ?selected=${selected}
 | 
			
		||||
                                >
 | 
			
		||||
                                    ${t`${prompt.name} ("${prompt.fieldKey}", of type ${prompt.type})`}
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Hold control/command to select multiple items.`}
 | 
			
		||||
@ -101,28 +109,19 @@ export class PromptStageForm extends ModelForm<PromptStage, string> {
 | 
			
		||||
                        name="validationPolicies"
 | 
			
		||||
                    >
 | 
			
		||||
                        <select name="users" class="pf-c-form-control" multiple>
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new PoliciesApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .policiesAllList({
 | 
			
		||||
                                        ordering: "name",
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((policies) => {
 | 
			
		||||
                                        return policies.results.map((policy) => {
 | 
			
		||||
                                            const selected = Array.from(
 | 
			
		||||
                                                this.instance?.validationPolicies || [],
 | 
			
		||||
                                            ).some((su) => {
 | 
			
		||||
                                                return su == policy.pk;
 | 
			
		||||
                                            });
 | 
			
		||||
                                            return html`<option
 | 
			
		||||
                                                value=${ifDefined(policy.pk)}
 | 
			
		||||
                                                ?selected=${selected}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${t`${policy.name} (${policy.verboseName})`}
 | 
			
		||||
                                            </option>`;
 | 
			
		||||
                                        });
 | 
			
		||||
                                    }),
 | 
			
		||||
                                html`<option>${t`Loading...`}</option>`,
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.policies?.results.map((policy) => {
 | 
			
		||||
                                const selected = Array.from(
 | 
			
		||||
                                    this.instance?.validationPolicies || [],
 | 
			
		||||
                                ).some((su) => {
 | 
			
		||||
                                    return su == policy.pk;
 | 
			
		||||
                                });
 | 
			
		||||
                                return html`<option
 | 
			
		||||
                                    value=${ifDefined(policy.pk)}
 | 
			
		||||
                                    ?selected=${selected}
 | 
			
		||||
                                >
 | 
			
		||||
                                    ${t`${policy.name} (${policy.verboseName})`}
 | 
			
		||||
                                </option>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </select>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${t`Selected policies are executed when the stage is submitted to validate the data.`}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user