web: remove more until (#5057)
* more cleanup Signed-off-by: Jens Langhammer <jens@goauthentik.io> * don't dynamically import duo form Signed-off-by: Jens Langhammer <jens@goauthentik.io> * migrate more Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix import Signed-off-by: Jens Langhammer <jens@goauthentik.io> * properly send evens when tab isn't switched Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix loop on tabs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * migrate more Signed-off-by: Jens Langhammer <jens@goauthentik.io> * don't bubble tab events Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove most other uses of until() Signed-off-by: Jens Langhammer <jens@goauthentik.io> * cleanup user settings Signed-off-by: Jens Langhammer <jens@goauthentik.io> * only use stale for issues Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		@ -1,3 +1,4 @@
 | 
			
		||||
import { AdminInterface } from "@goauthentik/admin/AdminInterface";
 | 
			
		||||
import "@goauthentik/admin/admin-overview/TopApplicationsTable";
 | 
			
		||||
import "@goauthentik/admin/admin-overview/cards/AdminStatusCard";
 | 
			
		||||
import "@goauthentik/admin/admin-overview/cards/RecentEventsCard";
 | 
			
		||||
@ -8,8 +9,7 @@ import "@goauthentik/admin/admin-overview/charts/AdminLoginAuthorizeChart";
 | 
			
		||||
import "@goauthentik/admin/admin-overview/charts/OutpostStatusChart";
 | 
			
		||||
import "@goauthentik/admin/admin-overview/charts/SyncStatusChart";
 | 
			
		||||
import { VERSION } from "@goauthentik/common/constants";
 | 
			
		||||
import { me } from "@goauthentik/common/users";
 | 
			
		||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
			
		||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@goauthentik/elements/PageHeader";
 | 
			
		||||
import "@goauthentik/elements/cards/AggregatePromiseCard";
 | 
			
		||||
import { paramURL } from "@goauthentik/elements/router/RouterOutlet";
 | 
			
		||||
@ -17,15 +17,13 @@ import { paramURL } from "@goauthentik/elements/router/RouterOutlet";
 | 
			
		||||
import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { CSSResult, TemplateResult, css, html } from "lit";
 | 
			
		||||
import { customElement, state } from "lit/decorators.js";
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
 | 
			
		||||
import PFList from "@patternfly/patternfly/components/List/list.css";
 | 
			
		||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
 | 
			
		||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
 | 
			
		||||
 | 
			
		||||
import { SessionUser } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
export function versionFamily(): string {
 | 
			
		||||
    const parts = VERSION.split(".");
 | 
			
		||||
    parts.pop();
 | 
			
		||||
@ -58,17 +56,11 @@ export class AdminOverviewPage extends AKElement {
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    user?: SessionUser;
 | 
			
		||||
 | 
			
		||||
    async firstUpdated(): Promise<void> {
 | 
			
		||||
        this.user = await me();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render(): TemplateResult {
 | 
			
		||||
        let name = this.user?.user.username;
 | 
			
		||||
        if (this.user?.user.name) {
 | 
			
		||||
            name = this.user.user.name;
 | 
			
		||||
        const user = rootInterface<AdminInterface>()?.user;
 | 
			
		||||
        let name = user?.user.username;
 | 
			
		||||
        if (user?.user.name) {
 | 
			
		||||
            name = user.user.name;
 | 
			
		||||
        }
 | 
			
		||||
        return html`<ak-page-header icon="" header="" description=${t`General system status`}>
 | 
			
		||||
                <span slot="header"> ${t`Welcome, ${name}.`} </span>
 | 
			
		||||
 | 
			
		||||
@ -18,13 +18,12 @@ import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { CSSResult } from "lit";
 | 
			
		||||
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 PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
 | 
			
		||||
 | 
			
		||||
import { Outpost, OutpostTypeEnum, OutpostsApi } from "@goauthentik/api";
 | 
			
		||||
import { Outpost, OutpostHealth, OutpostTypeEnum, OutpostsApi } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
export function TypeToLabel(type?: OutpostTypeEnum): string {
 | 
			
		||||
    if (!type) return "";
 | 
			
		||||
@ -56,14 +55,31 @@ export class OutpostListPage extends TablePage<Outpost> {
 | 
			
		||||
    searchEnabled(): boolean {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<Outpost>> {
 | 
			
		||||
        return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesList({
 | 
			
		||||
        const outposts = await new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesList({
 | 
			
		||||
            ordering: this.order,
 | 
			
		||||
            page: page,
 | 
			
		||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
			
		||||
            search: this.search || "",
 | 
			
		||||
        });
 | 
			
		||||
        Promise.all(
 | 
			
		||||
            outposts.results.map((outpost) => {
 | 
			
		||||
                return new OutpostsApi(DEFAULT_CONFIG)
 | 
			
		||||
                    .outpostsInstancesHealthList({
 | 
			
		||||
                        uuid: outpost.pk,
 | 
			
		||||
                    })
 | 
			
		||||
                    .then((health) => {
 | 
			
		||||
                        this.health[outpost.pk] = health;
 | 
			
		||||
                    });
 | 
			
		||||
            }),
 | 
			
		||||
        );
 | 
			
		||||
        return outposts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    health: { [key: string]: OutpostHealth[] } = {};
 | 
			
		||||
 | 
			
		||||
    columns(): TableColumn[] {
 | 
			
		||||
        return [
 | 
			
		||||
            new TableColumn(t`Name`, "name"),
 | 
			
		||||
@ -136,25 +152,15 @@ export class OutpostListPage extends TablePage<Outpost> {
 | 
			
		||||
                    ${t`Detailed health (one instance per column, data is cached so may be out of date)`}
 | 
			
		||||
                </h3>
 | 
			
		||||
                <dl class="pf-c-description-list pf-m-3-col-on-lg">
 | 
			
		||||
                    ${until(
 | 
			
		||||
                        new OutpostsApi(DEFAULT_CONFIG)
 | 
			
		||||
                            .outpostsInstancesHealthList({
 | 
			
		||||
                                uuid: item.pk,
 | 
			
		||||
                            })
 | 
			
		||||
                            .then((health) => {
 | 
			
		||||
                                return health.map((h) => {
 | 
			
		||||
                                    return html` <div class="pf-c-description-list__group">
 | 
			
		||||
                                        <dd class="pf-c-description-list__description">
 | 
			
		||||
                                            <div class="pf-c-description-list__text">
 | 
			
		||||
                                                <ak-outpost-health
 | 
			
		||||
                                                    .outpostHealth=${h}
 | 
			
		||||
                                                ></ak-outpost-health>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </dd>
 | 
			
		||||
                                    </div>`;
 | 
			
		||||
                                });
 | 
			
		||||
                            }),
 | 
			
		||||
                    )}
 | 
			
		||||
                    ${this.health[item.pk].map((h) => {
 | 
			
		||||
                        return html`<div class="pf-c-description-list__group">
 | 
			
		||||
                            <dd class="pf-c-description-list__description">
 | 
			
		||||
                                <div class="pf-c-description-list__text">
 | 
			
		||||
                                    <ak-outpost-health .outpostHealth=${h}></ak-outpost-health>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </dd>
 | 
			
		||||
                        </div>`;
 | 
			
		||||
                    })}
 | 
			
		||||
                </dl>
 | 
			
		||||
            </div>
 | 
			
		||||
        </td>`;
 | 
			
		||||
 | 
			
		||||
@ -16,11 +16,10 @@ import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
			
		||||
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 { OutpostsApi, ServiceConnection } from "@goauthentik/api";
 | 
			
		||||
import { OutpostsApi, ServiceConnection, ServiceConnectionState } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-outpost-service-connection-list")
 | 
			
		||||
export class OutpostServiceConnectionListPage extends TablePage<ServiceConnection> {
 | 
			
		||||
@ -40,14 +39,31 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
 | 
			
		||||
    checkbox = true;
 | 
			
		||||
 | 
			
		||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<ServiceConnection>> {
 | 
			
		||||
        return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllList({
 | 
			
		||||
            ordering: this.order,
 | 
			
		||||
            page: page,
 | 
			
		||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
			
		||||
            search: this.search || "",
 | 
			
		||||
        });
 | 
			
		||||
        const connections = await new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllList(
 | 
			
		||||
            {
 | 
			
		||||
                ordering: this.order,
 | 
			
		||||
                page: page,
 | 
			
		||||
                pageSize: (await uiConfig()).pagination.perPage,
 | 
			
		||||
                search: this.search || "",
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
        Promise.all(
 | 
			
		||||
            connections.results.map((connection) => {
 | 
			
		||||
                return new OutpostsApi(DEFAULT_CONFIG)
 | 
			
		||||
                    .outpostsServiceConnectionsAllStateRetrieve({
 | 
			
		||||
                        uuid: connection.pk,
 | 
			
		||||
                    })
 | 
			
		||||
                    .then((state) => {
 | 
			
		||||
                        this.state[connection.pk] = state;
 | 
			
		||||
                    });
 | 
			
		||||
            }),
 | 
			
		||||
        );
 | 
			
		||||
        return connections;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    state: { [key: string]: ServiceConnectionState } = {};
 | 
			
		||||
 | 
			
		||||
    columns(): TableColumn[] {
 | 
			
		||||
        return [
 | 
			
		||||
            new TableColumn(t`Name`, "name"),
 | 
			
		||||
@ -62,27 +78,16 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
 | 
			
		||||
    order = "name";
 | 
			
		||||
 | 
			
		||||
    row(item: ServiceConnection): TemplateResult[] {
 | 
			
		||||
        const itemState = this.state[item.pk];
 | 
			
		||||
        return [
 | 
			
		||||
            html`${item.name}`,
 | 
			
		||||
            html`${item.verboseName}`,
 | 
			
		||||
            html`<ak-label color=${item.local ? PFColor.Grey : PFColor.Green}>
 | 
			
		||||
                ${item.local ? t`Yes` : t`No`}
 | 
			
		||||
            </ak-label>`,
 | 
			
		||||
            html`${until(
 | 
			
		||||
                new OutpostsApi(DEFAULT_CONFIG)
 | 
			
		||||
                    .outpostsServiceConnectionsAllStateRetrieve({
 | 
			
		||||
                        uuid: item.pk || "",
 | 
			
		||||
                    })
 | 
			
		||||
                    .then((state) => {
 | 
			
		||||
                        if (state.healthy) {
 | 
			
		||||
                            return html`<ak-label color=${PFColor.Green}
 | 
			
		||||
                                >${ifDefined(state.version)}</ak-label
 | 
			
		||||
                            >`;
 | 
			
		||||
                        }
 | 
			
		||||
                        return html`<ak-label color=${PFColor.Red}>${t`Unhealthy`}</ak-label>`;
 | 
			
		||||
                    }),
 | 
			
		||||
                html`<ak-spinner></ak-spinner>`,
 | 
			
		||||
            )}`,
 | 
			
		||||
            html`${itemState.healthy
 | 
			
		||||
                ? html`<ak-label color=${PFColor.Green}>${ifDefined(itemState.version)}</ak-label>`
 | 
			
		||||
                : html`<ak-label color=${PFColor.Red}>${t`Unhealthy`}</ak-label>`}`,
 | 
			
		||||
            html` <ak-forms-modal>
 | 
			
		||||
                <span slot="submit"> ${t`Update`} </span>
 | 
			
		||||
                <span slot="header"> ${t`Update ${item.verboseName}`} </span>
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ import { convertToTitle } from "@goauthentik/common/utils";
 | 
			
		||||
import MDProviderOAuth2 from "@goauthentik/docs/providers/oauth2/index.md";
 | 
			
		||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@goauthentik/elements/CodeMirror";
 | 
			
		||||
import "@goauthentik/elements/EmptyState";
 | 
			
		||||
import "@goauthentik/elements/Markdown";
 | 
			
		||||
import "@goauthentik/elements/Tabs";
 | 
			
		||||
import "@goauthentik/elements/buttons/ModalButton";
 | 
			
		||||
@ -15,8 +16,7 @@ import "@goauthentik/elements/events/ObjectChangelog";
 | 
			
		||||
import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { CSSResult, TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
 | 
			
		||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
 | 
			
		||||
@ -29,31 +29,35 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css";
 | 
			
		||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
 | 
			
		||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
			
		||||
 | 
			
		||||
import { OAuth2Provider, OAuth2ProviderSetupURLs, ProvidersApi } from "@goauthentik/api";
 | 
			
		||||
import {
 | 
			
		||||
    OAuth2Provider,
 | 
			
		||||
    OAuth2ProviderSetupURLs,
 | 
			
		||||
    PropertyMappingPreview,
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-provider-oauth2-view")
 | 
			
		||||
export class OAuth2ProviderViewPage extends AKElement {
 | 
			
		||||
    @property({ type: Number })
 | 
			
		||||
    set providerID(value: number) {
 | 
			
		||||
        const api = new ProvidersApi(DEFAULT_CONFIG);
 | 
			
		||||
        api.providersOauth2Retrieve({
 | 
			
		||||
            id: value,
 | 
			
		||||
        }).then((prov) => {
 | 
			
		||||
            this.provider = prov;
 | 
			
		||||
        });
 | 
			
		||||
        api.providersOauth2SetupUrlsRetrieve({
 | 
			
		||||
            id: value,
 | 
			
		||||
        }).then((prov) => {
 | 
			
		||||
            this.providerUrls = prov;
 | 
			
		||||
        });
 | 
			
		||||
        new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
            .providersOauth2Retrieve({
 | 
			
		||||
                id: value,
 | 
			
		||||
            })
 | 
			
		||||
            .then((prov) => {
 | 
			
		||||
                this.provider = prov;
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @property({ attribute: false })
 | 
			
		||||
    provider?: OAuth2Provider;
 | 
			
		||||
 | 
			
		||||
    @property({ attribute: false })
 | 
			
		||||
    @state()
 | 
			
		||||
    providerUrls?: OAuth2ProviderSetupURLs;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    preview?: PropertyMappingPreview;
 | 
			
		||||
 | 
			
		||||
    static get styles(): CSSResult[] {
 | 
			
		||||
        return [
 | 
			
		||||
            PFBase,
 | 
			
		||||
@ -82,10 +86,32 @@ export class OAuth2ProviderViewPage extends AKElement {
 | 
			
		||||
            return html``;
 | 
			
		||||
        }
 | 
			
		||||
        return html` <ak-tabs>
 | 
			
		||||
            <section slot="page-overview" data-tab-title="${t`Overview`}">
 | 
			
		||||
            <section
 | 
			
		||||
                slot="page-overview"
 | 
			
		||||
                data-tab-title="${t`Overview`}"
 | 
			
		||||
                @activate=${() => {
 | 
			
		||||
                    new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .providersOauth2SetupUrlsRetrieve({
 | 
			
		||||
                            id: this.provider?.pk || 0,
 | 
			
		||||
                        })
 | 
			
		||||
                        .then((prov) => {
 | 
			
		||||
                            this.providerUrls = prov;
 | 
			
		||||
                        });
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
                ${this.renderTabOverview()}
 | 
			
		||||
            </section>
 | 
			
		||||
            <section slot="page-preview" data-tab-title="${t`Preview`}">
 | 
			
		||||
            <section
 | 
			
		||||
                slot="page-preview"
 | 
			
		||||
                data-tab-title="${t`Preview`}"
 | 
			
		||||
                @activate=${() => {
 | 
			
		||||
                    new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .providersOauth2PreviewUserRetrieve({
 | 
			
		||||
                            id: this.provider?.pk || 0,
 | 
			
		||||
                        })
 | 
			
		||||
                        .then((preview) => (this.preview = preview));
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
                ${this.renderTabPreview()}
 | 
			
		||||
            </section>
 | 
			
		||||
            <section
 | 
			
		||||
@ -318,15 +344,9 @@ export class OAuth2ProviderViewPage extends AKElement {
 | 
			
		||||
                    ${t`Example JWT payload (for currently authenticated user)`}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="pf-c-card__body">
 | 
			
		||||
                    ${until(
 | 
			
		||||
                        new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                            .providersOauth2PreviewUserRetrieve({
 | 
			
		||||
                                id: this.provider?.pk,
 | 
			
		||||
                            })
 | 
			
		||||
                            .then((data) => {
 | 
			
		||||
                                return html`<pre>${JSON.stringify(data.preview, null, 4)}</pre>`;
 | 
			
		||||
                            }),
 | 
			
		||||
                    )}
 | 
			
		||||
                    ${this.preview
 | 
			
		||||
                        ? html`<pre>${JSON.stringify(this.preview?.preview, null, 4)}</pre>`
 | 
			
		||||
                        : html` <ak-empty-state ?loading=${true}></ak-empty-state> `}
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>`;
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ import { EVENT_REFRESH } from "@goauthentik/common/constants";
 | 
			
		||||
import { MessageLevel } from "@goauthentik/common/messages";
 | 
			
		||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@goauthentik/elements/CodeMirror";
 | 
			
		||||
import "@goauthentik/elements/EmptyState";
 | 
			
		||||
import "@goauthentik/elements/Tabs";
 | 
			
		||||
import "@goauthentik/elements/buttons/ActionButton";
 | 
			
		||||
import "@goauthentik/elements/buttons/ModalButton";
 | 
			
		||||
@ -15,9 +16,8 @@ import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
 | 
			
		||||
import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { CSSResult, 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 PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
 | 
			
		||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
 | 
			
		||||
@ -31,7 +31,13 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css";
 | 
			
		||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
 | 
			
		||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
			
		||||
 | 
			
		||||
import { CryptoApi, ProvidersApi, SAMLProvider } from "@goauthentik/api";
 | 
			
		||||
import {
 | 
			
		||||
    CertificateKeyPair,
 | 
			
		||||
    CryptoApi,
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
    SAMLMetadata,
 | 
			
		||||
    SAMLProvider,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
interface SAMLPreviewAttribute {
 | 
			
		||||
    attributes: {
 | 
			
		||||
@ -54,12 +60,40 @@ export class SAMLProviderViewPage extends AKElement {
 | 
			
		||||
            .providersSamlRetrieve({
 | 
			
		||||
                id: value,
 | 
			
		||||
            })
 | 
			
		||||
            .then((prov) => (this.provider = prov));
 | 
			
		||||
            .then((prov) => {
 | 
			
		||||
                this.provider = prov;
 | 
			
		||||
                if (prov.signingKp) {
 | 
			
		||||
                    new CryptoApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .cryptoCertificatekeypairsRetrieve({
 | 
			
		||||
                            kpUuid: prov.signingKp,
 | 
			
		||||
                        })
 | 
			
		||||
                        .then((kp) => (this.signer = kp));
 | 
			
		||||
                }
 | 
			
		||||
                if (prov.verificationKp) {
 | 
			
		||||
                    new CryptoApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .cryptoCertificatekeypairsRetrieve({
 | 
			
		||||
                            kpUuid: prov.verificationKp,
 | 
			
		||||
                        })
 | 
			
		||||
                        .then((kp) => (this.verifier = kp));
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @property({ attribute: false })
 | 
			
		||||
    provider?: SAMLProvider;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    preview?: SAMLPreviewAttribute;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    metadata?: SAMLMetadata;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    signer?: CertificateKeyPair;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    verifier?: CertificateKeyPair;
 | 
			
		||||
 | 
			
		||||
    static get styles(): CSSResult[] {
 | 
			
		||||
        return [
 | 
			
		||||
            PFBase,
 | 
			
		||||
@ -84,7 +118,7 @@ export class SAMLProviderViewPage extends AKElement {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async renderRelatedObjects(): Promise<TemplateResult> {
 | 
			
		||||
    renderRelatedObjects(): TemplateResult {
 | 
			
		||||
        const relatedObjects = [];
 | 
			
		||||
        if (this.provider?.assignedApplicationName) {
 | 
			
		||||
            relatedObjects.push(html`<div class="pf-c-description-list__group">
 | 
			
		||||
@ -122,10 +156,7 @@ export class SAMLProviderViewPage extends AKElement {
 | 
			
		||||
                </dd>
 | 
			
		||||
            </div>`);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.provider?.signingKp) {
 | 
			
		||||
            const kp = await new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsRetrieve({
 | 
			
		||||
                kpUuid: this.provider.signingKp,
 | 
			
		||||
            });
 | 
			
		||||
        if (this.signer) {
 | 
			
		||||
            relatedObjects.push(html`<div class="pf-c-description-list__group">
 | 
			
		||||
                <dt class="pf-c-description-list__term">
 | 
			
		||||
                    <span class="pf-c-description-list__text"
 | 
			
		||||
@ -134,7 +165,9 @@ export class SAMLProviderViewPage extends AKElement {
 | 
			
		||||
                </dt>
 | 
			
		||||
                <dd class="pf-c-description-list__description">
 | 
			
		||||
                    <div class="pf-c-description-list__text">
 | 
			
		||||
                        <a class="pf-c-button pf-m-primary" href=${kp.certificateDownloadUrl}
 | 
			
		||||
                        <a
 | 
			
		||||
                            class="pf-c-button pf-m-primary"
 | 
			
		||||
                            href=${this.signer.certificateDownloadUrl}
 | 
			
		||||
                            >${t`Download`}</a
 | 
			
		||||
                        >
 | 
			
		||||
                    </div>
 | 
			
		||||
@ -160,7 +193,19 @@ export class SAMLProviderViewPage extends AKElement {
 | 
			
		||||
                ${this.renderTabOverview()}
 | 
			
		||||
            </section>
 | 
			
		||||
            ${this.renderTabMetadata()}
 | 
			
		||||
            <section slot="page-preview" data-tab-title="${t`Preview`}">
 | 
			
		||||
            <section
 | 
			
		||||
                slot="page-preview"
 | 
			
		||||
                data-tab-title="${t`Preview`}"
 | 
			
		||||
                @activate=${() => {
 | 
			
		||||
                    new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .providersSamlPreviewUserRetrieve({
 | 
			
		||||
                            id: this.provider?.pk || 0,
 | 
			
		||||
                        })
 | 
			
		||||
                        .then((preview) => {
 | 
			
		||||
                            this.preview = preview.preview as SAMLPreviewAttribute;
 | 
			
		||||
                        });
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
                ${this.renderTabPreview()}
 | 
			
		||||
            </section>
 | 
			
		||||
            <section
 | 
			
		||||
@ -264,7 +309,7 @@ export class SAMLProviderViewPage extends AKElement {
 | 
			
		||||
                        </ak-forms-modal>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                ${until(this.renderRelatedObjects())}
 | 
			
		||||
                ${this.renderRelatedObjects()}
 | 
			
		||||
                ${
 | 
			
		||||
                    this.provider.assignedApplicationName
 | 
			
		||||
                        ? html` <div class="pf-c-card pf-l-grid__item pf-m-12-col">
 | 
			
		||||
@ -364,7 +409,17 @@ export class SAMLProviderViewPage extends AKElement {
 | 
			
		||||
        }
 | 
			
		||||
        return html`
 | 
			
		||||
            ${this.provider.assignedApplicationName
 | 
			
		||||
                ? html` <section slot="page-metadata" data-tab-title="${t`Metadata`}">
 | 
			
		||||
                ? html` <section
 | 
			
		||||
                      slot="page-metadata"
 | 
			
		||||
                      data-tab-title="${t`Metadata`}"
 | 
			
		||||
                      @activate=${() => {
 | 
			
		||||
                          new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                              .providersSamlMetadataRetrieve({
 | 
			
		||||
                                  id: this.provider?.pk || 0,
 | 
			
		||||
                              })
 | 
			
		||||
                              .then((metadata) => (this.metadata = metadata));
 | 
			
		||||
                      }}
 | 
			
		||||
                  >
 | 
			
		||||
                      <div
 | 
			
		||||
                          class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter"
 | 
			
		||||
                      >
 | 
			
		||||
@ -399,19 +454,11 @@ export class SAMLProviderViewPage extends AKElement {
 | 
			
		||||
                                  </ak-action-button>
 | 
			
		||||
                              </div>
 | 
			
		||||
                              <div class="pf-c-card__footer">
 | 
			
		||||
                                  ${until(
 | 
			
		||||
                                      new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                                          .providersSamlMetadataRetrieve({
 | 
			
		||||
                                              id: this.provider.pk || 0,
 | 
			
		||||
                                          })
 | 
			
		||||
                                          .then((m) => {
 | 
			
		||||
                                              return html`<ak-codemirror
 | 
			
		||||
                                                  mode="xml"
 | 
			
		||||
                                                  ?readOnly=${true}
 | 
			
		||||
                                                  value="${ifDefined(m.metadata)}"
 | 
			
		||||
                                              ></ak-codemirror>`;
 | 
			
		||||
                                          }),
 | 
			
		||||
                                  )}
 | 
			
		||||
                                  <ak-codemirror
 | 
			
		||||
                                      mode="xml"
 | 
			
		||||
                                      ?readOnly=${true}
 | 
			
		||||
                                      value="${ifDefined(this.metadata?.metadata)}"
 | 
			
		||||
                                  ></ak-codemirror>
 | 
			
		||||
                              </div>
 | 
			
		||||
                          </div>
 | 
			
		||||
                      </div>
 | 
			
		||||
@ -421,65 +468,50 @@ export class SAMLProviderViewPage extends AKElement {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    renderTabPreview(): TemplateResult {
 | 
			
		||||
        if (!this.provider) {
 | 
			
		||||
            return html``;
 | 
			
		||||
        if (!this.preview) {
 | 
			
		||||
            return html`<ak-empty-state ?loading=${true}></ak-empty-state>`;
 | 
			
		||||
        }
 | 
			
		||||
        return html` <div
 | 
			
		||||
            class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter"
 | 
			
		||||
        >
 | 
			
		||||
            <div class="pf-c-card">
 | 
			
		||||
                <div class="pf-c-card__title">${t`Example SAML attributes`}</div>
 | 
			
		||||
                ${until(
 | 
			
		||||
                    new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .providersSamlPreviewUserRetrieve({
 | 
			
		||||
                            id: this.provider?.pk,
 | 
			
		||||
                        })
 | 
			
		||||
                        .then((data) => {
 | 
			
		||||
                            const d = data.preview as SAMLPreviewAttribute;
 | 
			
		||||
                            return html`
 | 
			
		||||
                                <div class="pf-c-card__body">
 | 
			
		||||
                                    <dl class="pf-c-description-list pf-m-2-col-on-lg">
 | 
			
		||||
                                        <div class="pf-c-description-list__group">
 | 
			
		||||
                                            <dt class="pf-c-description-list__term">
 | 
			
		||||
                                                <span class="pf-c-description-list__text"
 | 
			
		||||
                                                    >${t`NameID attribute`}</span
 | 
			
		||||
                                                >
 | 
			
		||||
                                            </dt>
 | 
			
		||||
                                            <dd class="pf-c-description-list__description">
 | 
			
		||||
                                                <div class="pf-c-description-list__text">
 | 
			
		||||
                                                    ${d.nameID}
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                            </dd>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </dl>
 | 
			
		||||
                <div class="pf-c-card__body">
 | 
			
		||||
                    <dl class="pf-c-description-list pf-m-2-col-on-lg">
 | 
			
		||||
                        <div class="pf-c-description-list__group">
 | 
			
		||||
                            <dt class="pf-c-description-list__term">
 | 
			
		||||
                                <span class="pf-c-description-list__text"
 | 
			
		||||
                                    >${t`NameID attribute`}</span
 | 
			
		||||
                                >
 | 
			
		||||
                            </dt>
 | 
			
		||||
                            <dd class="pf-c-description-list__description">
 | 
			
		||||
                                <div class="pf-c-description-list__text">
 | 
			
		||||
                                    ${this.preview?.nameID}
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div class="pf-c-card__body">
 | 
			
		||||
                                    <dl class="pf-c-description-list pf-m-2-col-on-lg">
 | 
			
		||||
                                        ${d.attributes.map((attr) => {
 | 
			
		||||
                                            return html` <div class="pf-c-description-list__group">
 | 
			
		||||
                                                <dt class="pf-c-description-list__term">
 | 
			
		||||
                                                    <span class="pf-c-description-list__text"
 | 
			
		||||
                                                        >${attr.Name}</span
 | 
			
		||||
                                                    >
 | 
			
		||||
                                                </dt>
 | 
			
		||||
                                                <dd class="pf-c-description-list__description">
 | 
			
		||||
                                                    <div class="pf-c-description-list__text">
 | 
			
		||||
                                                        <ul class="pf-c-list">
 | 
			
		||||
                                                            ${attr.Value.map((value) => {
 | 
			
		||||
                                                                return html`
 | 
			
		||||
                                                                    <li><pre>${value}</pre></li>
 | 
			
		||||
                                                                `;
 | 
			
		||||
                                                            })}
 | 
			
		||||
                                                        </ul>
 | 
			
		||||
                                                    </div>
 | 
			
		||||
                                                </dd>
 | 
			
		||||
                                            </div>`;
 | 
			
		||||
                                        })}
 | 
			
		||||
                                    </dl>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            `;
 | 
			
		||||
                        }),
 | 
			
		||||
                )}
 | 
			
		||||
                            </dd>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </dl>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="pf-c-card__body">
 | 
			
		||||
                    <dl class="pf-c-description-list pf-m-2-col-on-lg">
 | 
			
		||||
                        ${this.preview?.attributes.map((attr) => {
 | 
			
		||||
                            return html` <div class="pf-c-description-list__group">
 | 
			
		||||
                                <dt class="pf-c-description-list__term">
 | 
			
		||||
                                    <span class="pf-c-description-list__text">${attr.Name}</span>
 | 
			
		||||
                                </dt>
 | 
			
		||||
                                <dd class="pf-c-description-list__description">
 | 
			
		||||
                                    <div class="pf-c-description-list__text">
 | 
			
		||||
                                        <ul class="pf-c-list">
 | 
			
		||||
                                            ${attr.Value.map((value) => {
 | 
			
		||||
                                                return html` <li><pre>${value}</pre></li> `;
 | 
			
		||||
                                            })}
 | 
			
		||||
                                        </ul>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </dd>
 | 
			
		||||
                            </div>`;
 | 
			
		||||
                        })}
 | 
			
		||||
                    </dl>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
import "@goauthentik/admin/providers/scim/SCIMProviderForm";
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
 | 
			
		||||
import { me } from "@goauthentik/common/users";
 | 
			
		||||
import MDSCIMProvider from "@goauthentik/docs/providers/scim/index.md";
 | 
			
		||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@goauthentik/elements/Markdown";
 | 
			
		||||
@ -14,7 +13,6 @@ import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { CSSResult, TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
 | 
			
		||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
 | 
			
		||||
@ -29,7 +27,7 @@ import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
 | 
			
		||||
import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css";
 | 
			
		||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
			
		||||
 | 
			
		||||
import { ProvidersApi, SCIMProvider, SessionUser } from "@goauthentik/api";
 | 
			
		||||
import { ProvidersApi, SCIMProvider, Task } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-provider-scim-view")
 | 
			
		||||
export class SCIMProviderViewPage extends AKElement {
 | 
			
		||||
@ -51,7 +49,7 @@ export class SCIMProviderViewPage extends AKElement {
 | 
			
		||||
    provider?: SCIMProvider;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    me?: SessionUser;
 | 
			
		||||
    syncState?: Task;
 | 
			
		||||
 | 
			
		||||
    static get styles(): CSSResult[] {
 | 
			
		||||
        return [
 | 
			
		||||
@ -76,9 +74,6 @@ export class SCIMProviderViewPage extends AKElement {
 | 
			
		||||
            if (!this.provider?.pk) return;
 | 
			
		||||
            this.providerID = this.provider?.pk;
 | 
			
		||||
        });
 | 
			
		||||
        me().then((user) => {
 | 
			
		||||
            this.me = user;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render(): TemplateResult {
 | 
			
		||||
@ -86,7 +81,22 @@ export class SCIMProviderViewPage extends AKElement {
 | 
			
		||||
            return html``;
 | 
			
		||||
        }
 | 
			
		||||
        return html` <ak-tabs>
 | 
			
		||||
            <section slot="page-overview" data-tab-title="${t`Overview`}">
 | 
			
		||||
            <section
 | 
			
		||||
                slot="page-overview"
 | 
			
		||||
                data-tab-title="${t`Overview`}"
 | 
			
		||||
                @activate=${() => {
 | 
			
		||||
                    new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .providersScimSyncStatusRetrieve({
 | 
			
		||||
                            id: this.provider?.pk || 0,
 | 
			
		||||
                        })
 | 
			
		||||
                        .then((state) => {
 | 
			
		||||
                            this.syncState = state;
 | 
			
		||||
                        })
 | 
			
		||||
                        .catch(() => {
 | 
			
		||||
                            this.syncState = undefined;
 | 
			
		||||
                        });
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
                ${this.renderTabOverview()}
 | 
			
		||||
            </section>
 | 
			
		||||
            <section
 | 
			
		||||
@ -158,23 +168,13 @@ export class SCIMProviderViewPage extends AKElement {
 | 
			
		||||
                            <p>${t`Sync status`}</p>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="pf-c-card__body">
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .providersScimSyncStatusRetrieve({
 | 
			
		||||
                                        id: this.provider.pk,
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((task) => {
 | 
			
		||||
                                        return html` <ul class="pf-c-list">
 | 
			
		||||
                                            ${task.messages.map((m) => {
 | 
			
		||||
                                                return html`<li>${m}</li>`;
 | 
			
		||||
                                            })}
 | 
			
		||||
                                        </ul>`;
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .catch(() => {
 | 
			
		||||
                                        return html`${t`Sync not run yet.`}`;
 | 
			
		||||
                                    }),
 | 
			
		||||
                                "loading",
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.syncState
 | 
			
		||||
                                ? html` <ul class="pf-c-list">
 | 
			
		||||
                                      ${this.syncState.messages.map((m) => {
 | 
			
		||||
                                          return html`<li>${m}</li>`;
 | 
			
		||||
                                      })}
 | 
			
		||||
                                  </ul>`
 | 
			
		||||
                                : html` ${t`Sync not run yet.`} `}
 | 
			
		||||
                        </div>
 | 
			
		||||
 | 
			
		||||
                        <div class="pf-c-card__footer">
 | 
			
		||||
 | 
			
		||||
@ -12,8 +12,7 @@ import "@goauthentik/elements/forms/ModalForm";
 | 
			
		||||
import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { CSSResult, TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
 | 
			
		||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
 | 
			
		||||
@ -24,7 +23,7 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css";
 | 
			
		||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
 | 
			
		||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
			
		||||
 | 
			
		||||
import { LDAPSource, SourcesApi, TaskStatusEnum } from "@goauthentik/api";
 | 
			
		||||
import { LDAPSource, SourcesApi, Task, TaskStatusEnum } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-source-ldap-view")
 | 
			
		||||
export class LDAPSourceViewPage extends AKElement {
 | 
			
		||||
@ -42,6 +41,9 @@ export class LDAPSourceViewPage extends AKElement {
 | 
			
		||||
    @property({ attribute: false })
 | 
			
		||||
    source!: LDAPSource;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    syncState: Task[] = [];
 | 
			
		||||
 | 
			
		||||
    static get styles(): CSSResult[] {
 | 
			
		||||
        return [PFBase, PFPage, PFButton, PFGrid, PFContent, PFCard, PFDescriptionList, PFList];
 | 
			
		||||
    }
 | 
			
		||||
@ -63,6 +65,15 @@ export class LDAPSourceViewPage extends AKElement {
 | 
			
		||||
                slot="page-overview"
 | 
			
		||||
                data-tab-title="${t`Overview`}"
 | 
			
		||||
                class="pf-c-page__main-section pf-m-no-padding-mobile"
 | 
			
		||||
                @activate=${() => {
 | 
			
		||||
                    new SourcesApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .sourcesLdapSyncStatusList({
 | 
			
		||||
                            slug: this.source.slug,
 | 
			
		||||
                        })
 | 
			
		||||
                        .then((state) => {
 | 
			
		||||
                            this.syncState = state;
 | 
			
		||||
                        });
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
                <div class="pf-l-grid pf-m-gutter">
 | 
			
		||||
                    <div class="pf-c-card pf-l-grid__item pf-m-12-col">
 | 
			
		||||
@ -123,39 +134,31 @@ export class LDAPSourceViewPage extends AKElement {
 | 
			
		||||
                            <p>${t`Sync status`}</p>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="pf-c-card__body">
 | 
			
		||||
                            ${until(
 | 
			
		||||
                                new SourcesApi(DEFAULT_CONFIG)
 | 
			
		||||
                                    .sourcesLdapSyncStatusList({
 | 
			
		||||
                                        slug: this.source.slug,
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .then((tasks) => {
 | 
			
		||||
                                        if (tasks.length < 1) {
 | 
			
		||||
                                            return html`<p>${t`Not synced yet.`}</p>`;
 | 
			
		||||
                                        }
 | 
			
		||||
                                        return html`<ul class="pf-c-list">
 | 
			
		||||
                                            ${tasks.map((task) => {
 | 
			
		||||
                                                let header = "";
 | 
			
		||||
                                                if (task.status === TaskStatusEnum.Warning) {
 | 
			
		||||
                                                    header = t`Task finished with warnings`;
 | 
			
		||||
                                                } else if (task.status === TaskStatusEnum.Error) {
 | 
			
		||||
                                                    header = t`Task finished with errors`;
 | 
			
		||||
                                                } else {
 | 
			
		||||
                                                    header = t`Last sync: ${task.taskFinishTimestamp.toLocaleString()}`;
 | 
			
		||||
                                                }
 | 
			
		||||
                                                return html`<li>
 | 
			
		||||
                                                    <p>${task.taskName}</p>
 | 
			
		||||
                                                    <ul class="pf-c-list">
 | 
			
		||||
                                                        <li>${header}</li>
 | 
			
		||||
                                                        ${task.messages.map((m) => {
 | 
			
		||||
                                                            return html`<li>${m}</li>`;
 | 
			
		||||
                                                        })}
 | 
			
		||||
                                                    </ul>
 | 
			
		||||
                                                </li> `;
 | 
			
		||||
                                            })}
 | 
			
		||||
                                        </ul>`;
 | 
			
		||||
                                    }),
 | 
			
		||||
                                "loading",
 | 
			
		||||
                            )}
 | 
			
		||||
                            ${this.syncState.length < 1
 | 
			
		||||
                                ? html`<p>${t`Not synced yet.`}</p>`
 | 
			
		||||
                                : html`
 | 
			
		||||
                                      <ul class="pf-c-list">
 | 
			
		||||
                                          ${this.syncState.map((task) => {
 | 
			
		||||
                                              let header = "";
 | 
			
		||||
                                              if (task.status === TaskStatusEnum.Warning) {
 | 
			
		||||
                                                  header = t`Task finished with warnings`;
 | 
			
		||||
                                              } else if (task.status === TaskStatusEnum.Error) {
 | 
			
		||||
                                                  header = t`Task finished with errors`;
 | 
			
		||||
                                              } else {
 | 
			
		||||
                                                  header = t`Last sync: ${task.taskFinishTimestamp.toLocaleString()}`;
 | 
			
		||||
                                              }
 | 
			
		||||
                                              return html`<li>
 | 
			
		||||
                                                  <p>${task.taskName}</p>
 | 
			
		||||
                                                  <ul class="pf-c-list">
 | 
			
		||||
                                                      <li>${header}</li>
 | 
			
		||||
                                                      ${task.messages.map((m) => {
 | 
			
		||||
                                                          return html`<li>${m}</li>`;
 | 
			
		||||
                                                      })}
 | 
			
		||||
                                                  </ul>
 | 
			
		||||
                                              </li> `;
 | 
			
		||||
                                          })}
 | 
			
		||||
                                      </ul>
 | 
			
		||||
                                  `}
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="pf-c-card__footer">
 | 
			
		||||
                            <ak-action-button
 | 
			
		||||
 | 
			
		||||
@ -12,9 +12,8 @@ import "@goauthentik/elements/forms/ModalForm";
 | 
			
		||||
import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { CSSResult, 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 PFButton from "@patternfly/patternfly/components/Button/button.css";
 | 
			
		||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
 | 
			
		||||
@ -24,7 +23,7 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css";
 | 
			
		||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
 | 
			
		||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
			
		||||
 | 
			
		||||
import { SAMLSource, SourcesApi } from "@goauthentik/api";
 | 
			
		||||
import { SAMLMetadata, SAMLSource, SourcesApi } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-source-saml-view")
 | 
			
		||||
export class SAMLSourceViewPage extends AKElement {
 | 
			
		||||
@ -42,6 +41,9 @@ export class SAMLSourceViewPage extends AKElement {
 | 
			
		||||
    @property({ attribute: false })
 | 
			
		||||
    source?: SAMLSource;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    metadata?: SAMLMetadata;
 | 
			
		||||
 | 
			
		||||
    static get styles(): CSSResult[] {
 | 
			
		||||
        return [PFBase, PFPage, PFGrid, PFButton, PFContent, PFCard, PFDescriptionList];
 | 
			
		||||
    }
 | 
			
		||||
@ -152,35 +154,34 @@ export class SAMLSourceViewPage extends AKElement {
 | 
			
		||||
                slot="page-metadata"
 | 
			
		||||
                data-tab-title="${t`Metadata`}"
 | 
			
		||||
                class="pf-c-page__main-section pf-m-no-padding-mobile"
 | 
			
		||||
                @activate=${() => {
 | 
			
		||||
                    new SourcesApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .sourcesSamlMetadataRetrieve({
 | 
			
		||||
                            slug: this.source?.slug || "",
 | 
			
		||||
                        })
 | 
			
		||||
                        .then((metadata) => {
 | 
			
		||||
                            this.metadata = metadata;
 | 
			
		||||
                        });
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
                <div class="pf-l-grid pf-m-gutter">
 | 
			
		||||
                    <div class="pf-c-card pf-l-grid__item pf-m-12-col">
 | 
			
		||||
                        ${until(
 | 
			
		||||
                            new SourcesApi(DEFAULT_CONFIG)
 | 
			
		||||
                                .sourcesSamlMetadataRetrieve({
 | 
			
		||||
                                    slug: this.source.slug,
 | 
			
		||||
                                })
 | 
			
		||||
                                .then((m) => {
 | 
			
		||||
                                    return html`
 | 
			
		||||
                                        <div class="pf-c-card__body">
 | 
			
		||||
                                            <ak-codemirror
 | 
			
		||||
                                                mode="xml"
 | 
			
		||||
                                                ?readOnly=${true}
 | 
			
		||||
                                                value="${ifDefined(m.metadata)}"
 | 
			
		||||
                                            ></ak-codemirror>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                        <div class="pf-c-card__footer">
 | 
			
		||||
                                            <a
 | 
			
		||||
                                                class="pf-c-button pf-m-primary"
 | 
			
		||||
                                                target="_blank"
 | 
			
		||||
                                                href=${ifDefined(m.downloadUrl)}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                ${t`Download`}
 | 
			
		||||
                                            </a>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    `;
 | 
			
		||||
                                }),
 | 
			
		||||
                        )}
 | 
			
		||||
                        <div class="pf-c-card__body">
 | 
			
		||||
                            <ak-codemirror
 | 
			
		||||
                                mode="xml"
 | 
			
		||||
                                ?readOnly=${true}
 | 
			
		||||
                                value="${ifDefined(this.metadata?.metadata)}"
 | 
			
		||||
                            ></ak-codemirror>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="pf-c-card__footer">
 | 
			
		||||
                            <a
 | 
			
		||||
                                class="pf-c-button pf-m-primary"
 | 
			
		||||
                                target="_blank"
 | 
			
		||||
                                href=${ifDefined(this.metadata?.downloadUrl)}
 | 
			
		||||
                            >
 | 
			
		||||
                                ${t`Download`}
 | 
			
		||||
                            </a>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </section>
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import "@goauthentik/admin/stages/StageWizard";
 | 
			
		||||
import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm";
 | 
			
		||||
import "@goauthentik/admin/stages/authenticator_duo/DuoDeviceImportForm";
 | 
			
		||||
import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm";
 | 
			
		||||
import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm";
 | 
			
		||||
import "@goauthentik/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm";
 | 
			
		||||
@ -33,7 +34,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 { Stage, StagesApi } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@ -100,20 +100,24 @@ export class StageListPage extends TablePage<Stage> {
 | 
			
		||||
        </ak-forms-delete-bulk>`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async renderStageActions(stage: Stage): Promise<TemplateResult> {
 | 
			
		||||
        if (stage.component === "ak-stage-authenticator-duo-form") {
 | 
			
		||||
            await import("@goauthentik/admin/stages/authenticator_duo/DuoDeviceImportForm");
 | 
			
		||||
            return html`<ak-forms-modal>
 | 
			
		||||
                <span slot="submit">${t`Import`}</span>
 | 
			
		||||
                <span slot="header">${t`Import Duo device`}</span>
 | 
			
		||||
                <ak-stage-authenticator-duo-device-import-form slot="form" .instancePk=${stage.pk}>
 | 
			
		||||
                </ak-stage-authenticator-duo-device-import-form>
 | 
			
		||||
                <button slot="trigger" class="pf-c-button pf-m-plain">
 | 
			
		||||
                    <i class="fas fa-file-import"></i>
 | 
			
		||||
                </button>
 | 
			
		||||
            </ak-forms-modal>`;
 | 
			
		||||
    renderStageActions(stage: Stage): TemplateResult {
 | 
			
		||||
        switch (stage.component) {
 | 
			
		||||
            case "ak-stage-authenticator-duo-form":
 | 
			
		||||
                return html`<ak-forms-modal>
 | 
			
		||||
                    <span slot="submit">${t`Import`}</span>
 | 
			
		||||
                    <span slot="header">${t`Import Duo device`}</span>
 | 
			
		||||
                    <ak-stage-authenticator-duo-device-import-form
 | 
			
		||||
                        slot="form"
 | 
			
		||||
                        .instancePk=${stage.pk}
 | 
			
		||||
                    >
 | 
			
		||||
                    </ak-stage-authenticator-duo-device-import-form>
 | 
			
		||||
                    <button slot="trigger" class="pf-c-button pf-m-plain">
 | 
			
		||||
                        <i class="fas fa-file-import"></i>
 | 
			
		||||
                    </button>
 | 
			
		||||
                </ak-forms-modal>`;
 | 
			
		||||
            default:
 | 
			
		||||
                return html``;
 | 
			
		||||
        }
 | 
			
		||||
        return html``;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    row(item: Stage): TemplateResult[] {
 | 
			
		||||
@ -144,7 +148,7 @@ export class StageListPage extends TablePage<Stage> {
 | 
			
		||||
                        <i class="fas fa-edit"></i>
 | 
			
		||||
                    </button>
 | 
			
		||||
                </ak-forms-modal>
 | 
			
		||||
                ${until(this.renderStageActions(item))}`,
 | 
			
		||||
                ${this.renderStageActions(item)}`,
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,10 +3,11 @@ import "@goauthentik/admin/users/UserActiveForm";
 | 
			
		||||
import "@goauthentik/admin/users/UserForm";
 | 
			
		||||
import "@goauthentik/admin/users/UserPasswordForm";
 | 
			
		||||
import "@goauthentik/admin/users/UserResetEmailForm";
 | 
			
		||||
import { DEFAULT_CONFIG, config, tenant } from "@goauthentik/common/api/config";
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { MessageLevel } from "@goauthentik/common/messages";
 | 
			
		||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
			
		||||
import { first } from "@goauthentik/common/utils";
 | 
			
		||||
import { rootInterface } from "@goauthentik/elements/Base";
 | 
			
		||||
import { PFColor } from "@goauthentik/elements/Label";
 | 
			
		||||
import "@goauthentik/elements/buttons/ActionButton";
 | 
			
		||||
import "@goauthentik/elements/buttons/Dropdown";
 | 
			
		||||
@ -25,7 +26,6 @@ import { t } from "@lingui/macro";
 | 
			
		||||
import { CSSResult, 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 PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
 | 
			
		||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
 | 
			
		||||
@ -189,19 +189,16 @@ export class RelatedUserList extends Table<User> {
 | 
			
		||||
                        <i class="fas fa-edit"></i>
 | 
			
		||||
                    </button>
 | 
			
		||||
                </ak-forms-modal>
 | 
			
		||||
                ${until(
 | 
			
		||||
                    config().then((config) => {
 | 
			
		||||
                        if (config.capabilities.includes(CapabilitiesEnum.Impersonate)) {
 | 
			
		||||
                            return html`<a
 | 
			
		||||
                                class="pf-c-button pf-m-tertiary"
 | 
			
		||||
                                href="${`/-/impersonation/${item.pk}/`}"
 | 
			
		||||
                            >
 | 
			
		||||
                                ${t`Impersonate`}
 | 
			
		||||
                            </a>`;
 | 
			
		||||
                        }
 | 
			
		||||
                        return html``;
 | 
			
		||||
                    }),
 | 
			
		||||
                )}`,
 | 
			
		||||
                ${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.Impersonate)
 | 
			
		||||
                    ? html`
 | 
			
		||||
                          <a
 | 
			
		||||
                              class="pf-c-button pf-m-tertiary"
 | 
			
		||||
                              href="${`/-/impersonation/${item.pk}/`}"
 | 
			
		||||
                          >
 | 
			
		||||
                              ${t`Impersonate`}
 | 
			
		||||
                          </a>
 | 
			
		||||
                      `
 | 
			
		||||
                    : html``}`,
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -266,70 +263,61 @@ export class RelatedUserList extends Table<User> {
 | 
			
		||||
                                            ${t`Set password`}
 | 
			
		||||
                                        </button>
 | 
			
		||||
                                    </ak-forms-modal>
 | 
			
		||||
                                    ${until(
 | 
			
		||||
                                        tenant().then((tenant) => {
 | 
			
		||||
                                            if (!tenant.flowRecovery) {
 | 
			
		||||
                                                return html`
 | 
			
		||||
                                                    <p>
 | 
			
		||||
                                                        ${t`To let a user directly reset a their password, configure a recovery flow on the currently active tenant.`}
 | 
			
		||||
                                                    </p>
 | 
			
		||||
                                                `;
 | 
			
		||||
                                            }
 | 
			
		||||
                                            return html`
 | 
			
		||||
                                                <ak-action-button
 | 
			
		||||
                                                    class="pf-m-secondary"
 | 
			
		||||
                                                    .apiRequest=${() => {
 | 
			
		||||
                                                        return new CoreApi(DEFAULT_CONFIG)
 | 
			
		||||
                                                            .coreUsersRecoveryRetrieve({
 | 
			
		||||
                                                                id: item.pk || 0,
 | 
			
		||||
                                                            })
 | 
			
		||||
                                                            .then((rec) => {
 | 
			
		||||
                                                                showMessage({
 | 
			
		||||
                                                                    level: MessageLevel.success,
 | 
			
		||||
                                                                    message: t`Successfully generated recovery link`,
 | 
			
		||||
                                                                    description: rec.link,
 | 
			
		||||
                                                                });
 | 
			
		||||
                                                            })
 | 
			
		||||
                                                            .catch((ex: ResponseError) => {
 | 
			
		||||
                                                                ex.response.json().then(() => {
 | 
			
		||||
                                                                    showMessage({
 | 
			
		||||
                                                                        level: MessageLevel.error,
 | 
			
		||||
                                                                        message: t`No recovery flow is configured.`,
 | 
			
		||||
                                                                    });
 | 
			
		||||
                                                                });
 | 
			
		||||
                                                            });
 | 
			
		||||
                                                    }}
 | 
			
		||||
                                                >
 | 
			
		||||
                                                    ${t`Copy recovery link`}
 | 
			
		||||
                                                </ak-action-button>
 | 
			
		||||
                                                ${item.email
 | 
			
		||||
                                                    ? html`<ak-forms-modal
 | 
			
		||||
                                                          .closeAfterSuccessfulSubmit=${false}
 | 
			
		||||
                                                      >
 | 
			
		||||
                                                          <span slot="submit">
 | 
			
		||||
                                                              ${t`Send link`}
 | 
			
		||||
                                                          </span>
 | 
			
		||||
                                                          <span slot="header">
 | 
			
		||||
                                                              ${t`Send recovery link to user`}
 | 
			
		||||
                                                          </span>
 | 
			
		||||
                                                          <ak-user-reset-email-form
 | 
			
		||||
                                                              slot="form"
 | 
			
		||||
                                                              .user=${item}
 | 
			
		||||
                                                          >
 | 
			
		||||
                                                          </ak-user-reset-email-form>
 | 
			
		||||
                                                          <button
 | 
			
		||||
                                                              slot="trigger"
 | 
			
		||||
                                                              class="pf-c-button pf-m-secondary"
 | 
			
		||||
                                                          >
 | 
			
		||||
                                                              ${t`Email recovery link`}
 | 
			
		||||
                                                          </button>
 | 
			
		||||
                                                      </ak-forms-modal>`
 | 
			
		||||
                                                    : html`<span
 | 
			
		||||
                                                          >${t`Recovery link cannot be emailed, user has no email address saved.`}</span
 | 
			
		||||
                                                      >`}
 | 
			
		||||
                                            `;
 | 
			
		||||
                                        }),
 | 
			
		||||
                                    )}
 | 
			
		||||
                                    ${rootInterface()?.tenant?.flowRecovery
 | 
			
		||||
                                        ? html`
 | 
			
		||||
                                              <ak-action-button
 | 
			
		||||
                                                  class="pf-m-secondary"
 | 
			
		||||
                                                  .apiRequest=${() => {
 | 
			
		||||
                                                      return new CoreApi(DEFAULT_CONFIG)
 | 
			
		||||
                                                          .coreUsersRecoveryRetrieve({
 | 
			
		||||
                                                              id: item.pk,
 | 
			
		||||
                                                          })
 | 
			
		||||
                                                          .then((rec) => {
 | 
			
		||||
                                                              showMessage({
 | 
			
		||||
                                                                  level: MessageLevel.success,
 | 
			
		||||
                                                                  message: t`Successfully generated recovery link`,
 | 
			
		||||
                                                                  description: rec.link,
 | 
			
		||||
                                                              });
 | 
			
		||||
                                                          })
 | 
			
		||||
                                                          .catch((ex: ResponseError) => {
 | 
			
		||||
                                                              ex.response.json().then(() => {
 | 
			
		||||
                                                                  showMessage({
 | 
			
		||||
                                                                      level: MessageLevel.error,
 | 
			
		||||
                                                                      message: t`No recovery flow is configured.`,
 | 
			
		||||
                                                                  });
 | 
			
		||||
                                                              });
 | 
			
		||||
                                                          });
 | 
			
		||||
                                                  }}
 | 
			
		||||
                                              >
 | 
			
		||||
                                                  ${t`Copy recovery link`}
 | 
			
		||||
                                              </ak-action-button>
 | 
			
		||||
                                              ${item.email
 | 
			
		||||
                                                  ? html`<ak-forms-modal
 | 
			
		||||
                                                        .closeAfterSuccessfulSubmit=${false}
 | 
			
		||||
                                                    >
 | 
			
		||||
                                                        <span slot="submit"> ${t`Send link`} </span>
 | 
			
		||||
                                                        <span slot="header">
 | 
			
		||||
                                                            ${t`Send recovery link to user`}
 | 
			
		||||
                                                        </span>
 | 
			
		||||
                                                        <ak-user-reset-email-form
 | 
			
		||||
                                                            slot="form"
 | 
			
		||||
                                                            .user=${item}
 | 
			
		||||
                                                        >
 | 
			
		||||
                                                        </ak-user-reset-email-form>
 | 
			
		||||
                                                        <button
 | 
			
		||||
                                                            slot="trigger"
 | 
			
		||||
                                                            class="pf-c-button pf-m-secondary"
 | 
			
		||||
                                                        >
 | 
			
		||||
                                                            ${t`Email recovery link`}
 | 
			
		||||
                                                        </button>
 | 
			
		||||
                                                    </ak-forms-modal>`
 | 
			
		||||
                                                  : html`<span
 | 
			
		||||
                                                        >${t`Recovery link cannot be emailed, user has no email address saved.`}</span
 | 
			
		||||
                                                    >`}
 | 
			
		||||
                                          `
 | 
			
		||||
                                        : html` <p>
 | 
			
		||||
                                              ${t`To let a user directly reset a their password, configure a recovery flow on the currently active tenant.`}
 | 
			
		||||
                                          </p>`}
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </dd>
 | 
			
		||||
                        </div>
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,14 @@
 | 
			
		||||
import { AdminInterface } from "@goauthentik/admin/AdminInterface";
 | 
			
		||||
import "@goauthentik/admin/users/ServiceAccountForm";
 | 
			
		||||
import "@goauthentik/admin/users/UserActiveForm";
 | 
			
		||||
import "@goauthentik/admin/users/UserForm";
 | 
			
		||||
import "@goauthentik/admin/users/UserPasswordForm";
 | 
			
		||||
import "@goauthentik/admin/users/UserResetEmailForm";
 | 
			
		||||
import { DEFAULT_CONFIG, config, tenant } from "@goauthentik/common/api/config";
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { MessageLevel } from "@goauthentik/common/messages";
 | 
			
		||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
			
		||||
import { me } from "@goauthentik/common/users";
 | 
			
		||||
import { first } from "@goauthentik/common/utils";
 | 
			
		||||
import { rootInterface } from "@goauthentik/elements/Base";
 | 
			
		||||
import { PFColor } from "@goauthentik/elements/Label";
 | 
			
		||||
import { PFSize } from "@goauthentik/elements/Spinner";
 | 
			
		||||
import "@goauthentik/elements/TreeView";
 | 
			
		||||
@ -23,14 +24,13 @@ import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
			
		||||
import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { CSSResult, TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
 | 
			
		||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
 | 
			
		||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
 | 
			
		||||
 | 
			
		||||
import { CapabilitiesEnum, CoreApi, ResponseError, User } from "@goauthentik/api";
 | 
			
		||||
import { CapabilitiesEnum, CoreApi, ResponseError, User, UserPath } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-user-list")
 | 
			
		||||
export class UserListPage extends TablePage<User> {
 | 
			
		||||
@ -56,18 +56,25 @@ export class UserListPage extends TablePage<User> {
 | 
			
		||||
    @property()
 | 
			
		||||
    activePath = getURLParam<string>("path", "/");
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    userPaths?: UserPath;
 | 
			
		||||
 | 
			
		||||
    static get styles(): CSSResult[] {
 | 
			
		||||
        return super.styles.concat(PFDescriptionList, PFCard, PFAlert);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<User>> {
 | 
			
		||||
        return new CoreApi(DEFAULT_CONFIG).coreUsersList({
 | 
			
		||||
        const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList({
 | 
			
		||||
            ordering: this.order,
 | 
			
		||||
            page: page,
 | 
			
		||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
			
		||||
            search: this.search || "",
 | 
			
		||||
            pathStartswith: getURLParam("path", ""),
 | 
			
		||||
        });
 | 
			
		||||
        this.userPaths = await new CoreApi(DEFAULT_CONFIG).coreUsersPathsRetrieve({
 | 
			
		||||
            search: this.search,
 | 
			
		||||
        });
 | 
			
		||||
        return users;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    columns(): TableColumn[] {
 | 
			
		||||
@ -81,6 +88,10 @@ export class UserListPage extends TablePage<User> {
 | 
			
		||||
 | 
			
		||||
    renderToolbarSelected(): TemplateResult {
 | 
			
		||||
        const disabled = this.selectedElements.length < 1;
 | 
			
		||||
        const currentUser = rootInterface<AdminInterface>()?.user;
 | 
			
		||||
        const shouldShowWarning = this.selectedElements.find((el) => {
 | 
			
		||||
            return el.pk === currentUser?.user.pk || el.pk == currentUser?.original?.pk;
 | 
			
		||||
        });
 | 
			
		||||
        return html`<ak-forms-delete-bulk
 | 
			
		||||
            objectLabel=${t`User(s)`}
 | 
			
		||||
            .objects=${this.selectedElements}
 | 
			
		||||
@ -102,28 +113,18 @@ export class UserListPage extends TablePage<User> {
 | 
			
		||||
                });
 | 
			
		||||
            }}
 | 
			
		||||
        >
 | 
			
		||||
            ${until(
 | 
			
		||||
                me().then((user) => {
 | 
			
		||||
                    const shouldShowWarning = this.selectedElements.find((el) => {
 | 
			
		||||
                        return el.pk === user.user.pk || el.pk == user.original?.pk;
 | 
			
		||||
                    });
 | 
			
		||||
                    if (shouldShowWarning) {
 | 
			
		||||
                        return html`
 | 
			
		||||
                            <div slot="notice" class="pf-c-form__alert">
 | 
			
		||||
                                <div class="pf-c-alert pf-m-inline pf-m-warning">
 | 
			
		||||
                                    <div class="pf-c-alert__icon">
 | 
			
		||||
                                        <i class="fas fa-exclamation-circle"></i>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                    <h4 class="pf-c-alert__title">
 | 
			
		||||
                                        ${t`Warning: You're about to delete the user you're logged in as (${shouldShowWarning.username}). Proceed at your own risk.`}
 | 
			
		||||
                                    </h4>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        `;
 | 
			
		||||
                    }
 | 
			
		||||
                    return html``;
 | 
			
		||||
                }),
 | 
			
		||||
            )}
 | 
			
		||||
            ${shouldShowWarning
 | 
			
		||||
                ? html`<div slot="notice" class="pf-c-form__alert">
 | 
			
		||||
                      <div class="pf-c-alert pf-m-inline pf-m-warning">
 | 
			
		||||
                          <div class="pf-c-alert__icon">
 | 
			
		||||
                              <i class="fas fa-exclamation-circle"></i>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <h4 class="pf-c-alert__title">
 | 
			
		||||
                              ${t`Warning: You're about to delete the user you're logged in as (${shouldShowWarning.username}). Proceed at your own risk.`}
 | 
			
		||||
                          </h4>
 | 
			
		||||
                      </div>
 | 
			
		||||
                  </div>`
 | 
			
		||||
                : html``}
 | 
			
		||||
            <button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
 | 
			
		||||
                ${t`Delete`}
 | 
			
		||||
            </button>
 | 
			
		||||
@ -148,19 +149,16 @@ export class UserListPage extends TablePage<User> {
 | 
			
		||||
                        <i class="fas fa-edit"></i>
 | 
			
		||||
                    </button>
 | 
			
		||||
                </ak-forms-modal>
 | 
			
		||||
                ${until(
 | 
			
		||||
                    config().then((config) => {
 | 
			
		||||
                        if (config.capabilities.includes(CapabilitiesEnum.Impersonate)) {
 | 
			
		||||
                            return html`<a
 | 
			
		||||
                                class="pf-c-button pf-m-tertiary"
 | 
			
		||||
                                href="${`/-/impersonation/${item.pk}/`}"
 | 
			
		||||
                            >
 | 
			
		||||
                                ${t`Impersonate`}
 | 
			
		||||
                            </a>`;
 | 
			
		||||
                        }
 | 
			
		||||
                        return html``;
 | 
			
		||||
                    }),
 | 
			
		||||
                )}`,
 | 
			
		||||
                ${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.Impersonate)
 | 
			
		||||
                    ? html`
 | 
			
		||||
                          <a
 | 
			
		||||
                              class="pf-c-button pf-m-tertiary"
 | 
			
		||||
                              href="${`/-/impersonation/${item.pk}/`}"
 | 
			
		||||
                          >
 | 
			
		||||
                              ${t`Impersonate`}
 | 
			
		||||
                          </a>
 | 
			
		||||
                      `
 | 
			
		||||
                    : html``}`,
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -194,7 +192,7 @@ export class UserListPage extends TablePage<User> {
 | 
			
		||||
                                            return new CoreApi(
 | 
			
		||||
                                                DEFAULT_CONFIG,
 | 
			
		||||
                                            ).coreUsersPartialUpdate({
 | 
			
		||||
                                                id: item.pk || 0,
 | 
			
		||||
                                                id: item.pk,
 | 
			
		||||
                                                patchedUserRequest: {
 | 
			
		||||
                                                    isActive: !item.isActive,
 | 
			
		||||
                                                },
 | 
			
		||||
@ -225,70 +223,61 @@ export class UserListPage extends TablePage<User> {
 | 
			
		||||
                                            ${t`Set password`}
 | 
			
		||||
                                        </button>
 | 
			
		||||
                                    </ak-forms-modal>
 | 
			
		||||
                                    ${until(
 | 
			
		||||
                                        tenant().then((tenant) => {
 | 
			
		||||
                                            if (!tenant.flowRecovery) {
 | 
			
		||||
                                                return html`
 | 
			
		||||
                                                    <p>
 | 
			
		||||
                                                        ${t`To let a user directly reset a their password, configure a recovery flow on the currently active tenant.`}
 | 
			
		||||
                                                    </p>
 | 
			
		||||
                                                `;
 | 
			
		||||
                                            }
 | 
			
		||||
                                            return html`
 | 
			
		||||
                                                <ak-action-button
 | 
			
		||||
                                                    class="pf-m-secondary"
 | 
			
		||||
                                                    .apiRequest=${() => {
 | 
			
		||||
                                                        return new CoreApi(DEFAULT_CONFIG)
 | 
			
		||||
                                                            .coreUsersRecoveryRetrieve({
 | 
			
		||||
                                                                id: item.pk || 0,
 | 
			
		||||
                                                            })
 | 
			
		||||
                                                            .then((rec) => {
 | 
			
		||||
                                                                showMessage({
 | 
			
		||||
                                                                    level: MessageLevel.success,
 | 
			
		||||
                                                                    message: t`Successfully generated recovery link`,
 | 
			
		||||
                                                                    description: rec.link,
 | 
			
		||||
                                                                });
 | 
			
		||||
                                                            })
 | 
			
		||||
                                                            .catch((ex: ResponseError) => {
 | 
			
		||||
                                                                ex.response.json().then(() => {
 | 
			
		||||
                                                                    showMessage({
 | 
			
		||||
                                                                        level: MessageLevel.error,
 | 
			
		||||
                                                                        message: t`No recovery flow is configured.`,
 | 
			
		||||
                                                                    });
 | 
			
		||||
                                                                });
 | 
			
		||||
                                                            });
 | 
			
		||||
                                                    }}
 | 
			
		||||
                                                >
 | 
			
		||||
                                                    ${t`Copy recovery link`}
 | 
			
		||||
                                                </ak-action-button>
 | 
			
		||||
                                                ${item.email
 | 
			
		||||
                                                    ? html`<ak-forms-modal
 | 
			
		||||
                                                          .closeAfterSuccessfulSubmit=${false}
 | 
			
		||||
                                                      >
 | 
			
		||||
                                                          <span slot="submit">
 | 
			
		||||
                                                              ${t`Send link`}
 | 
			
		||||
                                                          </span>
 | 
			
		||||
                                                          <span slot="header">
 | 
			
		||||
                                                              ${t`Send recovery link to user`}
 | 
			
		||||
                                                          </span>
 | 
			
		||||
                                                          <ak-user-reset-email-form
 | 
			
		||||
                                                              slot="form"
 | 
			
		||||
                                                              .user=${item}
 | 
			
		||||
                                                          >
 | 
			
		||||
                                                          </ak-user-reset-email-form>
 | 
			
		||||
                                                          <button
 | 
			
		||||
                                                              slot="trigger"
 | 
			
		||||
                                                              class="pf-c-button pf-m-secondary"
 | 
			
		||||
                                                          >
 | 
			
		||||
                                                              ${t`Email recovery link`}
 | 
			
		||||
                                                          </button>
 | 
			
		||||
                                                      </ak-forms-modal>`
 | 
			
		||||
                                                    : html`<span
 | 
			
		||||
                                                          >${t`Recovery link cannot be emailed, user has no email address saved.`}</span
 | 
			
		||||
                                                      >`}
 | 
			
		||||
                                            `;
 | 
			
		||||
                                        }),
 | 
			
		||||
                                    )}
 | 
			
		||||
                                    ${rootInterface()?.tenant?.flowRecovery
 | 
			
		||||
                                        ? html`
 | 
			
		||||
                                              <ak-action-button
 | 
			
		||||
                                                  class="pf-m-secondary"
 | 
			
		||||
                                                  .apiRequest=${() => {
 | 
			
		||||
                                                      return new CoreApi(DEFAULT_CONFIG)
 | 
			
		||||
                                                          .coreUsersRecoveryRetrieve({
 | 
			
		||||
                                                              id: item.pk,
 | 
			
		||||
                                                          })
 | 
			
		||||
                                                          .then((rec) => {
 | 
			
		||||
                                                              showMessage({
 | 
			
		||||
                                                                  level: MessageLevel.success,
 | 
			
		||||
                                                                  message: t`Successfully generated recovery link`,
 | 
			
		||||
                                                                  description: rec.link,
 | 
			
		||||
                                                              });
 | 
			
		||||
                                                          })
 | 
			
		||||
                                                          .catch((ex: ResponseError) => {
 | 
			
		||||
                                                              ex.response.json().then(() => {
 | 
			
		||||
                                                                  showMessage({
 | 
			
		||||
                                                                      level: MessageLevel.error,
 | 
			
		||||
                                                                      message: t`No recovery flow is configured.`,
 | 
			
		||||
                                                                  });
 | 
			
		||||
                                                              });
 | 
			
		||||
                                                          });
 | 
			
		||||
                                                  }}
 | 
			
		||||
                                              >
 | 
			
		||||
                                                  ${t`Copy recovery link`}
 | 
			
		||||
                                              </ak-action-button>
 | 
			
		||||
                                              ${item.email
 | 
			
		||||
                                                  ? html`<ak-forms-modal
 | 
			
		||||
                                                        .closeAfterSuccessfulSubmit=${false}
 | 
			
		||||
                                                    >
 | 
			
		||||
                                                        <span slot="submit"> ${t`Send link`} </span>
 | 
			
		||||
                                                        <span slot="header">
 | 
			
		||||
                                                            ${t`Send recovery link to user`}
 | 
			
		||||
                                                        </span>
 | 
			
		||||
                                                        <ak-user-reset-email-form
 | 
			
		||||
                                                            slot="form"
 | 
			
		||||
                                                            .user=${item}
 | 
			
		||||
                                                        >
 | 
			
		||||
                                                        </ak-user-reset-email-form>
 | 
			
		||||
                                                        <button
 | 
			
		||||
                                                            slot="trigger"
 | 
			
		||||
                                                            class="pf-c-button pf-m-secondary"
 | 
			
		||||
                                                        >
 | 
			
		||||
                                                            ${t`Email recovery link`}
 | 
			
		||||
                                                        </button>
 | 
			
		||||
                                                    </ak-forms-modal>`
 | 
			
		||||
                                                  : html`<span
 | 
			
		||||
                                                        >${t`Recovery link cannot be emailed, user has no email address saved.`}</span
 | 
			
		||||
                                                    >`}
 | 
			
		||||
                                          `
 | 
			
		||||
                                        : html` <p>
 | 
			
		||||
                                              ${t`To let a user directly reset a their password, configure a recovery flow on the currently active tenant.`}
 | 
			
		||||
                                          </p>`}
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </dd>
 | 
			
		||||
                        </div>
 | 
			
		||||
@ -323,18 +312,10 @@ export class UserListPage extends TablePage<User> {
 | 
			
		||||
            <div class="pf-c-card">
 | 
			
		||||
                <div class="pf-c-card__title">${t`User folders`}</div>
 | 
			
		||||
                <div class="pf-c-card__body">
 | 
			
		||||
                    ${until(
 | 
			
		||||
                        new CoreApi(DEFAULT_CONFIG)
 | 
			
		||||
                            .coreUsersPathsRetrieve({
 | 
			
		||||
                                search: this.search,
 | 
			
		||||
                            })
 | 
			
		||||
                            .then((paths) => {
 | 
			
		||||
                                return html`<ak-treeview
 | 
			
		||||
                                    .items=${paths.paths}
 | 
			
		||||
                                    activePath=${this.activePath}
 | 
			
		||||
                                ></ak-treeview>`;
 | 
			
		||||
                            }),
 | 
			
		||||
                    )}
 | 
			
		||||
                    <ak-treeview
 | 
			
		||||
                        .items=${this.userPaths?.paths || []}
 | 
			
		||||
                        activePath=${this.activePath}
 | 
			
		||||
                    ></ak-treeview>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>`;
 | 
			
		||||
 | 
			
		||||
@ -3,10 +3,10 @@ import "@goauthentik/admin/users/UserActiveForm";
 | 
			
		||||
import "@goauthentik/admin/users/UserChart";
 | 
			
		||||
import "@goauthentik/admin/users/UserForm";
 | 
			
		||||
import "@goauthentik/admin/users/UserPasswordForm";
 | 
			
		||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
 | 
			
		||||
import { MessageLevel } from "@goauthentik/common/messages";
 | 
			
		||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
			
		||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@goauthentik/elements/CodeMirror";
 | 
			
		||||
import { PFColor } from "@goauthentik/elements/Label";
 | 
			
		||||
import "@goauthentik/elements/PageHeader";
 | 
			
		||||
@ -27,7 +27,6 @@ import { t } from "@lingui/macro";
 | 
			
		||||
 | 
			
		||||
import { CSSResult, TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
 | 
			
		||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
 | 
			
		||||
@ -197,21 +196,18 @@ export class UserViewPage extends AKElement {
 | 
			
		||||
                                </button>
 | 
			
		||||
                            </ak-forms-modal>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        ${until(
 | 
			
		||||
                            config().then((config) => {
 | 
			
		||||
                                if (config.capabilities.includes(CapabilitiesEnum.Impersonate)) {
 | 
			
		||||
                                    return html` <div class="pf-c-card__footer">
 | 
			
		||||
                                        <a
 | 
			
		||||
                                            class="pf-c-button pf-m-tertiary"
 | 
			
		||||
                                            href="${`/-/impersonation/${this.user?.pk}/`}"
 | 
			
		||||
                                        >
 | 
			
		||||
                                            ${t`Impersonate`}
 | 
			
		||||
                                        </a>
 | 
			
		||||
                                    </div>`;
 | 
			
		||||
                                }
 | 
			
		||||
                                return html``;
 | 
			
		||||
                            }),
 | 
			
		||||
                        )}
 | 
			
		||||
                        ${rootInterface()?.config?.capabilities.includes(
 | 
			
		||||
                            CapabilitiesEnum.Impersonate,
 | 
			
		||||
                        )
 | 
			
		||||
                            ? html`
 | 
			
		||||
                                  <a
 | 
			
		||||
                                      class="pf-c-button pf-m-tertiary"
 | 
			
		||||
                                      href="${`/-/impersonation/${this.user?.pk}/`}"
 | 
			
		||||
                                  >
 | 
			
		||||
                                      ${t`Impersonate`}
 | 
			
		||||
                                  </a>
 | 
			
		||||
                              `
 | 
			
		||||
                            : html``}
 | 
			
		||||
                        <div class="pf-c-card__footer">
 | 
			
		||||
                            <ak-user-active-form
 | 
			
		||||
                                .obj=${this.user}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user