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:
parent
af7189953c
commit
b3dd87bbab
|
@ -16,3 +16,4 @@ markComment: >
|
||||||
This issue has been automatically marked as stale because it has not had
|
This issue has been automatically marked as stale because it has not had
|
||||||
recent activity. It will be closed if no further activity occurs. Thank you
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
for your contributions.
|
for your contributions.
|
||||||
|
only: issues
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { AdminInterface } from "@goauthentik/admin/AdminInterface";
|
||||||
import "@goauthentik/admin/admin-overview/TopApplicationsTable";
|
import "@goauthentik/admin/admin-overview/TopApplicationsTable";
|
||||||
import "@goauthentik/admin/admin-overview/cards/AdminStatusCard";
|
import "@goauthentik/admin/admin-overview/cards/AdminStatusCard";
|
||||||
import "@goauthentik/admin/admin-overview/cards/RecentEventsCard";
|
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/OutpostStatusChart";
|
||||||
import "@goauthentik/admin/admin-overview/charts/SyncStatusChart";
|
import "@goauthentik/admin/admin-overview/charts/SyncStatusChart";
|
||||||
import { VERSION } from "@goauthentik/common/constants";
|
import { VERSION } from "@goauthentik/common/constants";
|
||||||
import { me } from "@goauthentik/common/users";
|
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
|
||||||
import "@goauthentik/elements/PageHeader";
|
import "@goauthentik/elements/PageHeader";
|
||||||
import "@goauthentik/elements/cards/AggregatePromiseCard";
|
import "@goauthentik/elements/cards/AggregatePromiseCard";
|
||||||
import { paramURL } from "@goauthentik/elements/router/RouterOutlet";
|
import { paramURL } from "@goauthentik/elements/router/RouterOutlet";
|
||||||
|
@ -17,15 +17,13 @@ import { paramURL } from "@goauthentik/elements/router/RouterOutlet";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
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 PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||||
import PFList from "@patternfly/patternfly/components/List/list.css";
|
import PFList from "@patternfly/patternfly/components/List/list.css";
|
||||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||||
|
|
||||||
import { SessionUser } from "@goauthentik/api";
|
|
||||||
|
|
||||||
export function versionFamily(): string {
|
export function versionFamily(): string {
|
||||||
const parts = VERSION.split(".");
|
const parts = VERSION.split(".");
|
||||||
parts.pop();
|
parts.pop();
|
||||||
|
@ -58,17 +56,11 @@ export class AdminOverviewPage extends AKElement {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@state()
|
|
||||||
user?: SessionUser;
|
|
||||||
|
|
||||||
async firstUpdated(): Promise<void> {
|
|
||||||
this.user = await me();
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
let name = this.user?.user.username;
|
const user = rootInterface<AdminInterface>()?.user;
|
||||||
if (this.user?.user.name) {
|
let name = user?.user.username;
|
||||||
name = this.user.user.name;
|
if (user?.user.name) {
|
||||||
|
name = user.user.name;
|
||||||
}
|
}
|
||||||
return html`<ak-page-header icon="" header="" description=${t`General system status`}>
|
return html`<ak-page-header icon="" header="" description=${t`General system status`}>
|
||||||
<span slot="header"> ${t`Welcome, ${name}.`} </span>
|
<span slot="header"> ${t`Welcome, ${name}.`} </span>
|
||||||
|
|
|
@ -18,13 +18,12 @@ import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { CSSResult } from "lit";
|
import { CSSResult } from "lit";
|
||||||
import { TemplateResult, html } 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 { 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 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 {
|
export function TypeToLabel(type?: OutpostTypeEnum): string {
|
||||||
if (!type) return "";
|
if (!type) return "";
|
||||||
|
@ -56,14 +55,31 @@ export class OutpostListPage extends TablePage<Outpost> {
|
||||||
searchEnabled(): boolean {
|
searchEnabled(): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async apiEndpoint(page: number): Promise<PaginatedResponse<Outpost>> {
|
async apiEndpoint(page: number): Promise<PaginatedResponse<Outpost>> {
|
||||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesList({
|
const outposts = await new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesList({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
pageSize: (await uiConfig()).pagination.perPage,
|
pageSize: (await uiConfig()).pagination.perPage,
|
||||||
search: this.search || "",
|
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[] {
|
columns(): TableColumn[] {
|
||||||
return [
|
return [
|
||||||
new TableColumn(t`Name`, "name"),
|
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)`}
|
${t`Detailed health (one instance per column, data is cached so may be out of date)`}
|
||||||
</h3>
|
</h3>
|
||||||
<dl class="pf-c-description-list pf-m-3-col-on-lg">
|
<dl class="pf-c-description-list pf-m-3-col-on-lg">
|
||||||
${until(
|
${this.health[item.pk].map((h) => {
|
||||||
new OutpostsApi(DEFAULT_CONFIG)
|
return html`<div class="pf-c-description-list__group">
|
||||||
.outpostsInstancesHealthList({
|
<dd class="pf-c-description-list__description">
|
||||||
uuid: item.pk,
|
<div class="pf-c-description-list__text">
|
||||||
})
|
<ak-outpost-health .outpostHealth=${h}></ak-outpost-health>
|
||||||
.then((health) => {
|
</div>
|
||||||
return health.map((h) => {
|
</dd>
|
||||||
return html` <div class="pf-c-description-list__group">
|
</div>`;
|
||||||
<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>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</td>`;
|
</td>`;
|
||||||
|
|
|
@ -16,11 +16,10 @@ import { TablePage } from "@goauthentik/elements/table/TablePage";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { TemplateResult, html } 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 { 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")
|
@customElement("ak-outpost-service-connection-list")
|
||||||
export class OutpostServiceConnectionListPage extends TablePage<ServiceConnection> {
|
export class OutpostServiceConnectionListPage extends TablePage<ServiceConnection> {
|
||||||
|
@ -40,14 +39,31 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
|
||||||
checkbox = true;
|
checkbox = true;
|
||||||
|
|
||||||
async apiEndpoint(page: number): Promise<PaginatedResponse<ServiceConnection>> {
|
async apiEndpoint(page: number): Promise<PaginatedResponse<ServiceConnection>> {
|
||||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllList({
|
const connections = await new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllList(
|
||||||
ordering: this.order,
|
{
|
||||||
page: page,
|
ordering: this.order,
|
||||||
pageSize: (await uiConfig()).pagination.perPage,
|
page: page,
|
||||||
search: this.search || "",
|
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[] {
|
columns(): TableColumn[] {
|
||||||
return [
|
return [
|
||||||
new TableColumn(t`Name`, "name"),
|
new TableColumn(t`Name`, "name"),
|
||||||
|
@ -62,27 +78,16 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
|
||||||
order = "name";
|
order = "name";
|
||||||
|
|
||||||
row(item: ServiceConnection): TemplateResult[] {
|
row(item: ServiceConnection): TemplateResult[] {
|
||||||
|
const itemState = this.state[item.pk];
|
||||||
return [
|
return [
|
||||||
html`${item.name}`,
|
html`${item.name}`,
|
||||||
html`${item.verboseName}`,
|
html`${item.verboseName}`,
|
||||||
html`<ak-label color=${item.local ? PFColor.Grey : PFColor.Green}>
|
html`<ak-label color=${item.local ? PFColor.Grey : PFColor.Green}>
|
||||||
${item.local ? t`Yes` : t`No`}
|
${item.local ? t`Yes` : t`No`}
|
||||||
</ak-label>`,
|
</ak-label>`,
|
||||||
html`${until(
|
html`${itemState.healthy
|
||||||
new OutpostsApi(DEFAULT_CONFIG)
|
? html`<ak-label color=${PFColor.Green}>${ifDefined(itemState.version)}</ak-label>`
|
||||||
.outpostsServiceConnectionsAllStateRetrieve({
|
: html`<ak-label color=${PFColor.Red}>${t`Unhealthy`}</ak-label>`}`,
|
||||||
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` <ak-forms-modal>
|
html` <ak-forms-modal>
|
||||||
<span slot="submit"> ${t`Update`} </span>
|
<span slot="submit"> ${t`Update`} </span>
|
||||||
<span slot="header"> ${t`Update ${item.verboseName}`} </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 MDProviderOAuth2 from "@goauthentik/docs/providers/oauth2/index.md";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import "@goauthentik/elements/CodeMirror";
|
import "@goauthentik/elements/CodeMirror";
|
||||||
|
import "@goauthentik/elements/EmptyState";
|
||||||
import "@goauthentik/elements/Markdown";
|
import "@goauthentik/elements/Markdown";
|
||||||
import "@goauthentik/elements/Tabs";
|
import "@goauthentik/elements/Tabs";
|
||||||
import "@goauthentik/elements/buttons/ModalButton";
|
import "@goauthentik/elements/buttons/ModalButton";
|
||||||
|
@ -15,8 +16,7 @@ import "@goauthentik/elements/events/ObjectChangelog";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, html } from "lit";
|
import { CSSResult, TemplateResult, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
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 PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.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 PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.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")
|
@customElement("ak-provider-oauth2-view")
|
||||||
export class OAuth2ProviderViewPage extends AKElement {
|
export class OAuth2ProviderViewPage extends AKElement {
|
||||||
@property({ type: Number })
|
@property({ type: Number })
|
||||||
set providerID(value: number) {
|
set providerID(value: number) {
|
||||||
const api = new ProvidersApi(DEFAULT_CONFIG);
|
new ProvidersApi(DEFAULT_CONFIG)
|
||||||
api.providersOauth2Retrieve({
|
.providersOauth2Retrieve({
|
||||||
id: value,
|
id: value,
|
||||||
}).then((prov) => {
|
})
|
||||||
this.provider = prov;
|
.then((prov) => {
|
||||||
});
|
this.provider = prov;
|
||||||
api.providersOauth2SetupUrlsRetrieve({
|
});
|
||||||
id: value,
|
|
||||||
}).then((prov) => {
|
|
||||||
this.providerUrls = prov;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
provider?: OAuth2Provider;
|
provider?: OAuth2Provider;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@state()
|
||||||
providerUrls?: OAuth2ProviderSetupURLs;
|
providerUrls?: OAuth2ProviderSetupURLs;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
preview?: PropertyMappingPreview;
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [
|
return [
|
||||||
PFBase,
|
PFBase,
|
||||||
|
@ -82,10 +86,32 @@ export class OAuth2ProviderViewPage extends AKElement {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html` <ak-tabs>
|
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()}
|
${this.renderTabOverview()}
|
||||||
</section>
|
</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()}
|
${this.renderTabPreview()}
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
|
@ -318,15 +344,9 @@ export class OAuth2ProviderViewPage extends AKElement {
|
||||||
${t`Example JWT payload (for currently authenticated user)`}
|
${t`Example JWT payload (for currently authenticated user)`}
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
${until(
|
${this.preview
|
||||||
new ProvidersApi(DEFAULT_CONFIG)
|
? html`<pre>${JSON.stringify(this.preview?.preview, null, 4)}</pre>`
|
||||||
.providersOauth2PreviewUserRetrieve({
|
: html` <ak-empty-state ?loading=${true}></ak-empty-state> `}
|
||||||
id: this.provider?.pk,
|
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
return html`<pre>${JSON.stringify(data.preview, null, 4)}</pre>`;
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||||
import { MessageLevel } from "@goauthentik/common/messages";
|
import { MessageLevel } from "@goauthentik/common/messages";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import "@goauthentik/elements/CodeMirror";
|
import "@goauthentik/elements/CodeMirror";
|
||||||
|
import "@goauthentik/elements/EmptyState";
|
||||||
import "@goauthentik/elements/Tabs";
|
import "@goauthentik/elements/Tabs";
|
||||||
import "@goauthentik/elements/buttons/ActionButton";
|
import "@goauthentik/elements/buttons/ActionButton";
|
||||||
import "@goauthentik/elements/buttons/ModalButton";
|
import "@goauthentik/elements/buttons/ModalButton";
|
||||||
|
@ -15,9 +16,8 @@ import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, html } from "lit";
|
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 { ifDefined } from "lit/directives/if-defined.js";
|
||||||
import { until } from "lit/directives/until.js";
|
|
||||||
|
|
||||||
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.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 PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.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 {
|
interface SAMLPreviewAttribute {
|
||||||
attributes: {
|
attributes: {
|
||||||
|
@ -54,12 +60,40 @@ export class SAMLProviderViewPage extends AKElement {
|
||||||
.providersSamlRetrieve({
|
.providersSamlRetrieve({
|
||||||
id: value,
|
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 })
|
@property({ attribute: false })
|
||||||
provider?: SAMLProvider;
|
provider?: SAMLProvider;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
preview?: SAMLPreviewAttribute;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
metadata?: SAMLMetadata;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
signer?: CertificateKeyPair;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
verifier?: CertificateKeyPair;
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [
|
return [
|
||||||
PFBase,
|
PFBase,
|
||||||
|
@ -84,7 +118,7 @@ export class SAMLProviderViewPage extends AKElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderRelatedObjects(): Promise<TemplateResult> {
|
renderRelatedObjects(): TemplateResult {
|
||||||
const relatedObjects = [];
|
const relatedObjects = [];
|
||||||
if (this.provider?.assignedApplicationName) {
|
if (this.provider?.assignedApplicationName) {
|
||||||
relatedObjects.push(html`<div class="pf-c-description-list__group">
|
relatedObjects.push(html`<div class="pf-c-description-list__group">
|
||||||
|
@ -122,10 +156,7 @@ export class SAMLProviderViewPage extends AKElement {
|
||||||
</dd>
|
</dd>
|
||||||
</div>`);
|
</div>`);
|
||||||
}
|
}
|
||||||
if (this.provider?.signingKp) {
|
if (this.signer) {
|
||||||
const kp = await new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsRetrieve({
|
|
||||||
kpUuid: this.provider.signingKp,
|
|
||||||
});
|
|
||||||
relatedObjects.push(html`<div class="pf-c-description-list__group">
|
relatedObjects.push(html`<div class="pf-c-description-list__group">
|
||||||
<dt class="pf-c-description-list__term">
|
<dt class="pf-c-description-list__term">
|
||||||
<span class="pf-c-description-list__text"
|
<span class="pf-c-description-list__text"
|
||||||
|
@ -134,7 +165,9 @@ export class SAMLProviderViewPage extends AKElement {
|
||||||
</dt>
|
</dt>
|
||||||
<dd class="pf-c-description-list__description">
|
<dd class="pf-c-description-list__description">
|
||||||
<div class="pf-c-description-list__text">
|
<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
|
>${t`Download`}</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -160,7 +193,19 @@ export class SAMLProviderViewPage extends AKElement {
|
||||||
${this.renderTabOverview()}
|
${this.renderTabOverview()}
|
||||||
</section>
|
</section>
|
||||||
${this.renderTabMetadata()}
|
${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()}
|
${this.renderTabPreview()}
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
|
@ -264,7 +309,7 @@ export class SAMLProviderViewPage extends AKElement {
|
||||||
</ak-forms-modal>
|
</ak-forms-modal>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${until(this.renderRelatedObjects())}
|
${this.renderRelatedObjects()}
|
||||||
${
|
${
|
||||||
this.provider.assignedApplicationName
|
this.provider.assignedApplicationName
|
||||||
? html` <div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
? 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`
|
return html`
|
||||||
${this.provider.assignedApplicationName
|
${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
|
<div
|
||||||
class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter"
|
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>
|
</ak-action-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card__footer">
|
<div class="pf-c-card__footer">
|
||||||
${until(
|
<ak-codemirror
|
||||||
new ProvidersApi(DEFAULT_CONFIG)
|
mode="xml"
|
||||||
.providersSamlMetadataRetrieve({
|
?readOnly=${true}
|
||||||
id: this.provider.pk || 0,
|
value="${ifDefined(this.metadata?.metadata)}"
|
||||||
})
|
></ak-codemirror>
|
||||||
.then((m) => {
|
|
||||||
return html`<ak-codemirror
|
|
||||||
mode="xml"
|
|
||||||
?readOnly=${true}
|
|
||||||
value="${ifDefined(m.metadata)}"
|
|
||||||
></ak-codemirror>`;
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -421,65 +468,50 @@ export class SAMLProviderViewPage extends AKElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTabPreview(): TemplateResult {
|
renderTabPreview(): TemplateResult {
|
||||||
if (!this.provider) {
|
if (!this.preview) {
|
||||||
return html``;
|
return html`<ak-empty-state ?loading=${true}></ak-empty-state>`;
|
||||||
}
|
}
|
||||||
return html` <div
|
return html` <div
|
||||||
class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter"
|
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">
|
||||||
<div class="pf-c-card__title">${t`Example SAML attributes`}</div>
|
<div class="pf-c-card__title">${t`Example SAML attributes`}</div>
|
||||||
${until(
|
<div class="pf-c-card__body">
|
||||||
new ProvidersApi(DEFAULT_CONFIG)
|
<dl class="pf-c-description-list pf-m-2-col-on-lg">
|
||||||
.providersSamlPreviewUserRetrieve({
|
<div class="pf-c-description-list__group">
|
||||||
id: this.provider?.pk,
|
<dt class="pf-c-description-list__term">
|
||||||
})
|
<span class="pf-c-description-list__text"
|
||||||
.then((data) => {
|
>${t`NameID attribute`}</span
|
||||||
const d = data.preview as SAMLPreviewAttribute;
|
>
|
||||||
return html`
|
</dt>
|
||||||
<div class="pf-c-card__body">
|
<dd class="pf-c-description-list__description">
|
||||||
<dl class="pf-c-description-list pf-m-2-col-on-lg">
|
<div class="pf-c-description-list__text">
|
||||||
<div class="pf-c-description-list__group">
|
${this.preview?.nameID}
|
||||||
<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>
|
</div>
|
||||||
<div class="pf-c-card__body">
|
</dd>
|
||||||
<dl class="pf-c-description-list pf-m-2-col-on-lg">
|
</div>
|
||||||
${d.attributes.map((attr) => {
|
</dl>
|
||||||
return html` <div class="pf-c-description-list__group">
|
</div>
|
||||||
<dt class="pf-c-description-list__term">
|
<div class="pf-c-card__body">
|
||||||
<span class="pf-c-description-list__text"
|
<dl class="pf-c-description-list pf-m-2-col-on-lg">
|
||||||
>${attr.Name}</span
|
${this.preview?.attributes.map((attr) => {
|
||||||
>
|
return html` <div class="pf-c-description-list__group">
|
||||||
</dt>
|
<dt class="pf-c-description-list__term">
|
||||||
<dd class="pf-c-description-list__description">
|
<span class="pf-c-description-list__text">${attr.Name}</span>
|
||||||
<div class="pf-c-description-list__text">
|
</dt>
|
||||||
<ul class="pf-c-list">
|
<dd class="pf-c-description-list__description">
|
||||||
${attr.Value.map((value) => {
|
<div class="pf-c-description-list__text">
|
||||||
return html`
|
<ul class="pf-c-list">
|
||||||
<li><pre>${value}</pre></li>
|
${attr.Value.map((value) => {
|
||||||
`;
|
return html` <li><pre>${value}</pre></li> `;
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>`;
|
</div>`;
|
||||||
})}
|
})}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
`;
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import "@goauthentik/admin/providers/scim/SCIMProviderForm";
|
import "@goauthentik/admin/providers/scim/SCIMProviderForm";
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||||
import { me } from "@goauthentik/common/users";
|
|
||||||
import MDSCIMProvider from "@goauthentik/docs/providers/scim/index.md";
|
import MDSCIMProvider from "@goauthentik/docs/providers/scim/index.md";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import "@goauthentik/elements/Markdown";
|
import "@goauthentik/elements/Markdown";
|
||||||
|
@ -14,7 +13,6 @@ import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, html } from "lit";
|
import { CSSResult, TemplateResult, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
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 PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.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 PFStack from "@patternfly/patternfly/layouts/Stack/stack.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.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")
|
@customElement("ak-provider-scim-view")
|
||||||
export class SCIMProviderViewPage extends AKElement {
|
export class SCIMProviderViewPage extends AKElement {
|
||||||
|
@ -51,7 +49,7 @@ export class SCIMProviderViewPage extends AKElement {
|
||||||
provider?: SCIMProvider;
|
provider?: SCIMProvider;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
me?: SessionUser;
|
syncState?: Task;
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [
|
return [
|
||||||
|
@ -76,9 +74,6 @@ export class SCIMProviderViewPage extends AKElement {
|
||||||
if (!this.provider?.pk) return;
|
if (!this.provider?.pk) return;
|
||||||
this.providerID = this.provider?.pk;
|
this.providerID = this.provider?.pk;
|
||||||
});
|
});
|
||||||
me().then((user) => {
|
|
||||||
this.me = user;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
|
@ -86,7 +81,22 @@ export class SCIMProviderViewPage extends AKElement {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html` <ak-tabs>
|
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()}
|
${this.renderTabOverview()}
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
|
@ -158,23 +168,13 @@ export class SCIMProviderViewPage extends AKElement {
|
||||||
<p>${t`Sync status`}</p>
|
<p>${t`Sync status`}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
${until(
|
${this.syncState
|
||||||
new ProvidersApi(DEFAULT_CONFIG)
|
? html` <ul class="pf-c-list">
|
||||||
.providersScimSyncStatusRetrieve({
|
${this.syncState.messages.map((m) => {
|
||||||
id: this.provider.pk,
|
return html`<li>${m}</li>`;
|
||||||
})
|
})}
|
||||||
.then((task) => {
|
</ul>`
|
||||||
return html` <ul class="pf-c-list">
|
: html` ${t`Sync not run yet.`} `}
|
||||||
${task.messages.map((m) => {
|
|
||||||
return html`<li>${m}</li>`;
|
|
||||||
})}
|
|
||||||
</ul>`;
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
return html`${t`Sync not run yet.`}`;
|
|
||||||
}),
|
|
||||||
"loading",
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pf-c-card__footer">
|
<div class="pf-c-card__footer">
|
||||||
|
|
|
@ -12,8 +12,7 @@ import "@goauthentik/elements/forms/ModalForm";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, html } from "lit";
|
import { CSSResult, TemplateResult, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property, state } from "lit/decorators.js";
|
||||||
import { until } from "lit/directives/until.js";
|
|
||||||
|
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
import PFCard from "@patternfly/patternfly/components/Card/card.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 PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.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")
|
@customElement("ak-source-ldap-view")
|
||||||
export class LDAPSourceViewPage extends AKElement {
|
export class LDAPSourceViewPage extends AKElement {
|
||||||
|
@ -42,6 +41,9 @@ export class LDAPSourceViewPage extends AKElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
source!: LDAPSource;
|
source!: LDAPSource;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
syncState: Task[] = [];
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFBase, PFPage, PFButton, PFGrid, PFContent, PFCard, PFDescriptionList, PFList];
|
return [PFBase, PFPage, PFButton, PFGrid, PFContent, PFCard, PFDescriptionList, PFList];
|
||||||
}
|
}
|
||||||
|
@ -63,6 +65,15 @@ export class LDAPSourceViewPage extends AKElement {
|
||||||
slot="page-overview"
|
slot="page-overview"
|
||||||
data-tab-title="${t`Overview`}"
|
data-tab-title="${t`Overview`}"
|
||||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
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-l-grid pf-m-gutter">
|
||||||
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
<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>
|
<p>${t`Sync status`}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
${until(
|
${this.syncState.length < 1
|
||||||
new SourcesApi(DEFAULT_CONFIG)
|
? html`<p>${t`Not synced yet.`}</p>`
|
||||||
.sourcesLdapSyncStatusList({
|
: html`
|
||||||
slug: this.source.slug,
|
<ul class="pf-c-list">
|
||||||
})
|
${this.syncState.map((task) => {
|
||||||
.then((tasks) => {
|
let header = "";
|
||||||
if (tasks.length < 1) {
|
if (task.status === TaskStatusEnum.Warning) {
|
||||||
return html`<p>${t`Not synced yet.`}</p>`;
|
header = t`Task finished with warnings`;
|
||||||
}
|
} else if (task.status === TaskStatusEnum.Error) {
|
||||||
return html`<ul class="pf-c-list">
|
header = t`Task finished with errors`;
|
||||||
${tasks.map((task) => {
|
} else {
|
||||||
let header = "";
|
header = t`Last sync: ${task.taskFinishTimestamp.toLocaleString()}`;
|
||||||
if (task.status === TaskStatusEnum.Warning) {
|
}
|
||||||
header = t`Task finished with warnings`;
|
return html`<li>
|
||||||
} else if (task.status === TaskStatusEnum.Error) {
|
<p>${task.taskName}</p>
|
||||||
header = t`Task finished with errors`;
|
<ul class="pf-c-list">
|
||||||
} else {
|
<li>${header}</li>
|
||||||
header = t`Last sync: ${task.taskFinishTimestamp.toLocaleString()}`;
|
${task.messages.map((m) => {
|
||||||
}
|
return html`<li>${m}</li>`;
|
||||||
return html`<li>
|
})}
|
||||||
<p>${task.taskName}</p>
|
</ul>
|
||||||
<ul class="pf-c-list">
|
</li> `;
|
||||||
<li>${header}</li>
|
})}
|
||||||
${task.messages.map((m) => {
|
</ul>
|
||||||
return html`<li>${m}</li>`;
|
`}
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
</li> `;
|
|
||||||
})}
|
|
||||||
</ul>`;
|
|
||||||
}),
|
|
||||||
"loading",
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card__footer">
|
<div class="pf-c-card__footer">
|
||||||
<ak-action-button
|
<ak-action-button
|
||||||
|
|
|
@ -12,9 +12,8 @@ import "@goauthentik/elements/forms/ModalForm";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, html } from "lit";
|
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 { ifDefined } from "lit/directives/if-defined.js";
|
||||||
import { until } from "lit/directives/until.js";
|
|
||||||
|
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
import PFCard from "@patternfly/patternfly/components/Card/card.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 PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.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")
|
@customElement("ak-source-saml-view")
|
||||||
export class SAMLSourceViewPage extends AKElement {
|
export class SAMLSourceViewPage extends AKElement {
|
||||||
|
@ -42,6 +41,9 @@ export class SAMLSourceViewPage extends AKElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
source?: SAMLSource;
|
source?: SAMLSource;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
metadata?: SAMLMetadata;
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFBase, PFPage, PFGrid, PFButton, PFContent, PFCard, PFDescriptionList];
|
return [PFBase, PFPage, PFGrid, PFButton, PFContent, PFCard, PFDescriptionList];
|
||||||
}
|
}
|
||||||
|
@ -152,35 +154,34 @@ export class SAMLSourceViewPage extends AKElement {
|
||||||
slot="page-metadata"
|
slot="page-metadata"
|
||||||
data-tab-title="${t`Metadata`}"
|
data-tab-title="${t`Metadata`}"
|
||||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
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-l-grid pf-m-gutter">
|
||||||
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
||||||
${until(
|
<div class="pf-c-card__body">
|
||||||
new SourcesApi(DEFAULT_CONFIG)
|
<ak-codemirror
|
||||||
.sourcesSamlMetadataRetrieve({
|
mode="xml"
|
||||||
slug: this.source.slug,
|
?readOnly=${true}
|
||||||
})
|
value="${ifDefined(this.metadata?.metadata)}"
|
||||||
.then((m) => {
|
></ak-codemirror>
|
||||||
return html`
|
</div>
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__footer">
|
||||||
<ak-codemirror
|
<a
|
||||||
mode="xml"
|
class="pf-c-button pf-m-primary"
|
||||||
?readOnly=${true}
|
target="_blank"
|
||||||
value="${ifDefined(m.metadata)}"
|
href=${ifDefined(this.metadata?.downloadUrl)}
|
||||||
></ak-codemirror>
|
>
|
||||||
</div>
|
${t`Download`}
|
||||||
<div class="pf-c-card__footer">
|
</a>
|
||||||
<a
|
</div>
|
||||||
class="pf-c-button pf-m-primary"
|
|
||||||
target="_blank"
|
|
||||||
href=${ifDefined(m.downloadUrl)}
|
|
||||||
>
|
|
||||||
${t`Download`}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import "@goauthentik/admin/stages/StageWizard";
|
import "@goauthentik/admin/stages/StageWizard";
|
||||||
import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm";
|
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_sms/AuthenticatorSMSStageForm";
|
||||||
import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm";
|
import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm";
|
||||||
import "@goauthentik/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm";
|
import "@goauthentik/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm";
|
||||||
|
@ -33,7 +34,6 @@ import { t } from "@lingui/macro";
|
||||||
import { TemplateResult, html } from "lit";
|
import { TemplateResult, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
import { until } from "lit/directives/until.js";
|
|
||||||
|
|
||||||
import { Stage, StagesApi } from "@goauthentik/api";
|
import { Stage, StagesApi } from "@goauthentik/api";
|
||||||
|
|
||||||
|
@ -100,20 +100,24 @@ export class StageListPage extends TablePage<Stage> {
|
||||||
</ak-forms-delete-bulk>`;
|
</ak-forms-delete-bulk>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderStageActions(stage: Stage): Promise<TemplateResult> {
|
renderStageActions(stage: Stage): TemplateResult {
|
||||||
if (stage.component === "ak-stage-authenticator-duo-form") {
|
switch (stage.component) {
|
||||||
await import("@goauthentik/admin/stages/authenticator_duo/DuoDeviceImportForm");
|
case "ak-stage-authenticator-duo-form":
|
||||||
return html`<ak-forms-modal>
|
return html`<ak-forms-modal>
|
||||||
<span slot="submit">${t`Import`}</span>
|
<span slot="submit">${t`Import`}</span>
|
||||||
<span slot="header">${t`Import Duo device`}</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
|
||||||
</ak-stage-authenticator-duo-device-import-form>
|
slot="form"
|
||||||
<button slot="trigger" class="pf-c-button pf-m-plain">
|
.instancePk=${stage.pk}
|
||||||
<i class="fas fa-file-import"></i>
|
>
|
||||||
</button>
|
</ak-stage-authenticator-duo-device-import-form>
|
||||||
</ak-forms-modal>`;
|
<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[] {
|
row(item: Stage): TemplateResult[] {
|
||||||
|
@ -144,7 +148,7 @@ export class StageListPage extends TablePage<Stage> {
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
</button>
|
</button>
|
||||||
</ak-forms-modal>
|
</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/UserForm";
|
||||||
import "@goauthentik/admin/users/UserPasswordForm";
|
import "@goauthentik/admin/users/UserPasswordForm";
|
||||||
import "@goauthentik/admin/users/UserResetEmailForm";
|
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 { MessageLevel } from "@goauthentik/common/messages";
|
||||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||||
import { first } from "@goauthentik/common/utils";
|
import { first } from "@goauthentik/common/utils";
|
||||||
|
import { rootInterface } from "@goauthentik/elements/Base";
|
||||||
import { PFColor } from "@goauthentik/elements/Label";
|
import { PFColor } from "@goauthentik/elements/Label";
|
||||||
import "@goauthentik/elements/buttons/ActionButton";
|
import "@goauthentik/elements/buttons/ActionButton";
|
||||||
import "@goauthentik/elements/buttons/Dropdown";
|
import "@goauthentik/elements/buttons/Dropdown";
|
||||||
|
@ -25,7 +26,6 @@ import { t } from "@lingui/macro";
|
||||||
import { CSSResult, TemplateResult, html } from "lit";
|
import { CSSResult, TemplateResult, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
import { customElement, property, state } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.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 PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
|
||||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.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>
|
<i class="fas fa-edit"></i>
|
||||||
</button>
|
</button>
|
||||||
</ak-forms-modal>
|
</ak-forms-modal>
|
||||||
${until(
|
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.Impersonate)
|
||||||
config().then((config) => {
|
? html`
|
||||||
if (config.capabilities.includes(CapabilitiesEnum.Impersonate)) {
|
<a
|
||||||
return html`<a
|
class="pf-c-button pf-m-tertiary"
|
||||||
class="pf-c-button pf-m-tertiary"
|
href="${`/-/impersonation/${item.pk}/`}"
|
||||||
href="${`/-/impersonation/${item.pk}/`}"
|
>
|
||||||
>
|
${t`Impersonate`}
|
||||||
${t`Impersonate`}
|
</a>
|
||||||
</a>`;
|
`
|
||||||
}
|
: html``}`,
|
||||||
return html``;
|
|
||||||
}),
|
|
||||||
)}`,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,70 +263,61 @@ export class RelatedUserList extends Table<User> {
|
||||||
${t`Set password`}
|
${t`Set password`}
|
||||||
</button>
|
</button>
|
||||||
</ak-forms-modal>
|
</ak-forms-modal>
|
||||||
${until(
|
${rootInterface()?.tenant?.flowRecovery
|
||||||
tenant().then((tenant) => {
|
? html`
|
||||||
if (!tenant.flowRecovery) {
|
<ak-action-button
|
||||||
return html`
|
class="pf-m-secondary"
|
||||||
<p>
|
.apiRequest=${() => {
|
||||||
${t`To let a user directly reset a their password, configure a recovery flow on the currently active tenant.`}
|
return new CoreApi(DEFAULT_CONFIG)
|
||||||
</p>
|
.coreUsersRecoveryRetrieve({
|
||||||
`;
|
id: item.pk,
|
||||||
}
|
})
|
||||||
return html`
|
.then((rec) => {
|
||||||
<ak-action-button
|
showMessage({
|
||||||
class="pf-m-secondary"
|
level: MessageLevel.success,
|
||||||
.apiRequest=${() => {
|
message: t`Successfully generated recovery link`,
|
||||||
return new CoreApi(DEFAULT_CONFIG)
|
description: rec.link,
|
||||||
.coreUsersRecoveryRetrieve({
|
});
|
||||||
id: item.pk || 0,
|
})
|
||||||
})
|
.catch((ex: ResponseError) => {
|
||||||
.then((rec) => {
|
ex.response.json().then(() => {
|
||||||
showMessage({
|
showMessage({
|
||||||
level: MessageLevel.success,
|
level: MessageLevel.error,
|
||||||
message: t`Successfully generated recovery link`,
|
message: t`No recovery flow is configured.`,
|
||||||
description: rec.link,
|
});
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
.catch((ex: ResponseError) => {
|
}}
|
||||||
ex.response.json().then(() => {
|
>
|
||||||
showMessage({
|
${t`Copy recovery link`}
|
||||||
level: MessageLevel.error,
|
</ak-action-button>
|
||||||
message: t`No recovery flow is configured.`,
|
${item.email
|
||||||
});
|
? html`<ak-forms-modal
|
||||||
});
|
.closeAfterSuccessfulSubmit=${false}
|
||||||
});
|
>
|
||||||
}}
|
<span slot="submit"> ${t`Send link`} </span>
|
||||||
>
|
<span slot="header">
|
||||||
${t`Copy recovery link`}
|
${t`Send recovery link to user`}
|
||||||
</ak-action-button>
|
</span>
|
||||||
${item.email
|
<ak-user-reset-email-form
|
||||||
? html`<ak-forms-modal
|
slot="form"
|
||||||
.closeAfterSuccessfulSubmit=${false}
|
.user=${item}
|
||||||
>
|
>
|
||||||
<span slot="submit">
|
</ak-user-reset-email-form>
|
||||||
${t`Send link`}
|
<button
|
||||||
</span>
|
slot="trigger"
|
||||||
<span slot="header">
|
class="pf-c-button pf-m-secondary"
|
||||||
${t`Send recovery link to user`}
|
>
|
||||||
</span>
|
${t`Email recovery link`}
|
||||||
<ak-user-reset-email-form
|
</button>
|
||||||
slot="form"
|
</ak-forms-modal>`
|
||||||
.user=${item}
|
: html`<span
|
||||||
>
|
>${t`Recovery link cannot be emailed, user has no email address saved.`}</span
|
||||||
</ak-user-reset-email-form>
|
>`}
|
||||||
<button
|
`
|
||||||
slot="trigger"
|
: html` <p>
|
||||||
class="pf-c-button pf-m-secondary"
|
${t`To let a user directly reset a their password, configure a recovery flow on the currently active tenant.`}
|
||||||
>
|
</p>`}
|
||||||
${t`Email recovery link`}
|
|
||||||
</button>
|
|
||||||
</ak-forms-modal>`
|
|
||||||
: html`<span
|
|
||||||
>${t`Recovery link cannot be emailed, user has no email address saved.`}</span
|
|
||||||
>`}
|
|
||||||
`;
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
|
import { AdminInterface } from "@goauthentik/admin/AdminInterface";
|
||||||
import "@goauthentik/admin/users/ServiceAccountForm";
|
import "@goauthentik/admin/users/ServiceAccountForm";
|
||||||
import "@goauthentik/admin/users/UserActiveForm";
|
import "@goauthentik/admin/users/UserActiveForm";
|
||||||
import "@goauthentik/admin/users/UserForm";
|
import "@goauthentik/admin/users/UserForm";
|
||||||
import "@goauthentik/admin/users/UserPasswordForm";
|
import "@goauthentik/admin/users/UserPasswordForm";
|
||||||
import "@goauthentik/admin/users/UserResetEmailForm";
|
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 { MessageLevel } from "@goauthentik/common/messages";
|
||||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||||
import { me } from "@goauthentik/common/users";
|
|
||||||
import { first } from "@goauthentik/common/utils";
|
import { first } from "@goauthentik/common/utils";
|
||||||
|
import { rootInterface } from "@goauthentik/elements/Base";
|
||||||
import { PFColor } from "@goauthentik/elements/Label";
|
import { PFColor } from "@goauthentik/elements/Label";
|
||||||
import { PFSize } from "@goauthentik/elements/Spinner";
|
import { PFSize } from "@goauthentik/elements/Spinner";
|
||||||
import "@goauthentik/elements/TreeView";
|
import "@goauthentik/elements/TreeView";
|
||||||
|
@ -23,14 +24,13 @@ import { TablePage } from "@goauthentik/elements/table/TablePage";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, html } from "lit";
|
import { CSSResult, TemplateResult, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property, state } from "lit/decorators.js";
|
||||||
import { until } from "lit/directives/until.js";
|
|
||||||
|
|
||||||
import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
|
import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
|
||||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.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")
|
@customElement("ak-user-list")
|
||||||
export class UserListPage extends TablePage<User> {
|
export class UserListPage extends TablePage<User> {
|
||||||
|
@ -56,18 +56,25 @@ export class UserListPage extends TablePage<User> {
|
||||||
@property()
|
@property()
|
||||||
activePath = getURLParam<string>("path", "/");
|
activePath = getURLParam<string>("path", "/");
|
||||||
|
|
||||||
|
@state()
|
||||||
|
userPaths?: UserPath;
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return super.styles.concat(PFDescriptionList, PFCard, PFAlert);
|
return super.styles.concat(PFDescriptionList, PFCard, PFAlert);
|
||||||
}
|
}
|
||||||
|
|
||||||
async apiEndpoint(page: number): Promise<PaginatedResponse<User>> {
|
async apiEndpoint(page: number): Promise<PaginatedResponse<User>> {
|
||||||
return new CoreApi(DEFAULT_CONFIG).coreUsersList({
|
const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
pageSize: (await uiConfig()).pagination.perPage,
|
pageSize: (await uiConfig()).pagination.perPage,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
pathStartswith: getURLParam("path", ""),
|
pathStartswith: getURLParam("path", ""),
|
||||||
});
|
});
|
||||||
|
this.userPaths = await new CoreApi(DEFAULT_CONFIG).coreUsersPathsRetrieve({
|
||||||
|
search: this.search,
|
||||||
|
});
|
||||||
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
columns(): TableColumn[] {
|
columns(): TableColumn[] {
|
||||||
|
@ -81,6 +88,10 @@ export class UserListPage extends TablePage<User> {
|
||||||
|
|
||||||
renderToolbarSelected(): TemplateResult {
|
renderToolbarSelected(): TemplateResult {
|
||||||
const disabled = this.selectedElements.length < 1;
|
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
|
return html`<ak-forms-delete-bulk
|
||||||
objectLabel=${t`User(s)`}
|
objectLabel=${t`User(s)`}
|
||||||
.objects=${this.selectedElements}
|
.objects=${this.selectedElements}
|
||||||
|
@ -102,28 +113,18 @@ export class UserListPage extends TablePage<User> {
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
${until(
|
${shouldShowWarning
|
||||||
me().then((user) => {
|
? html`<div slot="notice" class="pf-c-form__alert">
|
||||||
const shouldShowWarning = this.selectedElements.find((el) => {
|
<div class="pf-c-alert pf-m-inline pf-m-warning">
|
||||||
return el.pk === user.user.pk || el.pk == user.original?.pk;
|
<div class="pf-c-alert__icon">
|
||||||
});
|
<i class="fas fa-exclamation-circle"></i>
|
||||||
if (shouldShowWarning) {
|
</div>
|
||||||
return html`
|
<h4 class="pf-c-alert__title">
|
||||||
<div slot="notice" class="pf-c-form__alert">
|
${t`Warning: You're about to delete the user you're logged in as (${shouldShowWarning.username}). Proceed at your own risk.`}
|
||||||
<div class="pf-c-alert pf-m-inline pf-m-warning">
|
</h4>
|
||||||
<div class="pf-c-alert__icon">
|
</div>
|
||||||
<i class="fas fa-exclamation-circle"></i>
|
</div>`
|
||||||
</div>
|
: html``}
|
||||||
<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``;
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||||
${t`Delete`}
|
${t`Delete`}
|
||||||
</button>
|
</button>
|
||||||
|
@ -148,19 +149,16 @@ export class UserListPage extends TablePage<User> {
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
</button>
|
</button>
|
||||||
</ak-forms-modal>
|
</ak-forms-modal>
|
||||||
${until(
|
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.Impersonate)
|
||||||
config().then((config) => {
|
? html`
|
||||||
if (config.capabilities.includes(CapabilitiesEnum.Impersonate)) {
|
<a
|
||||||
return html`<a
|
class="pf-c-button pf-m-tertiary"
|
||||||
class="pf-c-button pf-m-tertiary"
|
href="${`/-/impersonation/${item.pk}/`}"
|
||||||
href="${`/-/impersonation/${item.pk}/`}"
|
>
|
||||||
>
|
${t`Impersonate`}
|
||||||
${t`Impersonate`}
|
</a>
|
||||||
</a>`;
|
`
|
||||||
}
|
: html``}`,
|
||||||
return html``;
|
|
||||||
}),
|
|
||||||
)}`,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +192,7 @@ export class UserListPage extends TablePage<User> {
|
||||||
return new CoreApi(
|
return new CoreApi(
|
||||||
DEFAULT_CONFIG,
|
DEFAULT_CONFIG,
|
||||||
).coreUsersPartialUpdate({
|
).coreUsersPartialUpdate({
|
||||||
id: item.pk || 0,
|
id: item.pk,
|
||||||
patchedUserRequest: {
|
patchedUserRequest: {
|
||||||
isActive: !item.isActive,
|
isActive: !item.isActive,
|
||||||
},
|
},
|
||||||
|
@ -225,70 +223,61 @@ export class UserListPage extends TablePage<User> {
|
||||||
${t`Set password`}
|
${t`Set password`}
|
||||||
</button>
|
</button>
|
||||||
</ak-forms-modal>
|
</ak-forms-modal>
|
||||||
${until(
|
${rootInterface()?.tenant?.flowRecovery
|
||||||
tenant().then((tenant) => {
|
? html`
|
||||||
if (!tenant.flowRecovery) {
|
<ak-action-button
|
||||||
return html`
|
class="pf-m-secondary"
|
||||||
<p>
|
.apiRequest=${() => {
|
||||||
${t`To let a user directly reset a their password, configure a recovery flow on the currently active tenant.`}
|
return new CoreApi(DEFAULT_CONFIG)
|
||||||
</p>
|
.coreUsersRecoveryRetrieve({
|
||||||
`;
|
id: item.pk,
|
||||||
}
|
})
|
||||||
return html`
|
.then((rec) => {
|
||||||
<ak-action-button
|
showMessage({
|
||||||
class="pf-m-secondary"
|
level: MessageLevel.success,
|
||||||
.apiRequest=${() => {
|
message: t`Successfully generated recovery link`,
|
||||||
return new CoreApi(DEFAULT_CONFIG)
|
description: rec.link,
|
||||||
.coreUsersRecoveryRetrieve({
|
});
|
||||||
id: item.pk || 0,
|
})
|
||||||
})
|
.catch((ex: ResponseError) => {
|
||||||
.then((rec) => {
|
ex.response.json().then(() => {
|
||||||
showMessage({
|
showMessage({
|
||||||
level: MessageLevel.success,
|
level: MessageLevel.error,
|
||||||
message: t`Successfully generated recovery link`,
|
message: t`No recovery flow is configured.`,
|
||||||
description: rec.link,
|
});
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
.catch((ex: ResponseError) => {
|
}}
|
||||||
ex.response.json().then(() => {
|
>
|
||||||
showMessage({
|
${t`Copy recovery link`}
|
||||||
level: MessageLevel.error,
|
</ak-action-button>
|
||||||
message: t`No recovery flow is configured.`,
|
${item.email
|
||||||
});
|
? html`<ak-forms-modal
|
||||||
});
|
.closeAfterSuccessfulSubmit=${false}
|
||||||
});
|
>
|
||||||
}}
|
<span slot="submit"> ${t`Send link`} </span>
|
||||||
>
|
<span slot="header">
|
||||||
${t`Copy recovery link`}
|
${t`Send recovery link to user`}
|
||||||
</ak-action-button>
|
</span>
|
||||||
${item.email
|
<ak-user-reset-email-form
|
||||||
? html`<ak-forms-modal
|
slot="form"
|
||||||
.closeAfterSuccessfulSubmit=${false}
|
.user=${item}
|
||||||
>
|
>
|
||||||
<span slot="submit">
|
</ak-user-reset-email-form>
|
||||||
${t`Send link`}
|
<button
|
||||||
</span>
|
slot="trigger"
|
||||||
<span slot="header">
|
class="pf-c-button pf-m-secondary"
|
||||||
${t`Send recovery link to user`}
|
>
|
||||||
</span>
|
${t`Email recovery link`}
|
||||||
<ak-user-reset-email-form
|
</button>
|
||||||
slot="form"
|
</ak-forms-modal>`
|
||||||
.user=${item}
|
: html`<span
|
||||||
>
|
>${t`Recovery link cannot be emailed, user has no email address saved.`}</span
|
||||||
</ak-user-reset-email-form>
|
>`}
|
||||||
<button
|
`
|
||||||
slot="trigger"
|
: html` <p>
|
||||||
class="pf-c-button pf-m-secondary"
|
${t`To let a user directly reset a their password, configure a recovery flow on the currently active tenant.`}
|
||||||
>
|
</p>`}
|
||||||
${t`Email recovery link`}
|
|
||||||
</button>
|
|
||||||
</ak-forms-modal>`
|
|
||||||
: html`<span
|
|
||||||
>${t`Recovery link cannot be emailed, user has no email address saved.`}</span
|
|
||||||
>`}
|
|
||||||
`;
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
|
@ -323,18 +312,10 @@ export class UserListPage extends TablePage<User> {
|
||||||
<div class="pf-c-card">
|
<div class="pf-c-card">
|
||||||
<div class="pf-c-card__title">${t`User folders`}</div>
|
<div class="pf-c-card__title">${t`User folders`}</div>
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
${until(
|
<ak-treeview
|
||||||
new CoreApi(DEFAULT_CONFIG)
|
.items=${this.userPaths?.paths || []}
|
||||||
.coreUsersPathsRetrieve({
|
activePath=${this.activePath}
|
||||||
search: this.search,
|
></ak-treeview>
|
||||||
})
|
|
||||||
.then((paths) => {
|
|
||||||
return html`<ak-treeview
|
|
||||||
.items=${paths.paths}
|
|
||||||
activePath=${this.activePath}
|
|
||||||
></ak-treeview>`;
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
|
@ -3,10 +3,10 @@ import "@goauthentik/admin/users/UserActiveForm";
|
||||||
import "@goauthentik/admin/users/UserChart";
|
import "@goauthentik/admin/users/UserChart";
|
||||||
import "@goauthentik/admin/users/UserForm";
|
import "@goauthentik/admin/users/UserForm";
|
||||||
import "@goauthentik/admin/users/UserPasswordForm";
|
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 { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||||
import { MessageLevel } from "@goauthentik/common/messages";
|
import { MessageLevel } from "@goauthentik/common/messages";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||||
import "@goauthentik/elements/CodeMirror";
|
import "@goauthentik/elements/CodeMirror";
|
||||||
import { PFColor } from "@goauthentik/elements/Label";
|
import { PFColor } from "@goauthentik/elements/Label";
|
||||||
import "@goauthentik/elements/PageHeader";
|
import "@goauthentik/elements/PageHeader";
|
||||||
|
@ -27,7 +27,6 @@ import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, html } from "lit";
|
import { CSSResult, TemplateResult, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { until } from "lit/directives/until.js";
|
|
||||||
|
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||||
|
@ -197,21 +196,18 @@ export class UserViewPage extends AKElement {
|
||||||
</button>
|
</button>
|
||||||
</ak-forms-modal>
|
</ak-forms-modal>
|
||||||
</div>
|
</div>
|
||||||
${until(
|
${rootInterface()?.config?.capabilities.includes(
|
||||||
config().then((config) => {
|
CapabilitiesEnum.Impersonate,
|
||||||
if (config.capabilities.includes(CapabilitiesEnum.Impersonate)) {
|
)
|
||||||
return html` <div class="pf-c-card__footer">
|
? html`
|
||||||
<a
|
<a
|
||||||
class="pf-c-button pf-m-tertiary"
|
class="pf-c-button pf-m-tertiary"
|
||||||
href="${`/-/impersonation/${this.user?.pk}/`}"
|
href="${`/-/impersonation/${this.user?.pk}/`}"
|
||||||
>
|
>
|
||||||
${t`Impersonate`}
|
${t`Impersonate`}
|
||||||
</a>
|
</a>
|
||||||
</div>`;
|
`
|
||||||
}
|
: html``}
|
||||||
return html``;
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
<div class="pf-c-card__footer">
|
<div class="pf-c-card__footer">
|
||||||
<ak-user-active-form
|
<ak-user-active-form
|
||||||
.obj=${this.user}
|
.obj=${this.user}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { DEFAULT_CONFIG, tenant } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import {
|
import {
|
||||||
EVENT_API_DRAWER_TOGGLE,
|
EVENT_API_DRAWER_TOGGLE,
|
||||||
EVENT_NOTIFICATION_DRAWER_TOGGLE,
|
EVENT_NOTIFICATION_DRAWER_TOGGLE,
|
||||||
|
@ -8,7 +8,7 @@ import {
|
||||||
} from "@goauthentik/common/constants";
|
} from "@goauthentik/common/constants";
|
||||||
import { currentInterface } from "@goauthentik/common/sentry";
|
import { currentInterface } from "@goauthentik/common/sentry";
|
||||||
import { me } from "@goauthentik/common/users";
|
import { me } from "@goauthentik/common/users";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||||
|
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
|
|
||||||
|
@ -35,17 +35,16 @@ export class PageHeader extends AKElement {
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
set header(value: string) {
|
set header(value: string) {
|
||||||
tenant().then((tenant) => {
|
const tenant = rootInterface()?.tenant;
|
||||||
const currentIf = currentInterface();
|
const currentIf = currentInterface();
|
||||||
let title = tenant.brandingTitle || TITLE_DEFAULT;
|
let title = tenant?.brandingTitle || TITLE_DEFAULT;
|
||||||
if (currentIf === "admin") {
|
if (currentIf === "admin") {
|
||||||
title = `${t`Admin`} - ${title}`;
|
title = `${t`Admin`} - ${title}`;
|
||||||
}
|
}
|
||||||
if (value !== "") {
|
if (value !== "") {
|
||||||
title = `${value} - ${title}`;
|
title = `${value} - ${title}`;
|
||||||
}
|
}
|
||||||
document.title = title;
|
document.title = title;
|
||||||
});
|
|
||||||
this._header = value;
|
this._header = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,12 +73,8 @@ export class Tabs extends AKElement {
|
||||||
updateURLParams(params);
|
updateURLParams(params);
|
||||||
const page = this.querySelector(`[slot='${this.currentPage}']`);
|
const page = this.querySelector(`[slot='${this.currentPage}']`);
|
||||||
if (!page) return;
|
if (!page) return;
|
||||||
page.dispatchEvent(
|
page.dispatchEvent(new CustomEvent(EVENT_REFRESH));
|
||||||
new CustomEvent(EVENT_REFRESH, {
|
page.dispatchEvent(new CustomEvent("activate"));
|
||||||
bubbles: true,
|
|
||||||
composed: true,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTab(page: Element): TemplateResult {
|
renderTab(page: Element): TemplateResult {
|
||||||
|
@ -94,10 +90,10 @@ export class Tabs extends AKElement {
|
||||||
const pages = Array.from(this.querySelectorAll(":scope > [slot^='page-']"));
|
const pages = Array.from(this.querySelectorAll(":scope > [slot^='page-']"));
|
||||||
if (window.location.hash.includes(ROUTE_SEPARATOR)) {
|
if (window.location.hash.includes(ROUTE_SEPARATOR)) {
|
||||||
const params = getURLParams();
|
const params = getURLParams();
|
||||||
if (this.pageIdentifier in params) {
|
if (this.pageIdentifier in params && !this.currentPage) {
|
||||||
if (this.querySelector(`[slot='${params[this.pageIdentifier]}']`) !== null) {
|
if (this.querySelector(`[slot='${params[this.pageIdentifier]}']`) !== null) {
|
||||||
// To update the URL to match with the current slot
|
// To update the URL to match with the current slot
|
||||||
this.currentPage = params[this.pageIdentifier] as string;
|
this.onClick(params[this.pageIdentifier] as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,12 @@ export class AggregatePromiseCard extends AggregateCard {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
promise?: Promise<Record<string, unknown>>;
|
promise?: Promise<Record<string, unknown>>;
|
||||||
|
|
||||||
promiseProxy(): Promise<TemplateResult> {
|
async promiseProxy(): Promise<TemplateResult> {
|
||||||
if (!this.promise) {
|
if (!this.promise) {
|
||||||
return new Promise<TemplateResult>(() => html``);
|
return html``;
|
||||||
}
|
}
|
||||||
return this.promise.then((s) => {
|
const value = await this.promise;
|
||||||
return html`<i class="fa fa-check-circle"></i> ${s.toString()}`;
|
return html`<i class="fa fa-check-circle"></i> ${value.toString()}`;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInner(): TemplateResult {
|
renderInner(): TemplateResult {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import { CSSResult, css } from "lit";
|
import { CSSResult, css } from "lit";
|
||||||
import { TemplateResult, html } from "lit";
|
import { TemplateResult, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { until } from "lit/directives/until.js";
|
|
||||||
|
|
||||||
import PFNav from "@patternfly/patternfly/components/Nav/nav.css";
|
import PFNav from "@patternfly/patternfly/components/Nav/nav.css";
|
||||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||||
|
@ -72,9 +71,6 @@ export class SidebarItem extends AKElement {
|
||||||
@property()
|
@property()
|
||||||
path?: string;
|
path?: string;
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
condition: () => Promise<boolean> = async () => true;
|
|
||||||
|
|
||||||
activeMatchers: RegExp[] = [];
|
activeMatchers: RegExp[] = [];
|
||||||
|
|
||||||
@property({ type: Boolean })
|
@property({ type: Boolean })
|
||||||
|
@ -145,16 +141,10 @@ export class SidebarItem extends AKElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
return html`${until(this.renderInner())}`;
|
return this.renderInner();
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderInner(): Promise<TemplateResult> {
|
renderInner(): TemplateResult {
|
||||||
if (this.condition) {
|
|
||||||
const result = await this.condition();
|
|
||||||
if (!result) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.childItems.length > 0) {
|
if (this.childItems.length > 0) {
|
||||||
return html`<li
|
return html`<li
|
||||||
class="pf-c-nav__item ${this.expanded ? "pf-m-expandable pf-m-expanded" : ""}"
|
class="pf-c-nav__item ${this.expanded ? "pf-m-expandable pf-m-expanded" : ""}"
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { me } from "@goauthentik/common/users";
|
import { AdminInterface } from "@goauthentik/admin/AdminInterface";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||||
import { customElement } from "lit/decorators.js";
|
import { customElement } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
import { until } from "lit/directives/until.js";
|
|
||||||
|
|
||||||
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
|
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
|
||||||
import PFNav from "@patternfly/patternfly/components/Nav/nav.css";
|
import PFNav from "@patternfly/patternfly/components/Nav/nav.css";
|
||||||
|
@ -34,18 +33,12 @@ export class SidebarUser extends AKElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
|
const me = rootInterface<AdminInterface>()?.user;
|
||||||
return html`
|
return html`
|
||||||
<a href="/if/user/#/settings" class="pf-c-nav__link user-avatar" id="user-settings">
|
<a href="/if/user/#/settings" class="pf-c-nav__link user-avatar" id="user-settings">
|
||||||
${until(
|
${me
|
||||||
me().then((u) => {
|
? html`<img class="pf-c-avatar" src="${ifDefined(me.user.avatar)}" alt="" />`
|
||||||
return html`<img
|
: html``}
|
||||||
class="pf-c-avatar"
|
|
||||||
src="${ifDefined(u.user.avatar)}"
|
|
||||||
alt=""
|
|
||||||
/>`;
|
|
||||||
}),
|
|
||||||
html``,
|
|
||||||
)}
|
|
||||||
</a>
|
</a>
|
||||||
<a href="/flows/-/default/invalidation/" class="pf-c-nav__link user-logout" id="logout">
|
<a href="/flows/-/default/invalidation/" class="pf-c-nav__link user-logout" id="logout">
|
||||||
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
|
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { DEFAULT_CONFIG, tenant } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import {
|
import {
|
||||||
EVENT_FLOW_ADVANCE,
|
EVENT_FLOW_ADVANCE,
|
||||||
EVENT_FLOW_INSPECTOR_TOGGLE,
|
EVENT_FLOW_INSPECTOR_TOGGLE,
|
||||||
|
@ -64,13 +64,11 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
);
|
);
|
||||||
window.location.assign((value as RedirectChallenge).to);
|
window.location.assign((value as RedirectChallenge).to);
|
||||||
}
|
}
|
||||||
tenant().then((tenant) => {
|
if (value?.flowInfo?.title) {
|
||||||
if (value?.flowInfo?.title) {
|
document.title = `${value.flowInfo?.title} - ${this.tenant?.brandingTitle}`;
|
||||||
document.title = `${value.flowInfo?.title} - ${tenant.brandingTitle}`;
|
} else {
|
||||||
} else {
|
document.title = this.tenant?.brandingTitle || TITLE_DEFAULT;
|
||||||
document.title = tenant.brandingTitle || TITLE_DEFAULT;
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,15 +525,13 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
<footer class="pf-c-login__footer">
|
<footer class="pf-c-login__footer">
|
||||||
<p></p>
|
<p></p>
|
||||||
<ul class="pf-c-list pf-m-inline">
|
<ul class="pf-c-list pf-m-inline">
|
||||||
${until(
|
${this.tenant?.uiFooterLinks?.map((link) => {
|
||||||
this.tenant?.uiFooterLinks?.map((link) => {
|
return html`<li>
|
||||||
return html`<li>
|
<a href="${link.href || ""}"
|
||||||
<a href="${link.href || ""}"
|
>${link.name}</a
|
||||||
>${link.name}</a
|
>
|
||||||
>
|
</li>`;
|
||||||
</li>`;
|
})}
|
||||||
}),
|
|
||||||
)}
|
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href="https://goauthentik.io?utm_source=authentik&utm_medium=flow"
|
href="https://goauthentik.io?utm_source=authentik&utm_medium=flow"
|
||||||
|
|
|
@ -40,6 +40,7 @@ export class AuthenticatorValidateStage
|
||||||
set loading(value: boolean) {
|
set loading(value: boolean) {
|
||||||
this.host.loading = value;
|
this.host.loading = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get loading(): boolean {
|
get loading(): boolean {
|
||||||
return this.host.loading;
|
return this.host.loading;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
|
||||||
import { me } from "@goauthentik/common/users";
|
|
||||||
import { truncateWords } from "@goauthentik/common/utils";
|
import { truncateWords } from "@goauthentik/common/utils";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||||
|
import { UserInterface } from "@goauthentik/user/UserInterface";
|
||||||
|
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
import { until } from "lit/directives/until.js";
|
|
||||||
|
|
||||||
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
|
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
|
@ -80,6 +78,7 @@ export class LibraryApplication extends AKElement {
|
||||||
if (!this.application) {
|
if (!this.application) {
|
||||||
return html`<ak-spinner></ak-spinner>`;
|
return html`<ak-spinner></ak-spinner>`;
|
||||||
}
|
}
|
||||||
|
const me = rootInterface<UserInterface>()?.me;
|
||||||
return html` <div
|
return html` <div
|
||||||
class="pf-c-card pf-m-hoverable pf-m-compact ${this.selected
|
class="pf-c-card pf-m-hoverable pf-m-compact ${this.selected
|
||||||
? "pf-m-selectable pf-m-selected"
|
? "pf-m-selectable pf-m-selected"
|
||||||
|
@ -93,24 +92,16 @@ export class LibraryApplication extends AKElement {
|
||||||
>
|
>
|
||||||
${this.renderIcon()}
|
${this.renderIcon()}
|
||||||
</a>
|
</a>
|
||||||
${until(
|
${rootInterface()?.uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser
|
||||||
uiConfig().then((config) => {
|
? html`
|
||||||
if (!config.enabledFeatures.applicationEdit) {
|
<a
|
||||||
return html``;
|
class="pf-c-button pf-m-control pf-m-small"
|
||||||
}
|
href="/if/admin/#/core/applications/${this.application?.slug}"
|
||||||
return me().then((u) => {
|
>
|
||||||
if (!u.user.isSuperuser) return html``;
|
<i class="fas fa-pencil-alt"></i>
|
||||||
return html`
|
</a>
|
||||||
<a
|
`
|
||||||
class="pf-c-button pf-m-control pf-m-small"
|
: html``}
|
||||||
href="/if/admin/#/core/applications/${this.application?.slug}"
|
|
||||||
>
|
|
||||||
<i class="fas fa-pencil-alt"></i>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card__title">
|
<div class="pf-c-card__title">
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { LayoutType, UIConfig, uiConfig } from "@goauthentik/common/ui/config";
|
import { LayoutType } from "@goauthentik/common/ui/config";
|
||||||
import { groupBy } from "@goauthentik/common/utils";
|
import { groupBy } from "@goauthentik/common/utils";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||||
import "@goauthentik/elements/EmptyState";
|
import "@goauthentik/elements/EmptyState";
|
||||||
import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch";
|
import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch";
|
||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||||
|
@ -12,7 +12,7 @@ import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { until } from "lit/directives/until.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||||
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
||||||
|
@ -130,10 +130,11 @@ export class LibraryPage extends AKElement {
|
||||||
return groupBy(this.filterApps(), (app) => app.group || "");
|
return groupBy(this.filterApps(), (app) => app.group || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
renderApps(config: UIConfig): TemplateResult {
|
renderApps(): TemplateResult {
|
||||||
let groupClass = "";
|
let groupClass = "";
|
||||||
let groupGrid = "";
|
let groupGrid = "";
|
||||||
switch (config.layout.type) {
|
const uiConfig = rootInterface()?.uiConfig;
|
||||||
|
switch (uiConfig?.layout.type) {
|
||||||
case LayoutType.row:
|
case LayoutType.row:
|
||||||
groupClass = "pf-m-12-col";
|
groupClass = "pf-m-12-col";
|
||||||
groupGrid =
|
groupGrid =
|
||||||
|
@ -161,7 +162,7 @@ export class LibraryPage extends AKElement {
|
||||||
return html`<ak-library-app
|
return html`<ak-library-app
|
||||||
class="pf-l-grid__item"
|
class="pf-l-grid__item"
|
||||||
.application=${app}
|
.application=${app}
|
||||||
background=${config.theme.cardBackground}
|
background=${ifDefined(uiConfig?.theme.cardBackground)}
|
||||||
?selected=${app.slug === this.selectedApp?.slug}
|
?selected=${app.slug === this.selectedApp?.slug}
|
||||||
></ak-library-app>`;
|
></ak-library-app>`;
|
||||||
})}
|
})}
|
||||||
|
@ -172,57 +173,48 @@ export class LibraryPage extends AKElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
return html`${until(
|
return html`<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||||
uiConfig().then((config) => {
|
<div class="pf-c-content header">
|
||||||
return html`<main
|
<h1>${t`My applications`}</h1>
|
||||||
role="main"
|
${rootInterface()?.uiConfig?.enabledFeatures.search
|
||||||
class="pf-c-page__main"
|
? html`<input
|
||||||
tabindex="-1"
|
@input=${(ev: InputEvent) => {
|
||||||
id="main-content"
|
this.query = (ev.target as HTMLInputElement).value;
|
||||||
>
|
updateURLParams({
|
||||||
<div class="pf-c-content header">
|
search: this.query,
|
||||||
<h1>${t`My applications`}</h1>
|
});
|
||||||
${config.enabledFeatures.search
|
if (!this.fuse) return;
|
||||||
? html`<input
|
const apps = this.fuse.search(this.query);
|
||||||
@input=${(ev: InputEvent) => {
|
if (apps.length < 1) return;
|
||||||
this.query = (ev.target as HTMLInputElement).value;
|
this.selectedApp = apps[0].item;
|
||||||
updateURLParams({
|
}}
|
||||||
search: this.query,
|
@keydown=${(ev: KeyboardEvent) => {
|
||||||
});
|
if (ev.key === "Enter" && this.selectedApp?.launchUrl) {
|
||||||
if (!this.fuse) return;
|
window.location.assign(this.selectedApp.launchUrl);
|
||||||
const apps = this.fuse.search(this.query);
|
} else if (ev.key === "Escape") {
|
||||||
if (apps.length < 1) return;
|
(ev.target as HTMLInputElement).value = "";
|
||||||
this.selectedApp = apps[0].item;
|
this.query = "";
|
||||||
}}
|
updateURLParams({
|
||||||
@keydown=${(ev: KeyboardEvent) => {
|
search: this.query,
|
||||||
if (ev.key === "Enter" && this.selectedApp?.launchUrl) {
|
});
|
||||||
window.location.assign(this.selectedApp.launchUrl);
|
this.selectedApp = undefined;
|
||||||
} else if (ev.key === "Escape") {
|
}
|
||||||
(ev.target as HTMLInputElement).value = "";
|
}}
|
||||||
this.query = "";
|
type="text"
|
||||||
updateURLParams({
|
class="pf-u-display-none pf-u-display-block-on-md"
|
||||||
search: this.query,
|
autofocus
|
||||||
});
|
placeholder=${t`Search...`}
|
||||||
this.selectedApp = undefined;
|
/>`
|
||||||
}
|
: html``}
|
||||||
}}
|
</div>
|
||||||
type="text"
|
<section class="pf-c-page__main-section">
|
||||||
class="pf-u-display-none pf-u-display-block-on-md"
|
${loading(
|
||||||
autofocus
|
this.apps,
|
||||||
placeholder=${t`Search...`}
|
html`${this.filterApps().length > 0
|
||||||
/>`
|
? this.renderApps()
|
||||||
: html``}
|
: this.renderEmptyState()}`,
|
||||||
</div>
|
)}
|
||||||
<section class="pf-c-page__main-section">
|
</section>
|
||||||
${loading(
|
</main>`;
|
||||||
this.apps,
|
|
||||||
html`${this.filterApps().length > 0
|
|
||||||
? this.renderApps(config)
|
|
||||||
: this.renderEmptyState()}`,
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
</main>`;
|
|
||||||
}),
|
|
||||||
)}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||||
import { me } from "@goauthentik/common/users";
|
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
|
||||||
import "@goauthentik/elements/Tabs";
|
import "@goauthentik/elements/Tabs";
|
||||||
import "@goauthentik/elements/user/SessionList";
|
import "@goauthentik/elements/user/SessionList";
|
||||||
import "@goauthentik/elements/user/UserConsentList";
|
import "@goauthentik/elements/user/UserConsentList";
|
||||||
|
import { UserInterface } from "@goauthentik/user/UserInterface";
|
||||||
import "@goauthentik/user/user-settings/details/UserPassword";
|
import "@goauthentik/user/user-settings/details/UserPassword";
|
||||||
import "@goauthentik/user/user-settings/details/UserSettingsFlowExecutor";
|
import "@goauthentik/user/user-settings/details/UserSettingsFlowExecutor";
|
||||||
import "@goauthentik/user/user-settings/mfa/MFADevicesPage";
|
import "@goauthentik/user/user-settings/mfa/MFADevicesPage";
|
||||||
|
@ -16,7 +16,6 @@ import { t } from "@lingui/macro";
|
||||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||||
import { customElement, state } from "lit/decorators.js";
|
import { customElement, state } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
import { until } from "lit/directives/until.js";
|
|
||||||
|
|
||||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||||
|
@ -62,7 +61,7 @@ export class UserSettingsPage extends AKElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
userSettings!: Promise<UserSetting[]>;
|
userSettings?: UserSetting[];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -71,11 +70,14 @@ export class UserSettingsPage extends AKElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated(): void {
|
async firstUpdated(): Promise<void> {
|
||||||
this.userSettings = new StagesApi(DEFAULT_CONFIG).stagesAllUserSettingsList();
|
this.userSettings = await new StagesApi(DEFAULT_CONFIG).stagesAllUserSettingsList();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
|
const pwStage = this.userSettings?.filter(
|
||||||
|
(stage) => stage.component === "ak-user-settings-password",
|
||||||
|
);
|
||||||
return html`<div class="pf-c-page">
|
return html`<div class="pf-c-page">
|
||||||
<main role="main" class="pf-c-page__main" tabindex="-1">
|
<main role="main" class="pf-c-page__main" tabindex="-1">
|
||||||
<ak-tabs ?vertical="${true}">
|
<ak-tabs ?vertical="${true}">
|
||||||
|
@ -89,20 +91,11 @@ export class UserSettingsPage extends AKElement {
|
||||||
<ak-user-settings-flow-executor></ak-user-settings-flow-executor>
|
<ak-user-settings-flow-executor></ak-user-settings-flow-executor>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-l-stack__item">
|
<div class="pf-l-stack__item">
|
||||||
${until(
|
${pwStage
|
||||||
this.userSettings?.then((settings) => {
|
? html`<ak-user-settings-password
|
||||||
const pwStage = settings.filter(
|
configureUrl=${ifDefined(pwStage[0].configureUrl)}
|
||||||
(stage) =>
|
></ak-user-settings-password>`
|
||||||
stage.component === "ak-user-settings-password",
|
: html``}
|
||||||
);
|
|
||||||
if (pwStage.length > 0) {
|
|
||||||
return html`<ak-user-settings-password
|
|
||||||
configureUrl=${ifDefined(pwStage[0].configureUrl)}
|
|
||||||
></ak-user-settings-password>`;
|
|
||||||
}
|
|
||||||
return html``;
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -111,26 +104,20 @@ export class UserSettingsPage extends AKElement {
|
||||||
data-tab-title="${t`Sessions`}"
|
data-tab-title="${t`Sessions`}"
|
||||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||||
>
|
>
|
||||||
${until(
|
<ak-user-session-list
|
||||||
me().then((u) => {
|
targetUser=${ifDefined(
|
||||||
return html`<ak-user-session-list
|
rootInterface<UserInterface>()?.me?.user.username,
|
||||||
targetUser=${u.user.username}
|
)}
|
||||||
></ak-user-session-list>`;
|
></ak-user-session-list>
|
||||||
}),
|
|
||||||
)}
|
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
slot="page-consents"
|
slot="page-consents"
|
||||||
data-tab-title="${t`Consent`}"
|
data-tab-title="${t`Consent`}"
|
||||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||||
>
|
>
|
||||||
${until(
|
<ak-user-consent-list
|
||||||
me().then((u) => {
|
userId=${ifDefined(rootInterface<UserInterface>()?.me?.user.pk)}
|
||||||
return html`<ak-user-consent-list
|
></ak-user-consent-list>
|
||||||
userId=${u.user.pk}
|
|
||||||
></ak-user-consent-list>`;
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
slot="page-mfa"
|
slot="page-mfa"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { DEFAULT_CONFIG, tenant } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||||
import { MessageLevel } from "@goauthentik/common/messages";
|
import { MessageLevel } from "@goauthentik/common/messages";
|
||||||
import { refreshMe } from "@goauthentik/common/users";
|
import { refreshMe } from "@goauthentik/common/users";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||||
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
|
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
|
||||||
import { StageHost } from "@goauthentik/flow/stages/base";
|
import { StageHost } from "@goauthentik/flow/stages/base";
|
||||||
import "@goauthentik/user/user-settings/details/stages/prompt/PromptStage";
|
import "@goauthentik/user/user-settings/details/stages/prompt/PromptStage";
|
||||||
|
@ -22,7 +22,6 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
import {
|
import {
|
||||||
ChallengeChoices,
|
ChallengeChoices,
|
||||||
ChallengeTypes,
|
ChallengeTypes,
|
||||||
CurrentTenant,
|
|
||||||
FlowChallengeResponseRequest,
|
FlowChallengeResponseRequest,
|
||||||
FlowErrorChallenge,
|
FlowErrorChallenge,
|
||||||
FlowsApi,
|
FlowsApi,
|
||||||
|
@ -51,18 +50,10 @@ export class UserSettingsFlowExecutor extends AKElement implements StageHost {
|
||||||
@property({ type: Boolean })
|
@property({ type: Boolean })
|
||||||
loading = false;
|
loading = false;
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
tenant!: CurrentTenant;
|
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFBase, PFCard, PFPage, PFButton, PFContent];
|
return [PFBase, PFCard, PFPage, PFButton, PFContent];
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
tenant().then((tenant) => (this.tenant = tenant));
|
|
||||||
}
|
|
||||||
|
|
||||||
submit(payload?: FlowChallengeResponseRequest): Promise<boolean> {
|
submit(payload?: FlowChallengeResponseRequest): Promise<boolean> {
|
||||||
if (!payload) return Promise.reject();
|
if (!payload) return Promise.reject();
|
||||||
if (!this.challenge) return Promise.reject();
|
if (!this.challenge) return Promise.reject();
|
||||||
|
@ -93,13 +84,12 @@ export class UserSettingsFlowExecutor extends AKElement implements StageHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated(): void {
|
firstUpdated(): void {
|
||||||
tenant().then((tenant) => {
|
const tenant = rootInterface()?.tenant;
|
||||||
this.flowSlug = tenant.flowUserSettings;
|
this.flowSlug = tenant?.flowUserSettings;
|
||||||
if (!this.flowSlug) {
|
if (!this.flowSlug) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.nextChallenge();
|
this.nextChallenge();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async nextChallenge(): Promise<void> {
|
async nextChallenge(): Promise<void> {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import { t } from "@lingui/macro";
|
||||||
import { TemplateResult, html } from "lit";
|
import { TemplateResult, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
import { until } from "lit/directives/until.js";
|
|
||||||
|
|
||||||
import { AuthenticatorsApi, Device, UserSetting } from "@goauthentik/api";
|
import { AuthenticatorsApi, Device, UserSetting } from "@goauthentik/api";
|
||||||
|
|
||||||
|
@ -47,7 +46,7 @@ export function deviceTypeName(device: Device): string {
|
||||||
@customElement("ak-user-settings-mfa")
|
@customElement("ak-user-settings-mfa")
|
||||||
export class MFADevicesPage extends Table<Device> {
|
export class MFADevicesPage extends Table<Device> {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
userSettings?: Promise<UserSetting[]>;
|
userSettings?: UserSetting[];
|
||||||
|
|
||||||
checkbox = true;
|
checkbox = true;
|
||||||
|
|
||||||
|
@ -70,41 +69,32 @@ export class MFADevicesPage extends Table<Device> {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderToolbar(): TemplateResult {
|
renderToolbar(): TemplateResult {
|
||||||
|
const settings = (this.userSettings || []).filter((stage) => {
|
||||||
|
if (stage.component === "ak-user-settings-password") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return stage.configureUrl;
|
||||||
|
});
|
||||||
return html`<ak-dropdown class="pf-c-dropdown">
|
return html`<ak-dropdown class="pf-c-dropdown">
|
||||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||||
<span class="pf-c-dropdown__toggle-text">${t`Enroll`}</span>
|
<span class="pf-c-dropdown__toggle-text">${t`Enroll`}</span>
|
||||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<ul class="pf-c-dropdown__menu" hidden>
|
<ul class="pf-c-dropdown__menu" hidden>
|
||||||
${until(
|
${settings.map((stage) => {
|
||||||
this.userSettings?.then((stages) => {
|
return html`<li>
|
||||||
return stages
|
<a
|
||||||
.filter((stage) => {
|
href="${ifDefined(stage.configureUrl)}${AndNext(
|
||||||
if (stage.component === "ak-user-settings-password") {
|
`/if/user/#/settings;${JSON.stringify({
|
||||||
return false;
|
page: "page-mfa",
|
||||||
}
|
})}`,
|
||||||
return stage.configureUrl;
|
)}"
|
||||||
})
|
class="pf-c-dropdown__menu-item"
|
||||||
.map((stage) => {
|
>
|
||||||
return html`<li>
|
${stageToAuthenticatorName(stage)}
|
||||||
<a
|
</a>
|
||||||
href="${ifDefined(stage.configureUrl)}${AndNext(
|
</li>`;
|
||||||
`/if/user/#/settings;${JSON.stringify({
|
})}
|
||||||
page: "page-mfa",
|
|
||||||
})}`,
|
|
||||||
)}"
|
|
||||||
class="pf-c-dropdown__menu-item"
|
|
||||||
>
|
|
||||||
${stageToAuthenticatorName(stage)}
|
|
||||||
</a>
|
|
||||||
</li>`;
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
html`<ak-empty-state
|
|
||||||
?loading="${true}"
|
|
||||||
header=${t`Loading`}
|
|
||||||
></ak-empty-state>`,
|
|
||||||
)}
|
|
||||||
</ul>
|
</ul>
|
||||||
</ak-dropdown>
|
</ak-dropdown>
|
||||||
${super.renderToolbar()}`;
|
${super.renderToolbar()}`;
|
||||||
|
|
|
@ -12,7 +12,6 @@ import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { until } from "lit/directives/until.js";
|
|
||||||
|
|
||||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||||
import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css";
|
import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css";
|
||||||
|
@ -22,7 +21,7 @@ import { PaginatedUserSourceConnectionList, SourcesApi, UserSetting } from "@goa
|
||||||
@customElement("ak-user-settings-source")
|
@customElement("ak-user-settings-source")
|
||||||
export class UserSourceSettingsPage extends AKElement {
|
export class UserSourceSettingsPage extends AKElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
sourceSettings?: Promise<UserSetting[]>;
|
sourceSettings?: UserSetting[];
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
connections?: PaginatedUserSourceConnectionList;
|
connections?: PaginatedUserSourceConnectionList;
|
||||||
|
@ -57,7 +56,7 @@ export class UserSourceSettingsPage extends AKElement {
|
||||||
|
|
||||||
async firstUpdated(): Promise<void> {
|
async firstUpdated(): Promise<void> {
|
||||||
const user = await me();
|
const user = await me();
|
||||||
this.sourceSettings = new SourcesApi(DEFAULT_CONFIG).sourcesAllUserSettingsList();
|
this.sourceSettings = await new SourcesApi(DEFAULT_CONFIG).sourcesAllUserSettingsList();
|
||||||
this.connections = await new SourcesApi(DEFAULT_CONFIG).sourcesUserConnectionsAllList({
|
this.connections = await new SourcesApi(DEFAULT_CONFIG).sourcesUserConnectionsAllList({
|
||||||
user: user.user.pk,
|
user: user.user.pk,
|
||||||
});
|
});
|
||||||
|
@ -115,30 +114,33 @@ export class UserSourceSettingsPage extends AKElement {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ul class="pf-c-data-list" role="list">
|
<ul class="pf-c-data-list" role="list">
|
||||||
${until(
|
${this.sourceSettings
|
||||||
this.sourceSettings?.then((source) => {
|
? html`
|
||||||
if (source.length < 1) {
|
${this.sourceSettings.length < 1
|
||||||
return html`<ak-empty-state
|
? html`<ak-empty-state
|
||||||
header=${t`No services available.`}
|
header=${t`No services available.`}
|
||||||
></ak-empty-state>`;
|
></ak-empty-state>`
|
||||||
}
|
: html`
|
||||||
return source.map((source) => {
|
${this.sourceSettings.map((source) => {
|
||||||
return html`<li class="pf-c-data-list__item">
|
return html`<li class="pf-c-data-list__item">
|
||||||
<div class="pf-c-data-list__item-content">
|
<div class="pf-c-data-list__item-content">
|
||||||
<div class="pf-c-data-list__cell">
|
<div class="pf-c-data-list__cell">
|
||||||
${renderSourceIcon(source.title, source.iconUrl)}
|
${renderSourceIcon(
|
||||||
${source.title}
|
source.title,
|
||||||
</div>
|
source.iconUrl,
|
||||||
<div class="pf-c-data-list__cell">
|
)}
|
||||||
${this.renderSourceSettings(source)}
|
${source.title}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="pf-c-data-list__cell">
|
||||||
</li>`;
|
${this.renderSourceSettings(source)}
|
||||||
});
|
</div>
|
||||||
}),
|
</div>
|
||||||
html`<ak-empty-state ?loading="${true}" header=${t`Loading`}>
|
</li>`;
|
||||||
</ak-empty-state>`,
|
})}
|
||||||
)}
|
`}
|
||||||
|
`
|
||||||
|
: html`<ak-empty-state ?loading="${true}" header=${t`Loading`}>
|
||||||
|
</ak-empty-state>`}
|
||||||
</ul>`;
|
</ul>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue