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