web: re-format with prettier
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
@ -1,5 +1,13 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { Application, CoreApi } from "authentik-api";
|
||||
@ -20,11 +28,15 @@ import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css";
|
||||
|
||||
@customElement("ak-library-app")
|
||||
export class LibraryApplication extends LitElement {
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
application?: Application;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFCard, PFAvatar, AKGlobal,
|
||||
return [
|
||||
PFBase,
|
||||
PFCard,
|
||||
PFAvatar,
|
||||
AKGlobal,
|
||||
css`
|
||||
a {
|
||||
height: 100%;
|
||||
@ -48,7 +60,7 @@ export class LibraryApplication extends LitElement {
|
||||
justify-content: center;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
`
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@ -56,19 +68,28 @@ export class LibraryApplication extends LitElement {
|
||||
if (!this.application) {
|
||||
return html`<ak-spinner></ak-spinner>`;
|
||||
}
|
||||
return html` <a href="${ifDefined(this.application.launchUrl ?? "")}" class="pf-c-card pf-m-hoverable pf-m-compact">
|
||||
return html` <a
|
||||
href="${ifDefined(this.application.launchUrl ?? "")}"
|
||||
class="pf-c-card pf-m-hoverable pf-m-compact"
|
||||
>
|
||||
<div class="pf-c-card__header">
|
||||
${this.application.metaIcon
|
||||
? html`<img class="app-icon pf-c-avatar" src="${ifDefined(this.application.metaIcon)}" alt="Application Icon"/>`
|
||||
? html`<img
|
||||
class="app-icon pf-c-avatar"
|
||||
src="${ifDefined(this.application.metaIcon)}"
|
||||
alt="Application Icon"
|
||||
/>`
|
||||
: html`<i class="fas fas fa-share-square"></i>`}
|
||||
${until(me().then((u) => {
|
||||
if (!u.user.isSuperuser) return html``;
|
||||
return html`
|
||||
<a href="#/core/applications/${this.application?.slug}">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</a>
|
||||
`;
|
||||
}))}
|
||||
${until(
|
||||
me().then((u) => {
|
||||
if (!u.user.isSuperuser) return html``;
|
||||
return html`
|
||||
<a href="#/core/applications/${this.application?.slug}">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</a>
|
||||
`;
|
||||
}),
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__title">
|
||||
<p id="card-1-check-label">${this.application.name}</p>
|
||||
@ -76,15 +97,13 @@ export class LibraryApplication extends LitElement {
|
||||
<small>${this.application.metaPublisher}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">${truncate(this.application.metaDescription, 35)}</div>
|
||||
</a>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@customElement("ak-library")
|
||||
export class LibraryPage extends LitElement {
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
apps?: AKResponse<Application>;
|
||||
|
||||
pageTitle(): string {
|
||||
@ -120,20 +139,23 @@ export class LibraryPage extends LitElement {
|
||||
|
||||
renderApps(): TemplateResult {
|
||||
return html`<div class="pf-l-gallery pf-m-gutter">
|
||||
${this.apps?.results.map((app) => html`<ak-library-app .application=${app}></ak-library-app>`)}
|
||||
${this.apps?.results.map(
|
||||
(app) => html`<ak-library-app .application=${app}></ak-library-app>`,
|
||||
)}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||
<ak-page-header
|
||||
icon="pf-icon pf-icon-applications"
|
||||
header=${t`Applications`}>
|
||||
<ak-page-header icon="pf-icon pf-icon-applications" header=${t`Applications`}>
|
||||
</ak-page-header>
|
||||
<section class="pf-c-page__main-section">
|
||||
${loading(this.apps, html`${(this.apps?.results.length || 0) > 0 ?
|
||||
this.renderApps() :
|
||||
this.renderEmptyState()}`)}
|
||||
${loading(
|
||||
this.apps,
|
||||
html`${(this.apps?.results.length || 0) > 0
|
||||
? this.renderApps()
|
||||
: this.renderEmptyState()}`,
|
||||
)}
|
||||
</section>
|
||||
</main>`;
|
||||
}
|
||||
|
||||
@ -26,101 +26,173 @@ import "../../elements/PageHeader";
|
||||
|
||||
@customElement("ak-admin-overview")
|
||||
export class AdminOverviewPage extends LitElement {
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFGrid, PFPage, PFContent, AKGlobal, css`
|
||||
.row-divider {
|
||||
margin-top: -4px;
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
.graph-container {
|
||||
height: 20em;
|
||||
}
|
||||
.big-graph-container {
|
||||
height: 35em;
|
||||
}
|
||||
.card-container {
|
||||
max-height: 10em;
|
||||
}
|
||||
`];
|
||||
return [
|
||||
PFGrid,
|
||||
PFPage,
|
||||
PFContent,
|
||||
AKGlobal,
|
||||
css`
|
||||
.row-divider {
|
||||
margin-top: -4px;
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
.graph-container {
|
||||
height: 20em;
|
||||
}
|
||||
.big-graph-container {
|
||||
height: 35em;
|
||||
}
|
||||
.card-container {
|
||||
max-height: 10em;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`
|
||||
<ak-page-header
|
||||
icon=""
|
||||
header=${t`System Overview`}
|
||||
description=${t`General system status`}>
|
||||
</ak-page-header>
|
||||
<section class="pf-c-page__main-section">
|
||||
<div class="pf-l-grid pf-m-gutter">
|
||||
<!-- row 1 -->
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container">
|
||||
<ak-aggregate-card icon="pf-icon pf-icon-infrastructure" header=${t`Policies`} headerLink="#/policy/policies">
|
||||
<ak-admin-status-chart-policy></ak-admin-status-chart-policy>
|
||||
</ak-aggregate-card>
|
||||
return html` <ak-page-header
|
||||
icon=""
|
||||
header=${t`System Overview`}
|
||||
description=${t`General system status`}
|
||||
>
|
||||
</ak-page-header>
|
||||
<section class="pf-c-page__main-section">
|
||||
<div class="pf-l-grid pf-m-gutter">
|
||||
<!-- row 1 -->
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
|
||||
>
|
||||
<ak-aggregate-card
|
||||
icon="pf-icon pf-icon-infrastructure"
|
||||
header=${t`Policies`}
|
||||
headerLink="#/policy/policies"
|
||||
>
|
||||
<ak-admin-status-chart-policy></ak-admin-status-chart-policy>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
|
||||
>
|
||||
<ak-aggregate-card
|
||||
icon="pf-icon pf-icon-server"
|
||||
header=${t`Flows`}
|
||||
headerLink="#/flow/flows"
|
||||
>
|
||||
<ak-admin-status-chart-flow></ak-admin-status-chart-flow>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
|
||||
>
|
||||
<ak-aggregate-card
|
||||
icon="fa fa-sync-alt"
|
||||
header=${t`Outpost status`}
|
||||
headerLink="#/outpost/outposts"
|
||||
>
|
||||
<ak-admin-status-chart-outpost></ak-admin-status-chart-outpost>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
|
||||
>
|
||||
<ak-aggregate-card
|
||||
icon="fa fa-sync-alt"
|
||||
header=${t`Users`}
|
||||
headerLink="#/identity/users"
|
||||
>
|
||||
<ak-admin-status-chart-user-count></ak-admin-status-chart-user-count>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
|
||||
>
|
||||
<ak-aggregate-card
|
||||
icon="fa fa-sync-alt"
|
||||
header=${t`Groups`}
|
||||
headerLink="#/identity/groups"
|
||||
>
|
||||
<ak-admin-status-chart-group-count></ak-admin-status-chart-group-count>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
|
||||
>
|
||||
<ak-aggregate-card
|
||||
icon="fa fa-sync-alt"
|
||||
header=${t`LDAP Sync status`}
|
||||
headerLink="#/core/sources"
|
||||
>
|
||||
<ak-admin-status-chart-ldap-sync></ak-admin-status-chart-ldap-sync>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-12-col row-divider">
|
||||
<hr />
|
||||
</div>
|
||||
<!-- row 2 -->
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-md pf-m-4-col-on-xl card-container"
|
||||
>
|
||||
<ak-admin-status-version
|
||||
icon="pf-icon pf-icon-bundle"
|
||||
header=${t`Version`}
|
||||
headerLink="https://github.com/goauthentik/authentik/releases"
|
||||
>
|
||||
</ak-admin-status-version>
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-2-col-on-md pf-m-2-col-on-xl card-container"
|
||||
>
|
||||
<ak-admin-status-card-backup
|
||||
icon="fa fa-database"
|
||||
header=${t`Backup status`}
|
||||
headerLink="#/administration/system-tasks"
|
||||
>
|
||||
</ak-admin-status-card-backup>
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-3-col-on-md pf-m-3-col-on-xl card-container"
|
||||
>
|
||||
<ak-admin-status-card-workers
|
||||
icon="pf-icon pf-icon-server"
|
||||
header=${t`Workers`}
|
||||
>
|
||||
</ak-admin-status-card-workers>
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-3-col-on-md pf-m-3-col-on-xl card-container"
|
||||
>
|
||||
<ak-admin-status-system
|
||||
icon="pf-icon pf-icon-server"
|
||||
header=${t`System status`}
|
||||
>
|
||||
</ak-admin-status-system>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-12-col row-divider">
|
||||
<hr />
|
||||
</div>
|
||||
<!-- row 3 -->
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-8-col-on-2xl big-graph-container"
|
||||
>
|
||||
<ak-aggregate-card
|
||||
icon="pf-icon pf-icon-server"
|
||||
header=${t`Logins over the last 24 hours`}
|
||||
>
|
||||
<ak-charts-admin-login></ak-charts-admin-login>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-4-col-on-2xl big-graph-container"
|
||||
>
|
||||
<ak-aggregate-card
|
||||
icon="pf-icon pf-icon-server"
|
||||
header=${t`Apps with most usage`}
|
||||
>
|
||||
<ak-top-applications-table></ak-top-applications-table>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container">
|
||||
<ak-aggregate-card icon="pf-icon pf-icon-server" header=${t`Flows`} headerLink="#/flow/flows">
|
||||
<ak-admin-status-chart-flow></ak-admin-status-chart-flow>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container">
|
||||
<ak-aggregate-card icon="fa fa-sync-alt" header=${t`Outpost status`} headerLink="#/outpost/outposts">
|
||||
<ak-admin-status-chart-outpost></ak-admin-status-chart-outpost>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container">
|
||||
<ak-aggregate-card icon="fa fa-sync-alt" header=${t`Users`} headerLink="#/identity/users">
|
||||
<ak-admin-status-chart-user-count></ak-admin-status-chart-user-count>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container">
|
||||
<ak-aggregate-card icon="fa fa-sync-alt" header=${t`Groups`} headerLink="#/identity/groups">
|
||||
<ak-admin-status-chart-group-count></ak-admin-status-chart-group-count>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container">
|
||||
<ak-aggregate-card icon="fa fa-sync-alt" header=${t`LDAP Sync status`} headerLink="#/core/sources">
|
||||
<ak-admin-status-chart-ldap-sync></ak-admin-status-chart-ldap-sync>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-12-col row-divider">
|
||||
<hr>
|
||||
</div>
|
||||
<!-- row 2 -->
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-md pf-m-4-col-on-xl card-container">
|
||||
<ak-admin-status-version icon="pf-icon pf-icon-bundle" header=${t`Version`} headerLink="https://github.com/goauthentik/authentik/releases">
|
||||
</ak-admin-status-version>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-2-col-on-md pf-m-2-col-on-xl card-container">
|
||||
<ak-admin-status-card-backup icon="fa fa-database" header=${t`Backup status`} headerLink="#/administration/system-tasks">
|
||||
</ak-admin-status-card-backup>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-3-col-on-md pf-m-3-col-on-xl card-container">
|
||||
<ak-admin-status-card-workers icon="pf-icon pf-icon-server" header=${t`Workers`}>
|
||||
</ak-admin-status-card-workers>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-3-col-on-md pf-m-3-col-on-xl card-container">
|
||||
<ak-admin-status-system icon="pf-icon pf-icon-server" header=${t`System status`}>
|
||||
</ak-admin-status-system>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-12-col row-divider">
|
||||
<hr>
|
||||
</div>
|
||||
<!-- row 3 -->
|
||||
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-8-col-on-2xl big-graph-container">
|
||||
<ak-aggregate-card icon="pf-icon pf-icon-server" header=${t`Logins over the last 24 hours`}>
|
||||
<ak-charts-admin-login></ak-charts-admin-login>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-4-col-on-2xl big-graph-container">
|
||||
<ak-aggregate-card icon="pf-icon pf-icon-server" header=${t`Apps with most usage`}>
|
||||
<ak-top-applications-table></ak-top-applications-table>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
</div>
|
||||
</section>`;
|
||||
</section>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,8 +9,7 @@ import { DEFAULT_CONFIG } from "../../api/Config";
|
||||
|
||||
@customElement("ak-top-applications-table")
|
||||
export class TopApplicationsTable extends LitElement {
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
topN?: EventTopPerUser[];
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
@ -18,41 +17,43 @@ export class TopApplicationsTable extends LitElement {
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
new EventsApi(DEFAULT_CONFIG).eventsEventsTopPerUserList({
|
||||
action: "authorize_application",
|
||||
topN: 11,
|
||||
}).then((events) => {
|
||||
this.topN = events;
|
||||
});
|
||||
new EventsApi(DEFAULT_CONFIG)
|
||||
.eventsEventsTopPerUserList({
|
||||
action: "authorize_application",
|
||||
topN: 11,
|
||||
})
|
||||
.then((events) => {
|
||||
this.topN = events;
|
||||
});
|
||||
}
|
||||
|
||||
renderRow(event: EventTopPerUser): TemplateResult {
|
||||
return html`<tr role="row">
|
||||
<td role="cell">${event.application.name}</td>
|
||||
<td role="cell">${event.countedEvents}</td>
|
||||
<td role="cell">
|
||||
${event.application.name}
|
||||
</td>
|
||||
<td role="cell">
|
||||
${event.countedEvents}
|
||||
</td>
|
||||
<td role="cell">
|
||||
<progress value="${event.countedEvents}" max="${this.topN ? this.topN[0].countedEvents : 0}"></progress>
|
||||
<progress
|
||||
value="${event.countedEvents}"
|
||||
max="${this.topN ? this.topN[0].countedEvents : 0}"
|
||||
></progress>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<table class="pf-c-table pf-m-compact" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">${t`Application`}</th>
|
||||
<th role="columnheader" scope="col">${t`Logins`}</th>
|
||||
<th role="columnheader" scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
${this.topN ? this.topN.map((e) => this.renderRow(e)) : html`<ak-spinner></ak-spinner>`}
|
||||
</tbody>
|
||||
</table>`;
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">${t`Application`}</th>
|
||||
<th role="columnheader" scope="col">${t`Logins`}</th>
|
||||
<th role="columnheader" scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
${this.topN
|
||||
? this.topN.map((e) => this.renderRow(e))
|
||||
: html`<ak-spinner></ak-spinner>`}
|
||||
</tbody>
|
||||
</table>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ export interface AdminStatus {
|
||||
}
|
||||
|
||||
export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
|
||||
abstract getPrimaryValue(): Promise<T>;
|
||||
|
||||
abstract getStatus(value: T): Promise<AdminStatus>;
|
||||
@ -30,16 +29,20 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
|
||||
renderInner(): TemplateResult {
|
||||
return html`<p class="center-value">
|
||||
${until(this.getPrimaryValue().then((v) => {
|
||||
this.value = v;
|
||||
return this.getStatus(v);
|
||||
}).then((status) => {
|
||||
return html`<p>
|
||||
<i class="${status.icon}"></i> ${this.renderValue()}
|
||||
</p>
|
||||
${status.message ? html`<p class="subtext">${status.message}</p>` : html``}`;
|
||||
}), html`<ak-spinner size="${PFSize.Large}"></ak-spinner>`)}
|
||||
${until(
|
||||
this.getPrimaryValue()
|
||||
.then((v) => {
|
||||
this.value = v;
|
||||
return this.getStatus(v);
|
||||
})
|
||||
.then((status) => {
|
||||
return html`<p><i class="${status.icon}"></i> ${this.renderValue()}</p>
|
||||
${status.message
|
||||
? html`<p class="subtext">${status.message}</p>`
|
||||
: html``}`;
|
||||
}),
|
||||
html`<ak-spinner size="${PFSize.Large}"></ak-spinner>`,
|
||||
)}
|
||||
</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,22 +7,24 @@ import { convertToTitle } from "../../../utils";
|
||||
|
||||
@customElement("ak-admin-status-card-backup")
|
||||
export class BackupStatusCard extends AdminStatusCard<StatusEnum> {
|
||||
|
||||
getPrimaryValue(): Promise<StatusEnum> {
|
||||
return new AdminApi(DEFAULT_CONFIG).adminSystemTasksRetrieve({
|
||||
id: "backup_database"
|
||||
}).then((value) => {
|
||||
return value.status;
|
||||
}).catch(() => {
|
||||
// On error (probably 404), check the config and see if the server
|
||||
// can even backup
|
||||
return config().then(c => {
|
||||
if (c.capabilities.includes(CapabilitiesEnum.Backup)) {
|
||||
return StatusEnum.Error;
|
||||
}
|
||||
return StatusEnum.Warning;
|
||||
return new AdminApi(DEFAULT_CONFIG)
|
||||
.adminSystemTasksRetrieve({
|
||||
id: "backup_database",
|
||||
})
|
||||
.then((value) => {
|
||||
return value.status;
|
||||
})
|
||||
.catch(() => {
|
||||
// On error (probably 404), check the config and see if the server
|
||||
// can even backup
|
||||
return config().then((c) => {
|
||||
if (c.capabilities.includes(CapabilitiesEnum.Backup)) {
|
||||
return StatusEnum.Error;
|
||||
}
|
||||
return StatusEnum.Warning;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
renderValue(): TemplateResult {
|
||||
@ -43,9 +45,8 @@ export class BackupStatusCard extends AdminStatusCard<StatusEnum> {
|
||||
});
|
||||
default:
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle pf-m-success"
|
||||
icon: "fa fa-check-circle pf-m-success",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
|
||||
|
||||
@customElement("ak-admin-status-system")
|
||||
export class SystemStatusCard extends AdminStatusCard<System> {
|
||||
|
||||
now?: Date;
|
||||
|
||||
header = "OK";
|
||||
@ -35,12 +34,11 @@ export class SystemStatusCard extends AdminStatusCard<System> {
|
||||
}
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle pf-m-success",
|
||||
message: t`Everything is ok.`
|
||||
message: t`Everything is ok.`,
|
||||
});
|
||||
}
|
||||
|
||||
renderValue(): TemplateResult {
|
||||
return html`${this.header}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
|
||||
|
||||
@customElement("ak-admin-status-version")
|
||||
export class VersionStatusCard extends AdminStatusCard<Version> {
|
||||
|
||||
getPrimaryValue(): Promise<Version> {
|
||||
return new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
|
||||
}
|
||||
@ -26,12 +25,11 @@ export class VersionStatusCard extends AdminStatusCard<Version> {
|
||||
}
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle pf-m-success",
|
||||
message: t`Up-to-date!`
|
||||
message: t`Up-to-date!`,
|
||||
});
|
||||
}
|
||||
|
||||
renderValue(): TemplateResult {
|
||||
return html`${this.value?.versionCurrent}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ import { AdminStatus, AdminStatusCard } from "./AdminStatusCard";
|
||||
|
||||
@customElement("ak-admin-status-card-workers")
|
||||
export class WorkersStatusCard extends AdminStatusCard<number> {
|
||||
|
||||
getPrimaryValue(): Promise<number> {
|
||||
return new AdminApi(DEFAULT_CONFIG).adminWorkersRetrieve().then((workers) => {
|
||||
return workers.count;
|
||||
@ -21,9 +20,8 @@ export class WorkersStatusCard extends AdminStatusCard<number> {
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle pf-m-success"
|
||||
icon: "fa fa-check-circle pf-m-success",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -13,7 +13,6 @@ interface FlowMetrics {
|
||||
|
||||
@customElement("ak-admin-status-chart-flow")
|
||||
export class PolicyStatusChart extends AKChart<FlowMetrics> {
|
||||
|
||||
getChartType(): string {
|
||||
return "doughnut";
|
||||
}
|
||||
@ -32,9 +31,11 @@ export class PolicyStatusChart extends AKChart<FlowMetrics> {
|
||||
async apiRequest(): Promise<FlowMetrics> {
|
||||
const api = new FlowsApi(DEFAULT_CONFIG);
|
||||
const cached = (await api.flowsInstancesCacheInfoRetrieve()).count || 0;
|
||||
const count = (await api.flowsInstancesList({
|
||||
pageSize: 1
|
||||
})).pagination.count;
|
||||
const count = (
|
||||
await api.flowsInstancesList({
|
||||
pageSize: 1,
|
||||
})
|
||||
).pagination.count;
|
||||
this.centerText = count.toString();
|
||||
return {
|
||||
count: count - cached,
|
||||
@ -44,24 +45,14 @@ export class PolicyStatusChart extends AKChart<FlowMetrics> {
|
||||
|
||||
getChartData(data: FlowMetrics): ChartData {
|
||||
return {
|
||||
labels: [
|
||||
t`Total flows`,
|
||||
t`Cached flows`,
|
||||
],
|
||||
labels: [t`Total flows`, t`Cached flows`],
|
||||
datasets: [
|
||||
{
|
||||
backgroundColor: [
|
||||
"#2b9af3",
|
||||
"#3e8635",
|
||||
],
|
||||
backgroundColor: ["#2b9af3", "#3e8635"],
|
||||
spanGaps: true,
|
||||
data: [
|
||||
data.count,
|
||||
data.cached,
|
||||
],
|
||||
data: [data.count, data.cached],
|
||||
},
|
||||
]
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ interface GroupMetrics {
|
||||
|
||||
@customElement("ak-admin-status-chart-group-count")
|
||||
export class GroupCountStatusChart extends AKChart<GroupMetrics> {
|
||||
|
||||
getChartType(): string {
|
||||
return "doughnut";
|
||||
}
|
||||
@ -30,12 +29,16 @@ export class GroupCountStatusChart extends AKChart<GroupMetrics> {
|
||||
|
||||
async apiRequest(): Promise<GroupMetrics> {
|
||||
const api = new CoreApi(DEFAULT_CONFIG);
|
||||
const count = (await api.coreGroupsList({
|
||||
pageSize: 1
|
||||
})).pagination.count;
|
||||
const superusers = (await api.coreGroupsList({
|
||||
isSuperuser: true
|
||||
})).pagination.count;
|
||||
const count = (
|
||||
await api.coreGroupsList({
|
||||
pageSize: 1,
|
||||
})
|
||||
).pagination.count;
|
||||
const superusers = (
|
||||
await api.coreGroupsList({
|
||||
isSuperuser: true,
|
||||
})
|
||||
).pagination.count;
|
||||
this.centerText = count.toString();
|
||||
return {
|
||||
count: count - superusers,
|
||||
@ -45,24 +48,14 @@ export class GroupCountStatusChart extends AKChart<GroupMetrics> {
|
||||
|
||||
getChartData(data: GroupMetrics): ChartData {
|
||||
return {
|
||||
labels: [
|
||||
t`Total groups`,
|
||||
t`Superuser-groups`,
|
||||
],
|
||||
labels: [t`Total groups`, t`Superuser-groups`],
|
||||
datasets: [
|
||||
{
|
||||
backgroundColor: [
|
||||
"#2b9af3",
|
||||
"#3e8635",
|
||||
],
|
||||
backgroundColor: ["#2b9af3", "#3e8635"],
|
||||
spanGaps: true,
|
||||
data: [
|
||||
data.count,
|
||||
data.superusers,
|
||||
],
|
||||
data: [data.count, data.superusers],
|
||||
},
|
||||
]
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -14,7 +14,6 @@ interface LDAPSyncStats {
|
||||
|
||||
@customElement("ak-admin-status-chart-ldap-sync")
|
||||
export class LDAPSyncStatusChart extends AKChart<LDAPSyncStats> {
|
||||
|
||||
getChartType(): string {
|
||||
return "doughnut";
|
||||
}
|
||||
@ -36,56 +35,45 @@ export class LDAPSyncStatusChart extends AKChart<LDAPSyncStats> {
|
||||
let healthy = 0;
|
||||
let failed = 0;
|
||||
let unsynced = 0;
|
||||
await Promise.all(sources.results.map(async (element) => {
|
||||
try {
|
||||
const health = await api.sourcesLdapSyncStatusRetrieve({
|
||||
slug: element.slug,
|
||||
});
|
||||
if (health.status !== StatusEnum.Successful) {
|
||||
failed += 1;
|
||||
}
|
||||
const now = new Date().getTime();
|
||||
const maxDelta = 3600000; // 1 hour
|
||||
if (!health || (now - health.taskFinishTimestamp.getTime()) > maxDelta) {
|
||||
await Promise.all(
|
||||
sources.results.map(async (element) => {
|
||||
try {
|
||||
const health = await api.sourcesLdapSyncStatusRetrieve({
|
||||
slug: element.slug,
|
||||
});
|
||||
if (health.status !== StatusEnum.Successful) {
|
||||
failed += 1;
|
||||
}
|
||||
const now = new Date().getTime();
|
||||
const maxDelta = 3600000; // 1 hour
|
||||
if (!health || now - health.taskFinishTimestamp.getTime() > maxDelta) {
|
||||
unsynced += 1;
|
||||
} else {
|
||||
healthy += 1;
|
||||
}
|
||||
} catch {
|
||||
unsynced += 1;
|
||||
} else {
|
||||
healthy += 1;
|
||||
}
|
||||
} catch {
|
||||
unsynced += 1;
|
||||
}
|
||||
}));
|
||||
}),
|
||||
);
|
||||
this.centerText = sources.pagination.count.toString();
|
||||
return {
|
||||
healthy: sources.pagination.count === 0 ? -1 : healthy,
|
||||
failed,
|
||||
unsynced
|
||||
unsynced,
|
||||
};
|
||||
}
|
||||
|
||||
getChartData(data: LDAPSyncStats): ChartData {
|
||||
return {
|
||||
labels: [
|
||||
t`Healthy sources`,
|
||||
t`Failed sources`,
|
||||
t`Unsynced sources`,
|
||||
],
|
||||
labels: [t`Healthy sources`, t`Failed sources`, t`Unsynced sources`],
|
||||
datasets: [
|
||||
{
|
||||
backgroundColor: [
|
||||
"#3e8635",
|
||||
"#C9190B",
|
||||
"#2b9af3",
|
||||
],
|
||||
backgroundColor: ["#3e8635", "#C9190B", "#2b9af3"],
|
||||
spanGaps: true,
|
||||
data: [
|
||||
data.healthy,
|
||||
data.failed,
|
||||
data.unsynced
|
||||
],
|
||||
data: [data.healthy, data.failed, data.unsynced],
|
||||
},
|
||||
]
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -14,7 +14,6 @@ interface OutpostStats {
|
||||
|
||||
@customElement("ak-admin-status-chart-outpost")
|
||||
export class OutpostStatusChart extends AKChart<OutpostStats> {
|
||||
|
||||
getChartType(): string {
|
||||
return "doughnut";
|
||||
}
|
||||
@ -36,52 +35,41 @@ export class OutpostStatusChart extends AKChart<OutpostStats> {
|
||||
let healthy = 0;
|
||||
let outdated = 0;
|
||||
let unhealthy = 0;
|
||||
await Promise.all(outposts.results.map(async (element) => {
|
||||
const health = await api.outpostsInstancesHealthList({
|
||||
uuid: element.pk || "",
|
||||
});
|
||||
if (health.length === 0) {
|
||||
unhealthy += 1;
|
||||
}
|
||||
health.forEach(h => {
|
||||
if (h.versionOutdated) {
|
||||
outdated += 1;
|
||||
} else {
|
||||
healthy += 1;
|
||||
await Promise.all(
|
||||
outposts.results.map(async (element) => {
|
||||
const health = await api.outpostsInstancesHealthList({
|
||||
uuid: element.pk || "",
|
||||
});
|
||||
if (health.length === 0) {
|
||||
unhealthy += 1;
|
||||
}
|
||||
});
|
||||
}));
|
||||
health.forEach((h) => {
|
||||
if (h.versionOutdated) {
|
||||
outdated += 1;
|
||||
} else {
|
||||
healthy += 1;
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
this.centerText = outposts.pagination.count.toString();
|
||||
return {
|
||||
healthy: outposts.pagination.count === 0 ? -1 : healthy,
|
||||
outdated,
|
||||
unhealthy
|
||||
unhealthy,
|
||||
};
|
||||
}
|
||||
|
||||
getChartData(data: OutpostStats): ChartData {
|
||||
return {
|
||||
labels: [
|
||||
t`Healthy outposts`,
|
||||
t`Outdated outposts`,
|
||||
t`Unhealthy outposts`,
|
||||
],
|
||||
labels: [t`Healthy outposts`, t`Outdated outposts`, t`Unhealthy outposts`],
|
||||
datasets: [
|
||||
{
|
||||
backgroundColor: [
|
||||
"#3e8635",
|
||||
"#f0ab00",
|
||||
"#C9190B",
|
||||
],
|
||||
backgroundColor: ["#3e8635", "#f0ab00", "#C9190B"],
|
||||
spanGaps: true,
|
||||
data: [
|
||||
data.healthy,
|
||||
data.outdated,
|
||||
data.unhealthy
|
||||
],
|
||||
data: [data.healthy, data.outdated, data.unhealthy],
|
||||
},
|
||||
]
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -14,7 +14,6 @@ interface PolicyMetrics {
|
||||
|
||||
@customElement("ak-admin-status-chart-policy")
|
||||
export class PolicyStatusChart extends AKChart<PolicyMetrics> {
|
||||
|
||||
getChartType(): string {
|
||||
return "doughnut";
|
||||
}
|
||||
@ -33,13 +32,17 @@ export class PolicyStatusChart extends AKChart<PolicyMetrics> {
|
||||
async apiRequest(): Promise<PolicyMetrics> {
|
||||
const api = new PoliciesApi(DEFAULT_CONFIG);
|
||||
const cached = (await api.policiesAllCacheInfoRetrieve()).count || 0;
|
||||
const count = (await api.policiesAllList({
|
||||
pageSize: 1
|
||||
})).pagination.count;
|
||||
const unbound = (await api.policiesAllList({
|
||||
bindingsIsnull: true,
|
||||
promptstageIsnull: true,
|
||||
})).pagination.count;
|
||||
const count = (
|
||||
await api.policiesAllList({
|
||||
pageSize: 1,
|
||||
})
|
||||
).pagination.count;
|
||||
const unbound = (
|
||||
await api.policiesAllList({
|
||||
bindingsIsnull: true,
|
||||
promptstageIsnull: true,
|
||||
})
|
||||
).pagination.count;
|
||||
this.centerText = count.toString();
|
||||
return {
|
||||
// If we have more cache than total policies, only show that
|
||||
@ -52,27 +55,14 @@ export class PolicyStatusChart extends AKChart<PolicyMetrics> {
|
||||
|
||||
getChartData(data: PolicyMetrics): ChartData {
|
||||
return {
|
||||
labels: [
|
||||
t`Total policies`,
|
||||
t`Cached policies`,
|
||||
t`Unbound policies`,
|
||||
],
|
||||
labels: [t`Total policies`, t`Cached policies`, t`Unbound policies`],
|
||||
datasets: [
|
||||
{
|
||||
backgroundColor: [
|
||||
"#2b9af3",
|
||||
"#3e8635",
|
||||
"#f0ab00",
|
||||
],
|
||||
backgroundColor: ["#2b9af3", "#3e8635", "#f0ab00"],
|
||||
spanGaps: true,
|
||||
data: [
|
||||
data.count,
|
||||
data.cached,
|
||||
data.unbound
|
||||
],
|
||||
data: [data.count, data.cached, data.unbound],
|
||||
},
|
||||
]
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ interface UserMetrics {
|
||||
|
||||
@customElement("ak-admin-status-chart-user-count")
|
||||
export class UserCountStatusChart extends AKChart<UserMetrics> {
|
||||
|
||||
getChartType(): string {
|
||||
return "doughnut";
|
||||
}
|
||||
@ -30,12 +29,16 @@ export class UserCountStatusChart extends AKChart<UserMetrics> {
|
||||
|
||||
async apiRequest(): Promise<UserMetrics> {
|
||||
const api = new CoreApi(DEFAULT_CONFIG);
|
||||
const count = (await api.coreUsersList({
|
||||
pageSize: 1
|
||||
})).pagination.count;
|
||||
const superusers = (await api.coreUsersList({
|
||||
isSuperuser: true
|
||||
})).pagination.count;
|
||||
const count = (
|
||||
await api.coreUsersList({
|
||||
pageSize: 1,
|
||||
})
|
||||
).pagination.count;
|
||||
const superusers = (
|
||||
await api.coreUsersList({
|
||||
isSuperuser: true,
|
||||
})
|
||||
).pagination.count;
|
||||
this.centerText = count.toString();
|
||||
return {
|
||||
count: count - superusers,
|
||||
@ -45,24 +48,14 @@ export class UserCountStatusChart extends AKChart<UserMetrics> {
|
||||
|
||||
getChartData(data: UserMetrics): ChartData {
|
||||
return {
|
||||
labels: [
|
||||
t`Total users`,
|
||||
t`Superusers`,
|
||||
],
|
||||
labels: [t`Total users`, t`Superusers`],
|
||||
datasets: [
|
||||
{
|
||||
backgroundColor: [
|
||||
"#2b9af3",
|
||||
"#3e8635",
|
||||
],
|
||||
backgroundColor: ["#2b9af3", "#3e8635"],
|
||||
spanGaps: true,
|
||||
data: [
|
||||
data.count,
|
||||
data.superusers,
|
||||
],
|
||||
data: [data.count, data.superusers],
|
||||
},
|
||||
]
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,14 +9,13 @@ import "../../elements/forms/HorizontalFormElement";
|
||||
|
||||
@customElement("ak-application-check-access-form")
|
||||
export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
application!: Application;
|
||||
|
||||
@property({ attribute: false})
|
||||
@property({ attribute: false })
|
||||
result?: PolicyTestResult;
|
||||
|
||||
@property({ attribute: false})
|
||||
@property({ attribute: false })
|
||||
request?: number;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
@ -25,10 +24,12 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
|
||||
|
||||
send = (data: { forUser: number }): Promise<PolicyTestResult> => {
|
||||
this.request = data.forUser;
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationsCheckAccessRetrieve({
|
||||
slug: this.application?.slug,
|
||||
forUser: data.forUser,
|
||||
}).then(result => this.result = result);
|
||||
return new CoreApi(DEFAULT_CONFIG)
|
||||
.coreApplicationsCheckAccessRetrieve({
|
||||
slug: this.application?.slug,
|
||||
forUser: data.forUser,
|
||||
})
|
||||
.then((result) => (this.result = result));
|
||||
};
|
||||
|
||||
resetForm(): void {
|
||||
@ -37,25 +38,28 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
|
||||
}
|
||||
|
||||
renderResult(): TemplateResult {
|
||||
return html`
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Passing`}>
|
||||
return html` <ak-form-element-horizontal label=${t`Passing`}>
|
||||
<div class="pf-c-form__group-label">
|
||||
<div class="c-form__horizontal-group">
|
||||
<span class="pf-c-form__label-text">${this.result?.passing ? t`Yes` : t`No`}</span>
|
||||
<span class="pf-c-form__label-text"
|
||||
>${this.result?.passing ? t`Yes` : t`No`}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Messages`}>
|
||||
<ak-form-element-horizontal label=${t`Messages`}>
|
||||
<div class="pf-c-form__group-label">
|
||||
<div class="c-form__horizontal-group">
|
||||
<ul>
|
||||
${(this.result?.messages || []).length > 0 ?
|
||||
this.result?.messages?.map(m => {
|
||||
return html`<li><span class="pf-c-form__label-text">${m}</span></li>`;
|
||||
}) :
|
||||
html`<li><span class="pf-c-form__label-text">-</span></li>`}
|
||||
${(this.result?.messages || []).length > 0
|
||||
? this.result?.messages?.map((m) => {
|
||||
return html`<li>
|
||||
<span class="pf-c-form__label-text">${m}</span>
|
||||
</li>`;
|
||||
})
|
||||
: html`<li>
|
||||
<span class="pf-c-form__label-text">-</span>
|
||||
</li>`}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -64,22 +68,28 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`User`}
|
||||
?required=${true}
|
||||
name="forUser">
|
||||
<ak-form-element-horizontal label=${t`User`} ?required=${true} name="forUser">
|
||||
<select class="pf-c-form-control">
|
||||
${until(new CoreApi(DEFAULT_CONFIG).coreUsersList({
|
||||
ordering: "username",
|
||||
}).then(users => {
|
||||
return users.results.map(user => {
|
||||
return html`<option ?selected=${user.pk.toString() === this.request?.toString()} value=${user.pk}>${user.username}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new CoreApi(DEFAULT_CONFIG)
|
||||
.coreUsersList({
|
||||
ordering: "username",
|
||||
})
|
||||
.then((users) => {
|
||||
return users.results.map((user) => {
|
||||
return html`<option
|
||||
?selected=${user.pk.toString() === this.request?.toString()}
|
||||
value=${user.pk}
|
||||
>
|
||||
${user.username}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
${this.result ? this.renderResult(): html``}
|
||||
${this.result ? this.renderResult() : html``}
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
import { CoreApi, Application, ProvidersApi, Provider, PolicyEngineMode, CapabilitiesEnum } from "authentik-api";
|
||||
import {
|
||||
CoreApi,
|
||||
Application,
|
||||
ProvidersApi,
|
||||
Provider,
|
||||
PolicyEngineMode,
|
||||
CapabilitiesEnum,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -17,10 +24,9 @@ import { first } from "../../utils";
|
||||
|
||||
@customElement("ak-application-form")
|
||||
export class ApplicationForm extends ModelForm<Application, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<Application> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationsRetrieve({
|
||||
slug: pk
|
||||
slug: pk,
|
||||
});
|
||||
}
|
||||
|
||||
@ -47,18 +53,18 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
||||
if (this.instance) {
|
||||
writeOp = new CoreApi(DEFAULT_CONFIG).coreApplicationsUpdate({
|
||||
slug: this.instance.slug,
|
||||
applicationRequest: data
|
||||
applicationRequest: data,
|
||||
});
|
||||
} else {
|
||||
writeOp = new CoreApi(DEFAULT_CONFIG).coreApplicationsCreate({
|
||||
applicationRequest: data
|
||||
applicationRequest: data,
|
||||
});
|
||||
}
|
||||
return config().then((c) => {
|
||||
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
|
||||
const icon = this.getFormFile();
|
||||
if (icon || this.clearIcon) {
|
||||
return writeOp.then(app => {
|
||||
return writeOp.then((app) => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationsSetIconCreate({
|
||||
slug: app.slug,
|
||||
file: icon,
|
||||
@ -67,12 +73,12 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return writeOp.then(app => {
|
||||
return writeOp.then((app) => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationsSetIconUrlCreate({
|
||||
slug: app.slug,
|
||||
setIconURLRequest: {
|
||||
url: data.metaIcon || "",
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -81,7 +87,7 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
||||
|
||||
groupProviders(providers: Provider[]): TemplateResult {
|
||||
const m = new Map<string, Provider[]>();
|
||||
providers.forEach(p => {
|
||||
providers.forEach((p) => {
|
||||
if (!m.has(p.verboseName || "")) {
|
||||
m.set(p.verboseName || "", []);
|
||||
}
|
||||
@ -91,9 +97,11 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
||||
return html`
|
||||
${Array.from(m).map(([group, providers]) => {
|
||||
return html`<optgroup label=${group}>
|
||||
${providers.map(p => {
|
||||
const selected = (this.instance?.provider === p.pk) || (this.provider === p.pk);
|
||||
return html`<option ?selected=${selected} value=${ifDefined(p.pk)}>${p.name}</option>`;
|
||||
${providers.map((p) => {
|
||||
const selected = this.instance?.provider === p.pk || this.provider === p.pk;
|
||||
return html`<option ?selected=${selected} value=${ifDefined(p.pk)}>
|
||||
${p.name}
|
||||
</option>`;
|
||||
})}
|
||||
</optgroup>`;
|
||||
})}
|
||||
@ -102,129 +110,178 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${t`Application's display Name.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Slug`}
|
||||
?required=${true}
|
||||
name="slug">
|
||||
<input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Slug`} ?required=${true} name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${t`Internal application name, used in URLs.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Provider`}
|
||||
name="provider">
|
||||
<ak-form-element-horizontal label=${t`Provider`} name="provider">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.provider === undefined}>---------</option>
|
||||
${until(new ProvidersApi(DEFAULT_CONFIG).providersAllList({}).then(providers => {
|
||||
return this.groupProviders(providers.results);
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.provider === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new ProvidersApi(DEFAULT_CONFIG).providersAllList({}).then((providers) => {
|
||||
return this.groupProviders(providers.results);
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Select a provider that this application should use. Alternatively, create a new provider.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Select a provider that this application should use. Alternatively, create a new provider.`}
|
||||
</p>
|
||||
<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`Create provider`}</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>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
${until(new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create ${type.name}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button type="button" slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
${until(
|
||||
new ProvidersApi(DEFAULT_CONFIG)
|
||||
.providersAllTypesList()
|
||||
.then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header">
|
||||
${t`Create ${type.name}`}
|
||||
</span>
|
||||
<ak-proxy-form slot="form" type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button
|
||||
type="button"
|
||||
slot="trigger"
|
||||
class="pf-c-dropdown__menu-item"
|
||||
>
|
||||
${type.name}<br />
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}),
|
||||
html`<ak-spinner></ak-spinner>`,
|
||||
)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Policy engine mode`}
|
||||
?required=${true}
|
||||
name="policyEngineMode">
|
||||
name="policyEngineMode"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${PolicyEngineMode.Any} ?selected=${this.instance?.policyEngineMode === PolicyEngineMode.Any}>
|
||||
<option
|
||||
value=${PolicyEngineMode.Any}
|
||||
?selected=${this.instance?.policyEngineMode === PolicyEngineMode.Any}
|
||||
>
|
||||
${t`ANY, any policy must match to grant access.`}
|
||||
</option>
|
||||
<option value=${PolicyEngineMode.All} ?selected=${this.instance?.policyEngineMode === PolicyEngineMode.All}>
|
||||
<option
|
||||
value=${PolicyEngineMode.All}
|
||||
?selected=${this.instance?.policyEngineMode === PolicyEngineMode.All}
|
||||
>
|
||||
${t`ALL, all policies must match to grant access.`}
|
||||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`UI settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`UI settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Launch URL`}
|
||||
name="metaLaunchUrl">
|
||||
<input type="text" value="${ifDefined(this.instance?.metaLaunchUrl)}" class="pf-c-form-control">
|
||||
<p class="pf-c-form__helper-text">${t`If left empty, authentik will try to extract the launch URL based on the selected provider.`}</p>
|
||||
<ak-form-element-horizontal label=${t`Launch URL`} name="metaLaunchUrl">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.metaLaunchUrl)}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`If left empty, authentik will try to extract the launch URL based on the selected provider.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
${until(config().then((c) => {
|
||||
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
|
||||
${until(
|
||||
config().then((c) => {
|
||||
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${t`Icon`}
|
||||
name="metaIcon"
|
||||
>
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
${this.instance?.metaIcon
|
||||
? html`
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Currently set to:`}
|
||||
${this.instance?.metaIcon}
|
||||
</p>
|
||||
`
|
||||
: html``}
|
||||
</ak-form-element-horizontal>
|
||||
${this.instance?.metaIcon
|
||||
? html`
|
||||
<ak-form-element-horizontal>
|
||||
<div class="pf-c-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
@change=${(ev: Event) => {
|
||||
const target =
|
||||
ev.target as HTMLInputElement;
|
||||
this.clearIcon = target.checked;
|
||||
}}
|
||||
/>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Clear icon`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Delete currently set icon.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
`
|
||||
: html``}`;
|
||||
}
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${t`Icon`}
|
||||
name="metaIcon">
|
||||
<input type="file" value="" class="pf-c-form-control">
|
||||
${this.instance?.metaIcon ? html`
|
||||
<p class="pf-c-form__helper-text">${t`Currently set to:`} ${this.instance?.metaIcon}</p>
|
||||
`: html``}
|
||||
</ak-form-element-horizontal>
|
||||
${this.instance?.metaIcon ? html`
|
||||
<ak-form-element-horizontal>
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" @change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLInputElement;
|
||||
this.clearIcon = target.checked;
|
||||
}}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Clear icon`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Delete currently set icon.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
`: html``}`;
|
||||
}
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${t`Icon`}
|
||||
name="metaIcon">
|
||||
<input type="text" value="${first(this.instance?.metaIcon, "")}" class="pf-c-form-control">
|
||||
</ak-form-element-horizontal>`;
|
||||
}))}
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Description`}
|
||||
name="metaDescription">
|
||||
<textarea class="pf-c-form-control">${ifDefined(this.instance?.metaDescription)}</textarea>
|
||||
name="metaIcon"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.metaIcon, "")}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
</ak-form-element-horizontal>`;
|
||||
}),
|
||||
)}
|
||||
<ak-form-element-horizontal label=${t`Description`} name="metaDescription">
|
||||
<textarea class="pf-c-form-control">
|
||||
${ifDefined(this.instance?.metaDescription)}</textarea
|
||||
>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Publisher`}
|
||||
name="metaPublisher">
|
||||
<input type="text" value="${ifDefined(this.instance?.metaPublisher)}" class="pf-c-form-control">
|
||||
<ak-form-element-horizontal label=${t`Publisher`} name="metaPublisher">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.metaPublisher)}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -42,14 +42,17 @@ export class ApplicationListPage extends TablePage<Application> {
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(PFAvatar, css`
|
||||
tr td:first-child {
|
||||
width: auto;
|
||||
min-width: 0px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
`);
|
||||
return super.styles.concat(
|
||||
PFAvatar,
|
||||
css`
|
||||
tr td:first-child {
|
||||
width: auto;
|
||||
min-width: 0px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
@ -65,77 +68,68 @@ export class ApplicationListPage extends TablePage<Application> {
|
||||
|
||||
row(item: Application): TemplateResult[] {
|
||||
return [
|
||||
item.metaIcon ?
|
||||
html`<img class="app-icon pf-c-avatar" src="${item.metaIcon}" alt="${t`Application Icon`}">` :
|
||||
html`<i class="fas fa-question-circle"></i>`,
|
||||
item.metaIcon
|
||||
? html`<img
|
||||
class="app-icon pf-c-avatar"
|
||||
src="${item.metaIcon}"
|
||||
alt="${t`Application Icon`}"
|
||||
/>`
|
||||
: html`<i class="fas fa-question-circle"></i>`,
|
||||
html`<a href="#/core/applications/${item.slug}">
|
||||
<div>
|
||||
${item.name}
|
||||
</div>
|
||||
<div>${item.name}</div>
|
||||
${item.metaPublisher ? html`<small>${item.metaPublisher}</small>` : html``}
|
||||
</a>`,
|
||||
html`<code>${item.slug}</code>`,
|
||||
item.provider ?
|
||||
html`<a href="#/core/providers/${item.providerObj?.pk}">
|
||||
${item.providerObj?.name}
|
||||
</a>` :
|
||||
html`-`,
|
||||
item.provider
|
||||
? html`<a href="#/core/providers/${item.providerObj?.pk}">
|
||||
${item.providerObj?.name}
|
||||
</a>`
|
||||
: html`-`,
|
||||
html`${item.providerObj?.verboseName || "-"}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Application`}
|
||||
</span>
|
||||
<ak-application-form slot="form" .instancePk=${item.slug}>
|
||||
</ak-application-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${item.launchUrl ?
|
||||
html`<a href=${item.launchUrl} target="_blank" class="pf-c-button pf-m-secondary">
|
||||
${t`Open application`}
|
||||
</a>`:
|
||||
html``}
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Application`}
|
||||
.usedBy=${() => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationsUsedByList({
|
||||
slug: item.slug
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationsDestroy({
|
||||
slug: item.slug
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Application`} </span>
|
||||
<ak-application-form slot="form" .instancePk=${item.slug}>
|
||||
</ak-application-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
${item.launchUrl
|
||||
? html`<a
|
||||
href=${item.launchUrl}
|
||||
target="_blank"
|
||||
class="pf-c-button pf-m-secondary"
|
||||
>
|
||||
${t`Open application`}
|
||||
</a>`
|
||||
: html``}
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Application`}
|
||||
.usedBy=${() => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationsUsedByList({
|
||||
slug: item.slug,
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationsDestroy({
|
||||
slug: item.slug,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Application`}
|
||||
</span>
|
||||
<ak-application-form slot="form">
|
||||
</ak-application-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Create`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Application`} </span>
|
||||
<ak-application-form slot="form"> </ak-application-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${t`Create`}</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,21 +24,31 @@ import { ifDefined } from "lit-html/directives/if-defined";
|
||||
|
||||
@customElement("ak-application-view")
|
||||
export class ApplicationViewPage extends LitElement {
|
||||
|
||||
@property()
|
||||
set applicationSlug(value: string) {
|
||||
new CoreApi(DEFAULT_CONFIG).coreApplicationsRetrieve({
|
||||
slug: value
|
||||
}).then((app) => {
|
||||
this.application = app;
|
||||
});
|
||||
new CoreApi(DEFAULT_CONFIG)
|
||||
.coreApplicationsRetrieve({
|
||||
slug: value,
|
||||
})
|
||||
.then((app) => {
|
||||
this.application = app;
|
||||
});
|
||||
}
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
application!: Application;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFPage, PFContent, PFButton, PFDescriptionList, PFGallery, PFCard, AKGlobal];
|
||||
return [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFContent,
|
||||
PFButton,
|
||||
PFDescriptionList,
|
||||
PFGallery,
|
||||
PFCard,
|
||||
AKGlobal,
|
||||
];
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
@ -46,137 +56,178 @@ export class ApplicationViewPage extends LitElement {
|
||||
icon=${this.application?.metaIcon || ""}
|
||||
header=${this.application?.name || t`Loading`}
|
||||
description=${ifDefined(this.application?.metaPublisher)}
|
||||
.iconImage=${true}>
|
||||
.iconImage=${true}
|
||||
>
|
||||
</ak-page-header>
|
||||
${this.renderApp()}`;
|
||||
}
|
||||
|
||||
renderApp(): TemplateResult {
|
||||
if (!this.application) {
|
||||
return html`<ak-empty-state
|
||||
?loading="${true}"
|
||||
header=${t`Loading`}>
|
||||
</ak-empty-state>`;
|
||||
return html`<ak-empty-state ?loading="${true}" header=${t`Loading`}> </ak-empty-state>`;
|
||||
}
|
||||
return html`
|
||||
<ak-tabs>
|
||||
<section slot="page-overview" data-tab-title="${t`Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-l-gallery pf-m-gutter">
|
||||
<div class="pf-c-card pf-l-gallery__item">
|
||||
<div class="pf-c-card__title">${t`Related`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list">
|
||||
${this.application.providerObj ?
|
||||
html`<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Provider`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<a href="#/core/providers/${this.application.providerObj?.pk}">
|
||||
${this.application.providerObj?.name}
|
||||
</a>
|
||||
</div>
|
||||
</dd>
|
||||
</div>`:
|
||||
html``}
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Policy engine mode`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.application.policyEngineMode?.toUpperCase()}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Edit`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Application`}
|
||||
</span>
|
||||
<ak-application-form slot="form" .instancePk=${this.application.slug}>
|
||||
</ak-application-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Check access`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-forms-modal .closeAfterSuccessfulSubmit=${false}>
|
||||
<span slot="submit">
|
||||
${t`Check`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Check Application access`}
|
||||
</span>
|
||||
<ak-application-check-access-form slot="form" .application=${this.application}>
|
||||
</ak-application-check-access-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Test`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
${this.application.launchUrl ?
|
||||
html`<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Launch`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<a target="_blank" href=${this.application.launchUrl} slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Launch`}
|
||||
</a>
|
||||
</div>
|
||||
</dd>
|
||||
</div>`: html``}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card pf-l-gallery__item" style="grid-column-end: span 4;grid-row-end: span 2;">
|
||||
<div class="pf-c-card__title">${t`Logins over the last 24 hours`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${this.application && html`
|
||||
<ak-charts-application-authorize applicationSlug=${this.application.slug}>
|
||||
</ak-charts-application-authorize>`}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card pf-l-gallery__item" style="grid-column-end: span 5;grid-row-end: span 3;">
|
||||
<div class="pf-c-card__title">${t`Changelog`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.application.pk || ""}
|
||||
targetModelApp="authentik_core"
|
||||
targetModelName="application">
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
return html` <ak-tabs>
|
||||
<section
|
||||
slot="page-overview"
|
||||
data-tab-title="${t`Overview`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-l-gallery pf-m-gutter">
|
||||
<div class="pf-c-card pf-l-gallery__item">
|
||||
<div class="pf-c-card__title">${t`Related`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list">
|
||||
${this.application.providerObj
|
||||
? html`<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Provider`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<a
|
||||
href="#/core/providers/${this.application
|
||||
.providerObj?.pk}"
|
||||
>
|
||||
${this.application.providerObj?.name}
|
||||
</a>
|
||||
</div>
|
||||
</dd>
|
||||
</div>`
|
||||
: html``}
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Policy engine mode`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.application.policyEngineMode?.toUpperCase()}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Edit`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header">
|
||||
${t`Update Application`}
|
||||
</span>
|
||||
<ak-application-form
|
||||
slot="form"
|
||||
.instancePk=${this.application.slug}
|
||||
>
|
||||
</ak-application-form>
|
||||
<button
|
||||
slot="trigger"
|
||||
class="pf-c-button pf-m-secondary"
|
||||
>
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Check access`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-forms-modal .closeAfterSuccessfulSubmit=${false}>
|
||||
<span slot="submit"> ${t`Check`} </span>
|
||||
<span slot="header">
|
||||
${t`Check Application access`}
|
||||
</span>
|
||||
<ak-application-check-access-form
|
||||
slot="form"
|
||||
.application=${this.application}
|
||||
>
|
||||
</ak-application-check-access-form>
|
||||
<button
|
||||
slot="trigger"
|
||||
class="pf-c-button pf-m-secondary"
|
||||
>
|
||||
${t`Test`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
${this.application.launchUrl
|
||||
? html`<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Launch`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<a
|
||||
target="_blank"
|
||||
href=${this.application.launchUrl}
|
||||
slot="trigger"
|
||||
class="pf-c-button pf-m-secondary"
|
||||
>
|
||||
${t`Launch`}
|
||||
</a>
|
||||
</div>
|
||||
</dd>
|
||||
</div>`
|
||||
: html``}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div slot="page-policy-bindings" data-tab-title="${t`Policy / Group / User Bindings`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`These policies control which users can access this application.`}</div>
|
||||
<ak-bound-policies-list .target=${this.application.pk}>
|
||||
</ak-bound-policies-list>
|
||||
<div
|
||||
class="pf-c-card pf-l-gallery__item"
|
||||
style="grid-column-end: span 4;grid-row-end: span 2;"
|
||||
>
|
||||
<div class="pf-c-card__title">${t`Logins over the last 24 hours`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${this.application &&
|
||||
html` <ak-charts-application-authorize
|
||||
applicationSlug=${this.application.slug}
|
||||
>
|
||||
</ak-charts-application-authorize>`}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="pf-c-card pf-l-gallery__item"
|
||||
style="grid-column-end: span 5;grid-row-end: span 3;"
|
||||
>
|
||||
<div class="pf-c-card__title">${t`Changelog`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.application.pk || ""}
|
||||
targetModelApp="authentik_core"
|
||||
targetModelName="application"
|
||||
>
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>`;
|
||||
</section>
|
||||
<div
|
||||
slot="page-policy-bindings"
|
||||
data-tab-title="${t`Policy / Group / User Bindings`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">
|
||||
${t`These policies control which users can access this application.`}
|
||||
</div>
|
||||
<ak-bound-policies-list .target=${this.application.pk}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,38 +8,34 @@ import "../../elements/forms/HorizontalFormElement";
|
||||
|
||||
@customElement("ak-crypto-certificate-generate-form")
|
||||
export class CertificateKeyPairForm extends Form<CertificateGenerationRequest> {
|
||||
|
||||
getSuccessMessage(): string {
|
||||
return t`Successfully generated certificate-key pair.`;
|
||||
}
|
||||
|
||||
send = (data: CertificateGenerationRequest): Promise<CertificateKeyPair> => {
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsGenerateCreate({
|
||||
certificateGenerationRequest: data
|
||||
certificateGenerationRequest: data,
|
||||
});
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Common Name`}
|
||||
name="commonName"
|
||||
?required=${true}>
|
||||
<input type="text" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Common Name`} name="commonName" ?required=${true}>
|
||||
<input type="text" class="pf-c-form-control" required />
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Subject-alt name`}
|
||||
name="subjectAltName">
|
||||
<input class="pf-c-form-control" type="text">
|
||||
<p class="pf-c-form__helper-text">${t`Optional, comma-separated SubjectAlt Names.`}</p>
|
||||
<ak-form-element-horizontal label=${t`Subject-alt name`} name="subjectAltName">
|
||||
<input class="pf-c-form-control" type="text" />
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Optional, comma-separated SubjectAlt Names.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Validity days`}
|
||||
name="validityDays"
|
||||
?required=${true}>
|
||||
<input class="pf-c-form-control" type="number" value="365">
|
||||
?required=${true}
|
||||
>
|
||||
<input class="pf-c-form-control" type="number" value="365" />
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ import { ModelForm } from "../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-crypto-certificate-form")
|
||||
export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<CertificateKeyPair> {
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsRetrieve({
|
||||
kpUuid: pk,
|
||||
@ -29,39 +28,44 @@ export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string
|
||||
if (this.instance) {
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsPartialUpdate({
|
||||
kpUuid: this.instance.pk || "",
|
||||
patchedCertificateKeyPairRequest: data
|
||||
patchedCertificateKeyPairRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsCreate({
|
||||
certificateKeyPairRequest: data as unknown as CertificateKeyPairRequest
|
||||
certificateKeyPairRequest: data as unknown as CertificateKeyPairRequest,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
name="name"
|
||||
?required=${true}>
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} name="name" ?required=${true}>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Certificate`}
|
||||
name="certificateData"
|
||||
?writeOnly=${this.instance !== undefined}
|
||||
?required=${true}>
|
||||
?required=${true}
|
||||
>
|
||||
<textarea class="pf-c-form-control" required></textarea>
|
||||
<p class="pf-c-form__helper-text">${t`PEM-encoded Certificate data.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
name="keyData"
|
||||
?writeOnly=${this.instance !== undefined}
|
||||
label=${t`Private Key`}>
|
||||
<textarea class="pf-c-form-control" ></textarea>
|
||||
<p class="pf-c-form__helper-text">${t`Optional Private Key. If this is set, you can use this keypair for encryption.`}</p>
|
||||
label=${t`Private Key`}
|
||||
>
|
||||
<textarea class="pf-c-form-control"></textarea>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Optional Private Key. If this is set, you can use this keypair for encryption.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -62,122 +62,118 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
|
||||
html`${item.name}`,
|
||||
html`${item.privateKeyAvailable ? t`Yes` : t`No`}`,
|
||||
html`${item.certExpiry?.toLocaleString()}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Certificate-Key Pair`}
|
||||
</span>
|
||||
<ak-crypto-certificate-form slot="form" .instancePk=${item.pk}>
|
||||
</ak-crypto-certificate-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Certificate-Key Pair`}
|
||||
.usedBy=${() => {
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsUsedByList({
|
||||
kpUuid: item.pk
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsDestroy({
|
||||
kpUuid: item.pk
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Certificate-Key Pair`} </span>
|
||||
<ak-crypto-certificate-form slot="form" .instancePk=${item.pk}>
|
||||
</ak-crypto-certificate-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Certificate-Key Pair`}
|
||||
.usedBy=${() => {
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsUsedByList({
|
||||
kpUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsDestroy({
|
||||
kpUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: CertificateKeyPair): TemplateResult {
|
||||
return html`
|
||||
<td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<dl class="pf-c-description-list pf-m-horizontal">
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Certificate Fingerprint (SHA1)`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${item.fingerprintSha1}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Certificate Fingerprint (SHA256)`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${item.fingerprintSha256}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Certificate Subjet`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${item.certSubject}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Download`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<a class="pf-c-button pf-m-secondary" target="_blank"
|
||||
href=${item.certificateDownloadUrl}>
|
||||
${t`Download Certificate`}
|
||||
</a>
|
||||
${item.privateKeyAvailable ? html`<a class="pf-c-button pf-m-secondary" target="_blank"
|
||||
href=${item.privateKeyDownloadUrl}>
|
||||
${t`Download Private key`}
|
||||
</a>` : html``}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>`;
|
||||
return html` <td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<dl class="pf-c-description-list pf-m-horizontal">
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Certificate Fingerprint (SHA1)`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${item.fingerprintSha1}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Certificate Fingerprint (SHA256)`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${item.fingerprintSha256}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Certificate Subjet`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${item.certSubject}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Download`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<a
|
||||
class="pf-c-button pf-m-secondary"
|
||||
target="_blank"
|
||||
href=${item.certificateDownloadUrl}
|
||||
>
|
||||
${t`Download Certificate`}
|
||||
</a>
|
||||
${item.privateKeyAvailable
|
||||
? html`<a
|
||||
class="pf-c-button pf-m-secondary"
|
||||
target="_blank"
|
||||
href=${item.privateKeyDownloadUrl}
|
||||
>
|
||||
${t`Download Private key`}
|
||||
</a>`
|
||||
: html``}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>`;
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Certificate-Key Pair`}
|
||||
</span>
|
||||
<ak-crypto-certificate-form slot="form">
|
||||
</ak-crypto-certificate-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Create`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Generate`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Generate Certificate-Key Pair`}
|
||||
</span>
|
||||
<ak-crypto-certificate-generate-form slot="form">
|
||||
</ak-crypto-certificate-generate-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Generate`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Certificate-Key Pair`} </span>
|
||||
<ak-crypto-certificate-form slot="form"> </ak-crypto-certificate-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${t`Create`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Generate`} </span>
|
||||
<span slot="header"> ${t`Generate Certificate-Key Pair`} </span>
|
||||
<ak-crypto-certificate-generate-form slot="form">
|
||||
</ak-crypto-certificate-generate-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Generate`}</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { EventActions, FlowsApi } from "authentik-api";
|
||||
import "../../elements/Spinner";
|
||||
@ -16,12 +24,16 @@ import { VERSION } from "../../constants";
|
||||
|
||||
@customElement("ak-event-info")
|
||||
export class EventInfo extends LitElement {
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
event!: EventWithContext;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFButton, PFFlex, PFList, PFDescriptionList,
|
||||
return [
|
||||
PFBase,
|
||||
PFButton,
|
||||
PFFlex,
|
||||
PFList,
|
||||
PFDescriptionList,
|
||||
css`
|
||||
code {
|
||||
display: block;
|
||||
@ -37,7 +49,7 @@ export class EventInfo extends LitElement {
|
||||
width: 100%;
|
||||
height: 50rem;
|
||||
}
|
||||
`
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@ -116,7 +128,7 @@ export class EventInfo extends LitElement {
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${(context.to_email as string[]).map(to => {
|
||||
${(context.to_email as string[]).map((to) => {
|
||||
return html`<li>${to}</li>`;
|
||||
})}
|
||||
</div>
|
||||
@ -127,15 +139,15 @@ export class EventInfo extends LitElement {
|
||||
|
||||
defaultResponse(): TemplateResult {
|
||||
return html`<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Context`}</h3>
|
||||
<code>${JSON.stringify(this.event?.context, null, 4)}</code>
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`User`}</h3>
|
||||
<code>${JSON.stringify(this.event?.user, null, 4)}</code>
|
||||
</div>
|
||||
</div>`;
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Context`}</h3>
|
||||
<code>${JSON.stringify(this.event?.context, null, 4)}</code>
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`User`}</h3>
|
||||
<code>${JSON.stringify(this.event?.user, null, 4)}</code>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
buildGitHubIssueUrl(context: EventContext): string {
|
||||
@ -189,150 +201,201 @@ new?labels=bug,from_authentik&title=${encodeURIComponent(title)}
|
||||
return html`<ak-spinner size=${PFSize.Medium}></ak-spinner>`;
|
||||
}
|
||||
switch (this.event?.action) {
|
||||
case EventActions.ModelCreated:
|
||||
case EventActions.ModelUpdated:
|
||||
case EventActions.ModelDeleted:
|
||||
return html`
|
||||
<h3>${t`Affected model:`}</h3>
|
||||
${this.getModelInfo(this.event.context?.model as EventModel)}
|
||||
case EventActions.ModelCreated:
|
||||
case EventActions.ModelUpdated:
|
||||
case EventActions.ModelDeleted:
|
||||
return html`
|
||||
<h3>${t`Affected model:`}</h3>
|
||||
${this.getModelInfo(this.event.context?.model as EventModel)}
|
||||
`;
|
||||
case EventActions.AuthorizeApplication:
|
||||
return html`<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Authorized application:`}</h3>
|
||||
${this.getModelInfo(this.event.context.authorized_application as EventModel)}
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Using flow`}</h3>
|
||||
<span>${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
flowUuid: this.event.context.flow as string,
|
||||
}).then(resp => {
|
||||
return html`<a href="#/flow/flows/${resp.results[0].slug}">${resp.results[0].name}</a>`;
|
||||
}), html`<ak-spinner size=${PFSize.Medium}></ak-spinner>`)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.EmailSent:
|
||||
return html`<h3>${t`Email info:`}</h3>
|
||||
${this.getEmailInfo(this.event.context)}
|
||||
<ak-expand>
|
||||
<iframe srcdoc=${this.event.context.body}></iframe>
|
||||
</ak-expand>`;
|
||||
case EventActions.SecretView:
|
||||
return html`
|
||||
<h3>${t`Secret:`}</h3>
|
||||
${this.getModelInfo(this.event.context.secret as EventModel)}`;
|
||||
case EventActions.SystemException:
|
||||
return html`
|
||||
<a
|
||||
class="pf-c-button pf-m-primary"
|
||||
target="_blank"
|
||||
href=${this.buildGitHubIssueUrl(
|
||||
this.event.context
|
||||
)}>
|
||||
${t`Open issue on GitHub...`}
|
||||
</a>
|
||||
<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Exception`}</h3>
|
||||
<code>${this.event.context.message}</code>
|
||||
</div>
|
||||
</div>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.PropertyMappingException:
|
||||
return html`<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Exception`}</h3>
|
||||
<code>${this.event.context.message || this.event.context.error}</code>
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Expression`}</h3>
|
||||
<code>${this.event.context.expression}</code>
|
||||
</div>
|
||||
</div>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.PolicyException:
|
||||
return html`<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Binding`}</h3>
|
||||
${this.getModelInfo(this.event.context.binding as EventModel)}
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Request`}</h3>
|
||||
<ul class="pf-c-list">
|
||||
<li>${t`Object`}: ${this.getModelInfo((this.event.context.request as EventContext).obj as EventModel)}</li>
|
||||
<li><span>${t`Context`}: <code>${JSON.stringify((this.event.context.request as EventContext).context, null, 4)}</code></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Exception`}</h3>
|
||||
<code>${this.event.context.message || this.event.context.error}</code>
|
||||
</div>
|
||||
</div>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.PolicyExecution:
|
||||
return html`<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Binding`}</h3>
|
||||
${this.getModelInfo(this.event.context.binding as EventModel)}
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Request`}</h3>
|
||||
<ul class="pf-c-list">
|
||||
<li>${t`Object`}: ${this.getModelInfo((this.event.context.request as EventContext).obj as EventModel)}</li>
|
||||
<li><span>${t`Context`}: <code>${JSON.stringify((this.event.context.request as EventContext).context, null, 4)}</code></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Result`}</h3>
|
||||
<ul class="pf-c-list">
|
||||
<li>${t`Passing`}: ${(this.event.context.result as EventContext).passing}</li>
|
||||
<li>${t`Messages`}:
|
||||
<ul class="pf-c-list">
|
||||
${((this.event.context.result as EventContext).messages as string[]).map(msg => {
|
||||
return html`<li>${msg}</li>`;
|
||||
})}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.ConfigurationError:
|
||||
return html`<h3>${this.event.context.message}</h3>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.UpdateAvailable:
|
||||
return html`<h3>${t`New version available!`}</h3>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://github.com/goauthentik/authentik/releases/tag/version%2F${this.event.context.new_version}">
|
||||
${this.event.context.new_version}
|
||||
</a>`;
|
||||
// Action types which typically don't record any extra context.
|
||||
// If context is not empty, we fall to the default response.
|
||||
case EventActions.Login:
|
||||
if ("using_source" in this.event.context) {
|
||||
case EventActions.AuthorizeApplication:
|
||||
return html`<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Using source`}</h3>
|
||||
${this.getModelInfo(this.event.context.using_source as EventModel)}
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Authorized application:`}</h3>
|
||||
${this.getModelInfo(
|
||||
this.event.context.authorized_application as EventModel,
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Using flow`}</h3>
|
||||
<span
|
||||
>${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
flowUuid: this.event.context.flow as string,
|
||||
})
|
||||
.then((resp) => {
|
||||
return html`<a
|
||||
href="#/flow/flows/${resp.results[0].slug}"
|
||||
>${resp.results[0].name}</a
|
||||
>`;
|
||||
}),
|
||||
html`<ak-spinner size=${PFSize.Medium}></ak-spinner>`,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
return this.defaultResponse();
|
||||
case EventActions.LoginFailed:
|
||||
return html`
|
||||
<h3>${t`Attempted to log in as ${this.event.context.username}`}</h3>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.Logout:
|
||||
if (this.event.context === {}) {
|
||||
return html`<span>${t`No additional data available.`}</span>`;
|
||||
}
|
||||
return this.defaultResponse();
|
||||
default:
|
||||
return this.defaultResponse();
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.EmailSent:
|
||||
return html`<h3>${t`Email info:`}</h3>
|
||||
${this.getEmailInfo(this.event.context)}
|
||||
<ak-expand>
|
||||
<iframe srcdoc=${this.event.context.body}></iframe>
|
||||
</ak-expand>`;
|
||||
case EventActions.SecretView:
|
||||
return html` <h3>${t`Secret:`}</h3>
|
||||
${this.getModelInfo(this.event.context.secret as EventModel)}`;
|
||||
case EventActions.SystemException:
|
||||
return html` <a
|
||||
class="pf-c-button pf-m-primary"
|
||||
target="_blank"
|
||||
href=${this.buildGitHubIssueUrl(this.event.context)}
|
||||
>
|
||||
${t`Open issue on GitHub...`}
|
||||
</a>
|
||||
<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Exception`}</h3>
|
||||
<code>${this.event.context.message}</code>
|
||||
</div>
|
||||
</div>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.PropertyMappingException:
|
||||
return html`<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Exception`}</h3>
|
||||
<code>${this.event.context.message || this.event.context.error}</code>
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Expression`}</h3>
|
||||
<code>${this.event.context.expression}</code>
|
||||
</div>
|
||||
</div>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.PolicyException:
|
||||
return html`<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Binding`}</h3>
|
||||
${this.getModelInfo(this.event.context.binding as EventModel)}
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Request`}</h3>
|
||||
<ul class="pf-c-list">
|
||||
<li>
|
||||
${t`Object`}:
|
||||
${this.getModelInfo(
|
||||
(this.event.context.request as EventContext)
|
||||
.obj as EventModel,
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
<span
|
||||
>${t`Context`}:
|
||||
<code
|
||||
>${JSON.stringify(
|
||||
(this.event.context.request as EventContext)
|
||||
.context,
|
||||
null,
|
||||
4,
|
||||
)}</code
|
||||
></span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Exception`}</h3>
|
||||
<code>${this.event.context.message || this.event.context.error}</code>
|
||||
</div>
|
||||
</div>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.PolicyExecution:
|
||||
return html`<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Binding`}</h3>
|
||||
${this.getModelInfo(this.event.context.binding as EventModel)}
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Request`}</h3>
|
||||
<ul class="pf-c-list">
|
||||
<li>
|
||||
${t`Object`}:
|
||||
${this.getModelInfo(
|
||||
(this.event.context.request as EventContext)
|
||||
.obj as EventModel,
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
<span
|
||||
>${t`Context`}:
|
||||
<code
|
||||
>${JSON.stringify(
|
||||
(this.event.context.request as EventContext)
|
||||
.context,
|
||||
null,
|
||||
4,
|
||||
)}</code
|
||||
></span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Result`}</h3>
|
||||
<ul class="pf-c-list">
|
||||
<li>
|
||||
${t`Passing`}:
|
||||
${(this.event.context.result as EventContext).passing}
|
||||
</li>
|
||||
<li>
|
||||
${t`Messages`}:
|
||||
<ul class="pf-c-list">
|
||||
${(
|
||||
(this.event.context.result as EventContext)
|
||||
.messages as string[]
|
||||
).map((msg) => {
|
||||
return html`<li>${msg}</li>`;
|
||||
})}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.ConfigurationError:
|
||||
return html`<h3>${this.event.context.message}</h3>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.UpdateAvailable:
|
||||
return html`<h3>${t`New version available!`}</h3>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://github.com/goauthentik/authentik/releases/tag/version%2F${this
|
||||
.event.context.new_version}"
|
||||
>
|
||||
${this.event.context.new_version}
|
||||
</a>`;
|
||||
// Action types which typically don't record any extra context.
|
||||
// If context is not empty, we fall to the default response.
|
||||
case EventActions.Login:
|
||||
if ("using_source" in this.event.context) {
|
||||
return html`<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${t`Using source`}</h3>
|
||||
${this.getModelInfo(this.event.context.using_source as EventModel)}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
return this.defaultResponse();
|
||||
case EventActions.LoginFailed:
|
||||
return html` <h3>${t`Attempted to log in as ${this.event.context.username}`}</h3>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case EventActions.Logout:
|
||||
if (this.event.context === {}) {
|
||||
return html`<span>${t`No additional data available.`}</span>`;
|
||||
}
|
||||
return this.defaultResponse();
|
||||
default:
|
||||
return this.defaultResponse();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -13,14 +13,15 @@ import "../../elements/PageHeader";
|
||||
|
||||
@customElement("ak-event-info-page")
|
||||
export class EventInfoPage extends LitElement {
|
||||
|
||||
@property()
|
||||
set eventID(value: string) {
|
||||
new EventsApi(DEFAULT_CONFIG).eventsEventsRetrieve({
|
||||
eventUuid: value
|
||||
}).then((ev) => {
|
||||
this.event = ev as EventWithContext;
|
||||
});
|
||||
new EventsApi(DEFAULT_CONFIG)
|
||||
.eventsEventsRetrieve({
|
||||
eventUuid: value,
|
||||
})
|
||||
.then((ev) => {
|
||||
this.event = ev as EventWithContext;
|
||||
});
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
@ -32,19 +33,17 @@ export class EventInfoPage extends LitElement {
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<ak-page-header
|
||||
icon="pf-icon pf-icon-catalog"
|
||||
header=${t`Event ${this.event?.pk || ""}`}>
|
||||
</ak-page-header>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">
|
||||
${t`Event info`}
|
||||
icon="pf-icon pf-icon-catalog"
|
||||
header=${t`Event ${this.event?.pk || ""}`}
|
||||
>
|
||||
</ak-page-header>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`Event info`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-event-info .event=${this.event}></ak-event-info>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-event-info .event=${this.event}></ak-event-info>
|
||||
</div>
|
||||
</div>
|
||||
</section>`;
|
||||
</section>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -53,15 +53,15 @@ export class EventListPage extends TablePage<Event> {
|
||||
row(item: EventWithContext): TemplateResult[] {
|
||||
return [
|
||||
html`<div>${ActionToLabel(item.action)}</div>
|
||||
<small>${item.app}</small>`,
|
||||
item.user?.username ?
|
||||
html`<a href="#/identity/users/${item.user.pk}">
|
||||
${item.user?.username}
|
||||
</a>
|
||||
${item.user.on_behalf_of ? html`<small>
|
||||
${t`On behalf of ${item.user.on_behalf_of.username}`}
|
||||
</small>` : html``}` :
|
||||
html`-`,
|
||||
<small>${item.app}</small>`,
|
||||
item.user?.username
|
||||
? html`<a href="#/identity/users/${item.user.pk}"> ${item.user?.username} </a>
|
||||
${item.user.on_behalf_of
|
||||
? html`<small>
|
||||
${t`On behalf of ${item.user.on_behalf_of.username}`}
|
||||
</small>`
|
||||
: html``}`
|
||||
: html`-`,
|
||||
html`<span>${item.created?.toLocaleString()}</span>`,
|
||||
html`<span>${item.clientIp || "-"}</span>`,
|
||||
html`<span>${item.tenant?.name || "-"}</span>`,
|
||||
@ -72,15 +72,13 @@ export class EventListPage extends TablePage<Event> {
|
||||
}
|
||||
|
||||
renderExpanded(item: Event): TemplateResult {
|
||||
return html`
|
||||
<td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<ak-event-info .event=${item as EventWithContext}></ak-event-info>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>`;
|
||||
return html` <td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<ak-event-info .event=${item as EventWithContext}></ak-event-info>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ import { ModelForm } from "../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-event-rule-form")
|
||||
export class RuleForm extends ModelForm<NotificationRule, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<NotificationRule> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsRulesRetrieve({
|
||||
pbmUuid: pk,
|
||||
@ -29,24 +28,33 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
|
||||
if (this.instance) {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsRulesUpdate({
|
||||
pbmUuid: this.instance.pk || "",
|
||||
notificationRuleRequest: data
|
||||
notificationRuleRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsRulesCreate({
|
||||
notificationRuleRequest: data
|
||||
notificationRuleRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderSeverity(): TemplateResult {
|
||||
return html`
|
||||
<option value=${SeverityEnum.Alert} ?selected=${this.instance?.severity === SeverityEnum.Alert}>
|
||||
<option
|
||||
value=${SeverityEnum.Alert}
|
||||
?selected=${this.instance?.severity === SeverityEnum.Alert}
|
||||
>
|
||||
${t`Alert`}
|
||||
</option>
|
||||
<option value=${SeverityEnum.Warning} ?selected=${this.instance?.severity === SeverityEnum.Warning}>
|
||||
<option
|
||||
value=${SeverityEnum.Warning}
|
||||
?selected=${this.instance?.severity === SeverityEnum.Warning}
|
||||
>
|
||||
${t`Warning`}
|
||||
</option>
|
||||
<option value=${SeverityEnum.Notice} ?selected=${this.instance?.severity === SeverityEnum.Notice}>
|
||||
<option
|
||||
value=${SeverityEnum.Notice}
|
||||
?selected=${this.instance?.severity === SeverityEnum.Notice}
|
||||
>
|
||||
${t`Notice`}
|
||||
</option>
|
||||
`;
|
||||
@ -54,50 +62,69 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Group`}
|
||||
name="group">
|
||||
<ak-form-element-horizontal label=${t`Group`} name="group">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.group === undefined}>---------</option>
|
||||
${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => {
|
||||
return groups.results.map(group => {
|
||||
return html`<option value=${ifDefined(group.pk)} ?selected=${this.instance?.group === group.pk}>${group.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Transports`}
|
||||
?required=${true}
|
||||
name="transports">
|
||||
<select name="users" class="pf-c-form-control" multiple>
|
||||
${until(new EventsApi(DEFAULT_CONFIG).eventsTransportsList({}).then(transports => {
|
||||
return transports.results.map(transport => {
|
||||
const selected = Array.from(this.instance?.transports || []).some(su => {
|
||||
return su == transport.pk;
|
||||
<option value="" ?selected=${this.instance?.group === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then((groups) => {
|
||||
return groups.results.map((group) => {
|
||||
return html`<option
|
||||
value=${ifDefined(group.pk)}
|
||||
?selected=${this.instance?.group === group.pk}
|
||||
>
|
||||
${group.name}
|
||||
</option>`;
|
||||
});
|
||||
return html`<option value=${ifDefined(transport.pk)} ?selected=${selected}>${transport.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Select which transports should be used to notify the user. If none are selected, the notification will only be shown in the authentik UI.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Severity`}
|
||||
?required=${true}
|
||||
name="severity">
|
||||
<ak-form-element-horizontal label=${t`Transports`} ?required=${true} name="transports">
|
||||
<select name="users" class="pf-c-form-control" multiple>
|
||||
${until(
|
||||
new EventsApi(DEFAULT_CONFIG)
|
||||
.eventsTransportsList({})
|
||||
.then((transports) => {
|
||||
return transports.results.map((transport) => {
|
||||
const selected = Array.from(
|
||||
this.instance?.transports || [],
|
||||
).some((su) => {
|
||||
return su == transport.pk;
|
||||
});
|
||||
return html`<option
|
||||
value=${ifDefined(transport.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${transport.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Select which transports should be used to notify the user. If none are selected, the notification will only be shown in the authentik UI.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Hold control/command to select multiple items.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${t`Severity`} ?required=${true} name="severity">
|
||||
<select class="pf-c-form-control">
|
||||
${this.renderSeverity()}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -56,70 +56,54 @@ export class RuleListPage extends TablePage<NotificationRule> {
|
||||
html`${item.name}`,
|
||||
html`${item.severity}`,
|
||||
html`${item.groupObj?.name || t`None (rule disabled)`}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Notification Rule`}
|
||||
</span>
|
||||
<ak-event-rule-form slot="form" .instancePk=${item.pk}>
|
||||
</ak-event-rule-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Notification rule`}
|
||||
.usedBy=${() => {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsRulesUsedByList({
|
||||
pbmUuid: item.pk
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsRulesDestroy({
|
||||
pbmUuid: item.pk
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Notification Rule`} </span>
|
||||
<ak-event-rule-form slot="form" .instancePk=${item.pk}> </ak-event-rule-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Notification rule`}
|
||||
.usedBy=${() => {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsRulesUsedByList({
|
||||
pbmUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsRulesDestroy({
|
||||
pbmUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Notification Rule`}
|
||||
</span>
|
||||
<ak-event-rule-form slot="form">
|
||||
</ak-event-rule-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Create`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Notification Rule`} </span>
|
||||
<ak-event-rule-form slot="form"> </ak-event-rule-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${t`Create`}</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
|
||||
renderExpanded(item: NotificationRule): TemplateResult {
|
||||
return html`
|
||||
<td role="cell" colspan="4">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<p>${t`These bindings control upon which events this rule triggers. Bindings to
|
||||
groups/users are checked against the user of the event.`}</p>
|
||||
<ak-bound-policies-list .target=${item.pk}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>`;
|
||||
return html` <td role="cell" colspan="4">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<p>
|
||||
${t`These bindings control upon which events this rule triggers. Bindings to
|
||||
groups/users are checked against the user of the event.`}
|
||||
</p>
|
||||
<ak-bound-policies-list .target=${item.pk}> </ak-bound-policies-list>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,14 +10,13 @@ import { ModelForm } from "../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-event-transport-form")
|
||||
export class TransportForm extends ModelForm<NotificationTransport, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<NotificationTransport> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsTransportsRetrieve({
|
||||
uuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
@property({type: Boolean})
|
||||
@property({ type: Boolean })
|
||||
showWebhook = false;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
@ -32,24 +31,33 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
|
||||
if (this.instance) {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsTransportsUpdate({
|
||||
uuid: this.instance.pk || "",
|
||||
notificationTransportRequest: data
|
||||
notificationTransportRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsTransportsCreate({
|
||||
notificationTransportRequest: data
|
||||
notificationTransportRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderTransportModes(): TemplateResult {
|
||||
return html`
|
||||
<option value=${NotificationTransportModeEnum.Email} ?selected=${this.instance?.mode === NotificationTransportModeEnum.Email}>
|
||||
<option
|
||||
value=${NotificationTransportModeEnum.Email}
|
||||
?selected=${this.instance?.mode === NotificationTransportModeEnum.Email}
|
||||
>
|
||||
${t`Email`}
|
||||
</option>
|
||||
<option value=${NotificationTransportModeEnum.Webhook} ?selected=${this.instance?.mode === NotificationTransportModeEnum.Webhook}>
|
||||
<option
|
||||
value=${NotificationTransportModeEnum.Webhook}
|
||||
?selected=${this.instance?.mode === NotificationTransportModeEnum.Webhook}
|
||||
>
|
||||
${t`Webhook (generic)`}
|
||||
</option>
|
||||
<option value=${NotificationTransportModeEnum.WebhookSlack} ?selected=${this.instance?.mode === NotificationTransportModeEnum.WebhookSlack}>
|
||||
<option
|
||||
value=${NotificationTransportModeEnum.WebhookSlack}
|
||||
?selected=${this.instance?.mode === NotificationTransportModeEnum.WebhookSlack}
|
||||
>
|
||||
${t`Webhook (Slack/Discord)`}
|
||||
</option>
|
||||
`;
|
||||
@ -62,7 +70,10 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
|
||||
}
|
||||
|
||||
onModeChange(mode: string): void {
|
||||
if (mode === NotificationTransportModeEnum.Webhook || mode === NotificationTransportModeEnum.WebhookSlack) {
|
||||
if (
|
||||
mode === NotificationTransportModeEnum.Webhook ||
|
||||
mode === NotificationTransportModeEnum.WebhookSlack
|
||||
) {
|
||||
this.showWebhook = true;
|
||||
} else {
|
||||
this.showWebhook = false;
|
||||
@ -71,39 +82,49 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Mode`}
|
||||
?required=${true}
|
||||
name="mode">
|
||||
<select class="pf-c-form-control" @change=${(ev: Event) => {
|
||||
const current = (ev.target as HTMLInputElement).value;
|
||||
this.onModeChange(current);
|
||||
}}>
|
||||
<ak-form-element-horizontal label=${t`Mode`} ?required=${true} name="mode">
|
||||
<select
|
||||
class="pf-c-form-control"
|
||||
@change=${(ev: Event) => {
|
||||
const current = (ev.target as HTMLInputElement).value;
|
||||
this.onModeChange(current);
|
||||
}}
|
||||
>
|
||||
${this.renderTransportModes()}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
?hidden=${!this.showWebhook}
|
||||
label=${t`Webhook URL`}
|
||||
name="webhookUrl">
|
||||
<input type="text" value="${ifDefined(this.instance?.webhookUrl)}" class="pf-c-form-control">
|
||||
name="webhookUrl"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.webhookUrl)}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="sendOnce">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.sendOnce, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Send once`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.sendOnce, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Send once`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Only send notification once, for example when sending a webhook into a chat channel.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Only send notification once, for example when sending a webhook into a chat channel.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -52,64 +52,50 @@ export class TransportListPage extends TablePage<NotificationTransport> {
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`${item.modeVerbose}`,
|
||||
html`
|
||||
<ak-action-button
|
||||
.apiRequest=${() => {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsTransportsTestCreate({
|
||||
uuid: item.pk || "",
|
||||
});
|
||||
}}>
|
||||
${t`Test`}
|
||||
</ak-action-button>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Notification Transport`}
|
||||
</span>
|
||||
<ak-event-transport-form slot="form" .instancePk=${item.pk}>
|
||||
</ak-event-transport-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Notifications Transport`}
|
||||
.usedBy=${() => {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsTransportsUsedByList({
|
||||
uuid: item.pk
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsTransportsDestroy({
|
||||
uuid: item.pk
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
html` <ak-action-button
|
||||
.apiRequest=${() => {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsTransportsTestCreate({
|
||||
uuid: item.pk || "",
|
||||
});
|
||||
}}
|
||||
>
|
||||
${t`Test`}
|
||||
</ak-action-button>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Notification Transport`} </span>
|
||||
<ak-event-transport-form slot="form" .instancePk=${item.pk}>
|
||||
</ak-event-transport-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Notifications Transport`}
|
||||
.usedBy=${() => {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsTransportsUsedByList({
|
||||
uuid: item.pk,
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsTransportsDestroy({
|
||||
uuid: item.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Notification Transport`}
|
||||
</span>
|
||||
<ak-event-transport-form slot="form">
|
||||
</ak-event-transport-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Create`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Notification Transport`} </span>
|
||||
<ak-event-transport-form slot="form"> </ak-event-transport-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${t`Create`}</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,87 +47,78 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
html`${item.order}`,
|
||||
html`${item.stageObj?.name}`,
|
||||
html`${item.stageObj?.verboseName}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update ${item.stageObj?.verboseName}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
"instancePk": item.stage
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update ${item.stageObj?.verboseName}`} </span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
instancePk: item.stage,
|
||||
}}
|
||||
type=${ifDefined(item.stageObj?.component)}
|
||||
>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit Stage`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Stage binding`} </span>
|
||||
<ak-stage-binding-form slot="form" .instancePk=${item.pk}>
|
||||
</ak-stage-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit Binding`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Stage binding`}
|
||||
.usedBy=${() => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsUsedByList({
|
||||
fsbUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
type=${ifDefined(item.stageObj?.component)}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit Stage`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Stage binding`}
|
||||
</span>
|
||||
<ak-stage-binding-form slot="form" .instancePk=${item.pk}>
|
||||
</ak-stage-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit Binding`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Stage binding`}
|
||||
.usedBy=${() => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsUsedByList({
|
||||
fsbUuid: item.pk
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsDestroy({
|
||||
fsbUuid: item.pk,
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete Binding`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
.delete=${() => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsDestroy({
|
||||
fsbUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete Binding`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: FlowStageBinding): TemplateResult {
|
||||
return html`
|
||||
<td></td>
|
||||
<td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<div class="pf-c-content">
|
||||
<p>${t`These bindings control if this stage will be applied to the flow.`}</p>
|
||||
<ak-bound-policies-list .target=${item.policybindingmodelPtrId}>
|
||||
</ak-bound-policies-list>
|
||||
return html` <td></td>
|
||||
<td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<div class="pf-c-content">
|
||||
<p>
|
||||
${t`These bindings control if this stage will be applied to the flow.`}
|
||||
</p>
|
||||
<ak-bound-policies-list .target=${item.policybindingmodelPtrId}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>`;
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>`;
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(html`<ak-empty-state header=${t`No Stages bound`} icon="pf-icon-module">
|
||||
<div slot="body">
|
||||
${t`No stages are currently bound to this flow.`}
|
||||
</div>
|
||||
return super.renderEmpty(html`<ak-empty-state
|
||||
header=${t`No Stages bound`}
|
||||
icon="pf-icon-module"
|
||||
>
|
||||
<div slot="body">${t`No stages are currently bound to this flow.`}</div>
|
||||
<div slot="primary">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Stage binding`}
|
||||
</span>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Stage binding`} </span>
|
||||
<ak-stage-binding-form slot="form" targetPk=${ifDefined(this.target)}>
|
||||
</ak-stage-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
@ -140,50 +131,41 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Stage binding`}
|
||||
</span>
|
||||
<ak-stage-binding-form slot="form" targetPk=${ifDefined(this.target)}>
|
||||
</ak-stage-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Bind stage`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-secondary pf-c-button pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">${t`Create Stage`}</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(new StagesApi(DEFAULT_CONFIG).stagesAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create ${type.name}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Stage binding`} </span>
|
||||
<ak-stage-binding-form slot="form" targetPk=${ifDefined(this.target)}>
|
||||
</ak-stage-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${t`Bind stage`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-secondary pf-c-button pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">${t`Create Stage`}</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(
|
||||
new StagesApi(DEFAULT_CONFIG).stagesAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create ${type.name}`} </span>
|
||||
<ak-proxy-form slot="form" type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br />
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}),
|
||||
html`<ak-spinner></ak-spinner>`,
|
||||
)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,20 +12,21 @@ export const FILL_LIGHT_MODE = "#f0f0f0";
|
||||
|
||||
@customElement("ak-flow-diagram")
|
||||
export class FlowDiagram extends LitElement {
|
||||
|
||||
_flowSlug?: string;
|
||||
|
||||
@property()
|
||||
set flowSlug(value: string) {
|
||||
this._flowSlug = value;
|
||||
new FlowsApi(DEFAULT_CONFIG).flowsInstancesDiagramRetrieve({
|
||||
slug: value,
|
||||
}).then((data) => {
|
||||
this.diagram = FlowChart.parse(data.diagram || "");
|
||||
});
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesDiagramRetrieve({
|
||||
slug: value,
|
||||
})
|
||||
.then((data) => {
|
||||
this.diagram = FlowChart.parse(data.diagram || "");
|
||||
});
|
||||
}
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
diagram?: FlowChart.Instance;
|
||||
|
||||
@property()
|
||||
@ -73,5 +74,4 @@ export class FlowDiagram extends LitElement {
|
||||
}
|
||||
return loading(this.diagram, html``);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { Flow, FlowDesignationEnum, PolicyEngineMode, FlowsApi, CapabilitiesEnum } from "authentik-api";
|
||||
import {
|
||||
Flow,
|
||||
FlowDesignationEnum,
|
||||
PolicyEngineMode,
|
||||
FlowsApi,
|
||||
CapabilitiesEnum,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -11,7 +17,6 @@ import { first } from "../../utils";
|
||||
|
||||
@customElement("ak-flow-form")
|
||||
export class FlowForm extends ModelForm<Flow, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<Flow> {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesRetrieve({
|
||||
slug: pk,
|
||||
@ -34,18 +39,18 @@ export class FlowForm extends ModelForm<Flow, string> {
|
||||
if (this.instance) {
|
||||
writeOp = new FlowsApi(DEFAULT_CONFIG).flowsInstancesUpdate({
|
||||
slug: this.instance.slug,
|
||||
flowRequest: data
|
||||
flowRequest: data,
|
||||
});
|
||||
} else {
|
||||
writeOp = new FlowsApi(DEFAULT_CONFIG).flowsInstancesCreate({
|
||||
flowRequest: data
|
||||
flowRequest: data,
|
||||
});
|
||||
}
|
||||
return config().then((c) => {
|
||||
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
|
||||
const icon = this.getFormFile();
|
||||
if (icon || this.clearBackground) {
|
||||
return writeOp.then(app => {
|
||||
return writeOp.then((app) => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesSetBackgroundCreate({
|
||||
slug: app.slug,
|
||||
file: icon,
|
||||
@ -54,12 +59,12 @@ export class FlowForm extends ModelForm<Flow, string> {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return writeOp.then(app => {
|
||||
return writeOp.then((app) => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesSetBackgroundUrlCreate({
|
||||
slug: app.slug,
|
||||
setIconURLRequest: {
|
||||
url: data.background || "",
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -68,25 +73,46 @@ export class FlowForm extends ModelForm<Flow, string> {
|
||||
|
||||
renderDesignations(): TemplateResult {
|
||||
return html`
|
||||
<option value=${FlowDesignationEnum.Authentication} ?selected=${this.instance?.designation === FlowDesignationEnum.Authentication}>
|
||||
<option
|
||||
value=${FlowDesignationEnum.Authentication}
|
||||
?selected=${this.instance?.designation === FlowDesignationEnum.Authentication}
|
||||
>
|
||||
${t`Authentication`}
|
||||
</option>
|
||||
<option value=${FlowDesignationEnum.Authorization} ?selected=${this.instance?.designation === FlowDesignationEnum.Authorization}>
|
||||
<option
|
||||
value=${FlowDesignationEnum.Authorization}
|
||||
?selected=${this.instance?.designation === FlowDesignationEnum.Authorization}
|
||||
>
|
||||
${t`Authorization`}
|
||||
</option>
|
||||
<option value=${FlowDesignationEnum.Enrollment} ?selected=${this.instance?.designation === FlowDesignationEnum.Enrollment}>
|
||||
<option
|
||||
value=${FlowDesignationEnum.Enrollment}
|
||||
?selected=${this.instance?.designation === FlowDesignationEnum.Enrollment}
|
||||
>
|
||||
${t`Enrollment`}
|
||||
</option>
|
||||
<option value=${FlowDesignationEnum.Invalidation} ?selected=${this.instance?.designation === FlowDesignationEnum.Invalidation}>
|
||||
<option
|
||||
value=${FlowDesignationEnum.Invalidation}
|
||||
?selected=${this.instance?.designation === FlowDesignationEnum.Invalidation}
|
||||
>
|
||||
${t`Invalidation`}
|
||||
</option>
|
||||
<option value=${FlowDesignationEnum.Recovery} ?selected=${this.instance?.designation === FlowDesignationEnum.Recovery}>
|
||||
<option
|
||||
value=${FlowDesignationEnum.Recovery}
|
||||
?selected=${this.instance?.designation === FlowDesignationEnum.Recovery}
|
||||
>
|
||||
${t`Recovery`}
|
||||
</option>
|
||||
<option value=${FlowDesignationEnum.StageConfiguration} ?selected=${this.instance?.designation === FlowDesignationEnum.StageConfiguration}>
|
||||
<option
|
||||
value=${FlowDesignationEnum.StageConfiguration}
|
||||
?selected=${this.instance?.designation === FlowDesignationEnum.StageConfiguration}
|
||||
>
|
||||
${t`Stage Configuration`}
|
||||
</option>
|
||||
<option value=${FlowDesignationEnum.Unenrollment} ?selected=${this.instance?.designation === FlowDesignationEnum.Unenrollment}>
|
||||
<option
|
||||
value=${FlowDesignationEnum.Unenrollment}
|
||||
?selected=${this.instance?.designation === FlowDesignationEnum.Unenrollment}
|
||||
>
|
||||
${t`Unenrollment`}
|
||||
</option>
|
||||
`;
|
||||
@ -94,35 +120,48 @@ export class FlowForm extends ModelForm<Flow, string> {
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Title`}
|
||||
?required=${true}
|
||||
name="title">
|
||||
<input type="text" value="${ifDefined(this.instance?.title)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Title`} ?required=${true} name="title">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.title)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${t`Shown as the Title in Flow pages.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Slug`}
|
||||
?required=${true}
|
||||
name="slug">
|
||||
<input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Slug`} ?required=${true} name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${t`Visible in the URL.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Policy engine mode`}
|
||||
?required=${true}
|
||||
name="policyEngineMode">
|
||||
name="policyEngineMode"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${PolicyEngineMode.Any} ?selected=${this.instance?.policyEngineMode === PolicyEngineMode.Any}>
|
||||
<option
|
||||
value=${PolicyEngineMode.Any}
|
||||
?selected=${this.instance?.policyEngineMode === PolicyEngineMode.Any}
|
||||
>
|
||||
${t`ANY, any policy must match to grant access.`}
|
||||
</option>
|
||||
<option value=${PolicyEngineMode.All} ?selected=${this.instance?.policyEngineMode === PolicyEngineMode.All}>
|
||||
<option
|
||||
value=${PolicyEngineMode.All}
|
||||
?selected=${this.instance?.policyEngineMode === PolicyEngineMode.All}
|
||||
>
|
||||
${t`ALL, all policies must match to grant access.`}
|
||||
</option>
|
||||
</select>
|
||||
@ -130,56 +169,88 @@ export class FlowForm extends ModelForm<Flow, string> {
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Designation`}
|
||||
?required=${true}
|
||||
name="designation">
|
||||
name="designation"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.designation === undefined}>---------</option>
|
||||
<option value="" ?selected=${this.instance?.designation === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${this.renderDesignations()}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
${until(config().then((c) => {
|
||||
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
|
||||
${until(
|
||||
config().then((c) => {
|
||||
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${t`Background`}
|
||||
name="background"
|
||||
>
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
${this.instance?.background
|
||||
? html`
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Currently set to:`} ${this.instance?.background}
|
||||
</p>
|
||||
`
|
||||
: html``}
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Background shown during execution.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
${this.instance?.background
|
||||
? html`
|
||||
<ak-form-element-horizontal>
|
||||
<div class="pf-c-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
@change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLInputElement;
|
||||
this.clearBackground = target.checked;
|
||||
}}
|
||||
/>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Clear background image`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Delete currently set background image.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
`
|
||||
: html``}`;
|
||||
}
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${t`Background`}
|
||||
name="background">
|
||||
<input type="file" value="" class="pf-c-form-control">
|
||||
${this.instance?.background ? html`
|
||||
<p class="pf-c-form__helper-text">${t`Currently set to:`} ${this.instance?.background}</p>
|
||||
`: html``}
|
||||
<p class="pf-c-form__helper-text">${t`Background shown during execution.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
${this.instance?.background ? html`
|
||||
<ak-form-element-horizontal>
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" @change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLInputElement;
|
||||
this.clearBackground = target.checked;
|
||||
}}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Clear background image`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Delete currently set background image.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
`: html``}`;
|
||||
}
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${t`Background`}
|
||||
name="background">
|
||||
<input type="text" value="${first(this.instance?.background, "")}" class="pf-c-form-control">
|
||||
<p class="pf-c-form__helper-text">${t`Background shown during execution.`}</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
}))}
|
||||
name="background"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.background, "")}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Background shown during execution.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
}),
|
||||
)}
|
||||
<ak-form-element-horizontal name="compatibilityMode">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.compatibilityMode, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Compatibility mode`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.compatibilityMode, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Compatibility mode`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Enable compatibility mode, increases compatibility with password managers on mobile devices.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Enable compatibility mode, increases compatibility with password managers on mobile devices.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ import "../../elements/forms/HorizontalFormElement";
|
||||
|
||||
@customElement("ak-flow-import-form")
|
||||
export class FlowImportForm extends Form<Flow> {
|
||||
|
||||
getSuccessMessage(): string {
|
||||
return t`Successfully imported flow.`;
|
||||
}
|
||||
@ -20,19 +19,16 @@ export class FlowImportForm extends Form<Flow> {
|
||||
throw new Error("No form data");
|
||||
}
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesImportFlowCreate({
|
||||
file: file
|
||||
file: file,
|
||||
});
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Flow`}
|
||||
name="flow">
|
||||
<input type="file" value="" class="pf-c-form-control">
|
||||
<ak-form-element-horizontal label=${t`Flow`} name="flow">
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
<p class="pf-c-form__helper-text">${t`Background shown during execution.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -61,101 +61,79 @@ export class FlowListPage extends TablePage<Flow> {
|
||||
html`${item.designation}`,
|
||||
html`${Array.from(item.stages || []).length}`,
|
||||
html`${Array.from(item.policies || []).length}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Flow`}
|
||||
</span>
|
||||
<ak-flow-form slot="form" .instancePk=${item.slug}>
|
||||
</ak-flow-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Flow`} </span>
|
||||
<ak-flow-form slot="form" .instancePk=${item.slug}> </ak-flow-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Flow`}
|
||||
.usedBy=${() => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesUsedByList({
|
||||
slug: item.slug,
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesDestroy({
|
||||
slug: item.slug,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>
|
||||
<button
|
||||
class="pf-c-button pf-m-secondary"
|
||||
@click=${() => {
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesExecuteRetrieve({
|
||||
slug: item.slug,
|
||||
})
|
||||
.then((link) => {
|
||||
window.location.assign(
|
||||
`${link.link}?next=/%23${window.location.href}`,
|
||||
);
|
||||
});
|
||||
}}
|
||||
>
|
||||
${t`Execute`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Flow`}
|
||||
.usedBy=${() => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesUsedByList({
|
||||
slug: item.slug
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesDestroy({
|
||||
slug: item.slug
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>
|
||||
<button
|
||||
class="pf-c-button pf-m-secondary"
|
||||
@click=${() => {
|
||||
new FlowsApi(DEFAULT_CONFIG).flowsInstancesExecuteRetrieve({
|
||||
slug: item.slug
|
||||
}).then(link => {
|
||||
window.location.assign(`${link.link}?next=/%23${window.location.href}`);
|
||||
});
|
||||
}}>
|
||||
${t`Execute`}
|
||||
</button>
|
||||
<a class="pf-c-button pf-m-secondary" href=${item.exportUrl}>
|
||||
${t`Export`}
|
||||
</a>`,
|
||||
<a class="pf-c-button pf-m-secondary" href=${item.exportUrl}> ${t`Export`} </a>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Flow`}
|
||||
</span>
|
||||
<ak-flow-form slot="form">
|
||||
</ak-flow-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Create`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Import`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Import Flow`}
|
||||
</span>
|
||||
<ak-flow-import-form slot="form">
|
||||
</ak-flow-import-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Import`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-confirm
|
||||
successMessage=${t`Successfully cleared flow cache`}
|
||||
errorMessage=${t`Failed to delete flow cache`}
|
||||
action=${t`Clear cache`}
|
||||
.onConfirm=${() => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesCacheClearCreate();
|
||||
}}>
|
||||
<span slot="header">
|
||||
${t`Clear Flow cache`}
|
||||
</span>
|
||||
<p slot="body">
|
||||
${t`Are you sure you want to clear the flow cache?
|
||||
return html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Flow`} </span>
|
||||
<ak-flow-form slot="form"> </ak-flow-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${t`Create`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Import`} </span>
|
||||
<span slot="header"> ${t`Import Flow`} </span>
|
||||
<ak-flow-import-form slot="form"> </ak-flow-import-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${t`Import`}</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-confirm
|
||||
successMessage=${t`Successfully cleared flow cache`}
|
||||
errorMessage=${t`Failed to delete flow cache`}
|
||||
action=${t`Clear cache`}
|
||||
.onConfirm=${() => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesCacheClearCreate();
|
||||
}}
|
||||
>
|
||||
<span slot="header"> ${t`Clear Flow cache`} </span>
|
||||
<p slot="body">
|
||||
${t`Are you sure you want to clear the flow cache?
|
||||
This will cause all flows to be re-evaluated on their next usage.`}
|
||||
</p>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary" type="button">
|
||||
${t`Clear cache`}
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-forms-confirm>`;
|
||||
</p>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary" type="button">
|
||||
${t`Clear cache`}
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-forms-confirm>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
|
||||
import "../../elements/Tabs";
|
||||
import "../../elements/PageHeader";
|
||||
@ -23,14 +31,16 @@ import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css";
|
||||
export class FlowViewPage extends LitElement {
|
||||
@property()
|
||||
set flowSlug(value: string) {
|
||||
new FlowsApi(DEFAULT_CONFIG).flowsInstancesRetrieve({
|
||||
slug: value
|
||||
}).then((flow) => {
|
||||
this.flow = flow;
|
||||
});
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesRetrieve({
|
||||
slug: value,
|
||||
})
|
||||
.then((flow) => {
|
||||
this.flow = flow;
|
||||
});
|
||||
}
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
flow!: Flow;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
@ -42,7 +52,7 @@ export class FlowViewPage extends LitElement {
|
||||
ak-tabs {
|
||||
height: 100%;
|
||||
}
|
||||
`
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
@ -53,10 +63,15 @@ export class FlowViewPage extends LitElement {
|
||||
return html`<ak-page-header
|
||||
icon="pf-icon pf-icon-process-automation"
|
||||
header=${this.flow.name}
|
||||
description=${this.flow.title}>
|
||||
description=${this.flow.title}
|
||||
>
|
||||
</ak-page-header>
|
||||
<ak-tabs>
|
||||
<div slot="page-overview" data-tab-title="${t`Flow Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div
|
||||
slot="page-overview"
|
||||
data-tab-title="${t`Flow Overview`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-l-gallery pf-m-gutter">
|
||||
<div class="pf-c-card pf-l-gallery__item">
|
||||
<div class="pf-c-card__title">${t`Related`}</div>
|
||||
@ -64,30 +79,40 @@ export class FlowViewPage extends LitElement {
|
||||
<dl class="pf-c-description-list">
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Execute flow`}</span>
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Execute flow`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<button
|
||||
class="pf-c-button pf-m-primary"
|
||||
@click=${() => {
|
||||
new FlowsApi(DEFAULT_CONFIG).flowsInstancesExecuteRetrieve({
|
||||
slug: this.flow.slug
|
||||
}).then(link => {
|
||||
const finalURL = `${link.link}?next=/%23${window.location.hash}`;
|
||||
window.open(finalURL, "_blank");
|
||||
});
|
||||
}}>
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesExecuteRetrieve({
|
||||
slug: this.flow.slug,
|
||||
})
|
||||
.then((link) => {
|
||||
const finalURL = `${link.link}?next=/%23${window.location.hash}`;
|
||||
window.open(finalURL, "_blank");
|
||||
});
|
||||
}}
|
||||
>
|
||||
${t`Execute`}
|
||||
</button>
|
||||
</div>
|
||||
</dd>
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Export flow`}</span>
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Export flow`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<a class="pf-c-button pf-m-secondary" href=${this.flow.exportUrl}>
|
||||
<a
|
||||
class="pf-c-button pf-m-secondary"
|
||||
href=${this.flow.exportUrl}
|
||||
>
|
||||
${t`Export`}
|
||||
</a>
|
||||
</div>
|
||||
@ -96,40 +121,56 @@ export class FlowViewPage extends LitElement {
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card pf-l-gallery__item" style="grid-column-end: span 4;grid-row-end: span 2;">
|
||||
<div
|
||||
class="pf-c-card pf-l-gallery__item"
|
||||
style="grid-column-end: span 4;grid-row-end: span 2;"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-flow-diagram flowSlug=${this.flow.slug}>
|
||||
</ak-flow-diagram>
|
||||
<ak-flow-diagram flowSlug=${this.flow.slug}> </ak-flow-diagram>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="page-stage-bindings" data-tab-title="${t`Stage Bindings`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div
|
||||
slot="page-stage-bindings"
|
||||
data-tab-title="${t`Stage Bindings`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-bound-stages-list .target=${this.flow.pk}>
|
||||
</ak-bound-stages-list>
|
||||
<ak-bound-stages-list .target=${this.flow.pk}> </ak-bound-stages-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="page-policy-bindings" data-tab-title="${t`Policy / Group / User Bindings`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div
|
||||
slot="page-policy-bindings"
|
||||
data-tab-title="${t`Policy / Group / User Bindings`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`These bindings control which users can access this flow.`}</div>
|
||||
<div class="pf-c-card__title">
|
||||
${t`These bindings control which users can access this flow.`}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-bound-policies-list .target=${this.flow.policybindingmodelPtrId}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="page-changelog" data-tab-title="${t`Changelog`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div
|
||||
slot="page-changelog"
|
||||
data-tab-title="${t`Changelog`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.flow.pk || ""}
|
||||
targetModelApp="authentik_flows"
|
||||
targetModelName="flow">
|
||||
targetModelName="flow"
|
||||
>
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
import { FlowsApi, FlowStageBinding, InvalidResponseActionEnum, PolicyEngineMode, Stage, StagesApi } from "authentik-api";
|
||||
import {
|
||||
FlowsApi,
|
||||
FlowStageBinding,
|
||||
InvalidResponseActionEnum,
|
||||
PolicyEngineMode,
|
||||
Stage,
|
||||
StagesApi,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -11,7 +18,6 @@ import { ModelForm } from "../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-stage-binding-form")
|
||||
export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<FlowStageBinding> {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsRetrieve({
|
||||
fsbUuid: pk,
|
||||
@ -33,11 +39,11 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
if (this.instance) {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsUpdate({
|
||||
fsbUuid: this.instance.pk || "",
|
||||
flowStageBindingRequest: data
|
||||
flowStageBindingRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsCreate({
|
||||
flowStageBindingRequest: data
|
||||
flowStageBindingRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -45,11 +51,13 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
groupStages(stages: Stage[]): TemplateResult {
|
||||
return html`
|
||||
<option value="">---------</option>
|
||||
${groupBy<Stage>(stages, (s => s.verboseName || "")).map(([group, stages]) => {
|
||||
${groupBy<Stage>(stages, (s) => s.verboseName || "").map(([group, stages]) => {
|
||||
return html`<optgroup label=${group}>
|
||||
${stages.map(stage => {
|
||||
const selected = (this.instance?.stage === stage.pk);
|
||||
return html`<option ?selected=${selected} value=${ifDefined(stage.pk)}>${stage.name}</option>`;
|
||||
${stages.map((stage) => {
|
||||
const selected = this.instance?.stage === stage.pk;
|
||||
return html`<option ?selected=${selected} value=${ifDefined(stage.pk)}>
|
||||
${stage.name}
|
||||
</option>`;
|
||||
})}
|
||||
</optgroup>`;
|
||||
})}
|
||||
@ -60,36 +68,47 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
if (this.instance) {
|
||||
return Promise.resolve(this.instance.order);
|
||||
}
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsList({
|
||||
target: this.targetPk || "",
|
||||
}).then(bindings => {
|
||||
const orders = bindings.results.map(binding => binding.order);
|
||||
if (orders.length < 1) {
|
||||
return 0;
|
||||
}
|
||||
return Math.max(...orders) + 1;
|
||||
});
|
||||
return new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsBindingsList({
|
||||
target: this.targetPk || "",
|
||||
})
|
||||
.then((bindings) => {
|
||||
const orders = bindings.results.map((binding) => binding.order);
|
||||
if (orders.length < 1) {
|
||||
return 0;
|
||||
}
|
||||
return Math.max(...orders) + 1;
|
||||
});
|
||||
}
|
||||
|
||||
renderTarget(): TemplateResult {
|
||||
if (this.instance?.target || this.targetPk) {
|
||||
return html`
|
||||
<input required name="target" type="hidden" value=${ifDefined(this.instance?.target || this.targetPk)}>
|
||||
<input
|
||||
required
|
||||
name="target"
|
||||
type="hidden"
|
||||
value=${ifDefined(this.instance?.target || this.targetPk)}
|
||||
/>
|
||||
`;
|
||||
}
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${t`Target`}
|
||||
?required=${true}
|
||||
name="target">
|
||||
return html`<ak-form-element-horizontal label=${t`Target`} ?required=${true} name="target">
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk"
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
// No ?selected check here, as this input isnt shown on update forms
|
||||
return html`<option value=${ifDefined(flow.pk)}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
// No ?selected check here, as this input isnt shown on update forms
|
||||
return html`<option value=${ifDefined(flow.pk)}>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
@ -97,30 +116,36 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
${this.renderTarget()}
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Stage`}
|
||||
?required=${true}
|
||||
name="stage">
|
||||
<ak-form-element-horizontal label=${t`Stage`} ?required=${true} name="stage">
|
||||
<select class="pf-c-form-control">
|
||||
${until(new StagesApi(DEFAULT_CONFIG).stagesAllList({
|
||||
ordering: "pk"
|
||||
}).then(stages => {
|
||||
return this.groupStages(stages.results);
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new StagesApi(DEFAULT_CONFIG)
|
||||
.stagesAllList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((stages) => {
|
||||
return this.groupStages(stages.results);
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Order`}
|
||||
?required=${true}
|
||||
name="order">
|
||||
<input type="number" value="${until(this.getOrder())}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Order`} ?required=${true} name="order">
|
||||
<input
|
||||
type="number"
|
||||
value="${until(this.getOrder())}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="evaluateOnPlan">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.evaluateOnPlan, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Evaluate on plan`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.evaluateOnPlan, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Evaluate on plan`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Evaluate policies during the Flow planning process. Disable this for input-based policies. Should be used in conjunction with 'Re-evaluate policies', as with both options disabled, policies are **not** evaluated.`}
|
||||
@ -128,44 +153,69 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="reEvaluatePolicies">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.reEvaluatePolicies, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Re-evaluate policies`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.reEvaluatePolicies, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Re-evaluate policies`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Evaluate policies before the Stage is present to the user.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Evaluate policies before the Stage is present to the user.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Invalid response action`}
|
||||
?required=${true}
|
||||
name="invalidResponseAction">
|
||||
name="invalidResponseAction"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${InvalidResponseActionEnum.Retry} ?selected=${this.instance?.invalidResponseAction === InvalidResponseActionEnum.Retry}>
|
||||
<option
|
||||
value=${InvalidResponseActionEnum.Retry}
|
||||
?selected=${this.instance?.invalidResponseAction ===
|
||||
InvalidResponseActionEnum.Retry}
|
||||
>
|
||||
${t`RETRY returns the error message and a similar challenge to the executor.`}
|
||||
</option>
|
||||
<option value=${InvalidResponseActionEnum.Restart} ?selected=${this.instance?.invalidResponseAction === InvalidResponseActionEnum.Restart}>
|
||||
<option
|
||||
value=${InvalidResponseActionEnum.Restart}
|
||||
?selected=${this.instance?.invalidResponseAction ===
|
||||
InvalidResponseActionEnum.Restart}
|
||||
>
|
||||
${t`RESTART restarts the flow from the beginning.`}
|
||||
</option>
|
||||
<option value=${InvalidResponseActionEnum.RestartWithContext} ?selected=${this.instance?.invalidResponseAction === InvalidResponseActionEnum.RestartWithContext}>
|
||||
<option
|
||||
value=${InvalidResponseActionEnum.RestartWithContext}
|
||||
?selected=${this.instance?.invalidResponseAction ===
|
||||
InvalidResponseActionEnum.RestartWithContext}
|
||||
>
|
||||
${t`RESTART restarts the flow from the beginning, while keeping the flow context.`}
|
||||
</option>
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Configure how the flow executor should handle an invalid response to a challenge.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Configure how the flow executor should handle an invalid response to a challenge.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Policy engine mode`}
|
||||
?required=${true}
|
||||
name="policyEngineMode">
|
||||
name="policyEngineMode"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${PolicyEngineMode.Any} ?selected=${this.instance?.policyEngineMode === PolicyEngineMode.Any}>
|
||||
<option
|
||||
value=${PolicyEngineMode.Any}
|
||||
?selected=${this.instance?.policyEngineMode === PolicyEngineMode.Any}
|
||||
>
|
||||
${t`ANY, any policy must match to include this stage access.`}
|
||||
</option>
|
||||
<option value=${PolicyEngineMode.All} ?selected=${this.instance?.policyEngineMode === PolicyEngineMode.All}>
|
||||
<option
|
||||
value=${PolicyEngineMode.All}
|
||||
?selected=${this.instance?.policyEngineMode === PolicyEngineMode.All}
|
||||
>
|
||||
${t`ALL, all policies must match to include this stage access.`}
|
||||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -16,10 +16,9 @@ import { ModelForm } from "../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-group-form")
|
||||
export class GroupForm extends ModelForm<Group, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<Group> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsRetrieve({
|
||||
groupUuid: pk
|
||||
groupUuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
@ -35,101 +34,129 @@ export class GroupForm extends ModelForm<Group, string> {
|
||||
if (this.instance?.pk) {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsUpdate({
|
||||
groupUuid: this.instance.pk || "",
|
||||
groupRequest: data
|
||||
groupRequest: data,
|
||||
});
|
||||
} else {
|
||||
data.users = Array.from(this.instance?.users || []);
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsCreate({
|
||||
groupRequest: data
|
||||
groupRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="isSuperuser">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.isSuperuser, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Is superuser`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.isSuperuser, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Is superuser`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Users added to this group will be superusers.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Users added to this group will be superusers.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Parent`}
|
||||
name="parent">
|
||||
<ak-form-element-horizontal label=${t`Parent`} name="parent">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.parent === undefined}>---------</option>
|
||||
${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => {
|
||||
return groups.results.map(group => {
|
||||
return html`<option value=${ifDefined(group.pk)} ?selected=${this.instance?.parent === group.pk}>${group.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.parent === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then((groups) => {
|
||||
return groups.results.map((group) => {
|
||||
return html`<option
|
||||
value=${ifDefined(group.pk)}
|
||||
?selected=${this.instance?.parent === group.pk}
|
||||
>
|
||||
${group.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Members`}
|
||||
name="users">
|
||||
<ak-form-element-horizontal label=${t`Members`} name="users">
|
||||
<div class="pf-c-input-group">
|
||||
<ak-group-member-select-table
|
||||
.confirm=${(items: User[]) => {
|
||||
// Because the model only has the IDs, map the user list to IDs
|
||||
const ids = items.map(u => u.pk || 0);
|
||||
const ids = items.map((u) => u.pk || 0);
|
||||
if (!this.instance) this.instance = {} as Group;
|
||||
this.instance.users = Array.from(this.instance?.users || []).concat(ids);
|
||||
this.instance.users = Array.from(this.instance?.users || []).concat(
|
||||
ids,
|
||||
);
|
||||
this.requestUpdate();
|
||||
return Promise.resolve();
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-control" type="button">
|
||||
<i class="fas fa-plus" aria-hidden="true"></i>
|
||||
</button>
|
||||
</ak-group-member-select-table>
|
||||
<div class="pf-c-form-control">
|
||||
<ak-chip-group>
|
||||
${until(new CoreApi(DEFAULT_CONFIG).coreUsersList({
|
||||
ordering: "username",
|
||||
}).then(users => {
|
||||
return users.results.map(user => {
|
||||
const selected = Array.from(this.instance?.users || []).some(su => {
|
||||
return su == user.pk;
|
||||
});
|
||||
if (!selected) return;
|
||||
return html`<ak-chip
|
||||
.removable=${true}
|
||||
value=${ifDefined(user.pk)}
|
||||
@remove=${() => {
|
||||
if (!this.instance) return;
|
||||
const users = Array.from(this.instance?.users || []);
|
||||
const idx = users.indexOf(user.pk || 0);
|
||||
users.splice(idx, 1);
|
||||
this.instance.users = users;
|
||||
this.requestUpdate();
|
||||
}}>
|
||||
${user.username}
|
||||
</ak-chip>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new CoreApi(DEFAULT_CONFIG)
|
||||
.coreUsersList({
|
||||
ordering: "username",
|
||||
})
|
||||
.then((users) => {
|
||||
return users.results.map((user) => {
|
||||
const selected = Array.from(
|
||||
this.instance?.users || [],
|
||||
).some((su) => {
|
||||
return su == user.pk;
|
||||
});
|
||||
if (!selected) return;
|
||||
return html`<ak-chip
|
||||
.removable=${true}
|
||||
value=${ifDefined(user.pk)}
|
||||
@remove=${() => {
|
||||
if (!this.instance) return;
|
||||
const users = Array.from(
|
||||
this.instance?.users || [],
|
||||
);
|
||||
const idx = users.indexOf(user.pk || 0);
|
||||
users.splice(idx, 1);
|
||||
this.instance.users = users;
|
||||
this.requestUpdate();
|
||||
}}
|
||||
>
|
||||
${user.username}
|
||||
</ak-chip>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</ak-chip-group>
|
||||
</div>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Hold control/command to select multiple items.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Attributes`}
|
||||
?required=${true}
|
||||
name="attributes">
|
||||
<ak-codemirror mode="yaml" value="${YAML.stringify(first(this.instance?.attributes, {}))}">
|
||||
<ak-form-element-horizontal label=${t`Attributes`} ?required=${true} name="attributes">
|
||||
<ak-codemirror
|
||||
mode="yaml"
|
||||
value="${YAML.stringify(first(this.instance?.attributes, {}))}"
|
||||
>
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">${t`Set custom attributes using YAML or JSON.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Set custom attributes using YAML or JSON.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -55,56 +55,40 @@ export class GroupListPage extends TablePage<Group> {
|
||||
html`${item.parent || "-"}`,
|
||||
html`${Array.from(item.users || []).length}`,
|
||||
html`${item.isSuperuser ? t`Yes` : t`No`}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Group`}
|
||||
</span>
|
||||
<ak-group-form slot="form" .instancePk=${item.pk}>
|
||||
</ak-group-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Group`}
|
||||
.usedBy=${() => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsUsedByList({
|
||||
groupUuid: item.pk
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsDestroy({
|
||||
groupUuid: item.pk
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Group`} </span>
|
||||
<ak-group-form slot="form" .instancePk=${item.pk}> </ak-group-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Group`}
|
||||
.usedBy=${() => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsUsedByList({
|
||||
groupUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsDestroy({
|
||||
groupUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Group`}
|
||||
</span>
|
||||
<ak-group-form slot="form">
|
||||
</ak-group-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Create`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Group`} </span>
|
||||
<ak-group-form slot="form"> </ak-group-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${t`Create`}</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ export class MemberSelectTable extends TableModal<User> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreUsersList({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
pageSize: PAGE_SIZE /2,
|
||||
pageSize: PAGE_SIZE / 2,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
@ -57,33 +57,30 @@ export class MemberSelectTable extends TableModal<User> {
|
||||
|
||||
renderModalInner(): TemplateResult {
|
||||
return html`<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1 class="pf-c-title pf-m-2xl">
|
||||
${t`Select users to add`}
|
||||
</h1>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
${this.renderTable()}
|
||||
</section>
|
||||
<footer class="pf-c-modal-box__footer">
|
||||
<ak-spinner-button
|
||||
.callAction=${() => {
|
||||
return this.confirm(this.selectedElements).then(() => {
|
||||
<div class="pf-c-content">
|
||||
<h1 class="pf-c-title pf-m-2xl">${t`Select users to add`}</h1>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-light">${this.renderTable()}</section>
|
||||
<footer class="pf-c-modal-box__footer">
|
||||
<ak-spinner-button
|
||||
.callAction=${() => {
|
||||
return this.confirm(this.selectedElements).then(() => {
|
||||
this.open = false;
|
||||
});
|
||||
}}
|
||||
class="pf-m-primary"
|
||||
>
|
||||
${t`Add`} </ak-spinner-button
|
||||
>
|
||||
<ak-spinner-button
|
||||
.callAction=${async () => {
|
||||
this.open = false;
|
||||
});
|
||||
}}
|
||||
class="pf-m-primary">
|
||||
${t`Add`}
|
||||
</ak-spinner-button>
|
||||
<ak-spinner-button
|
||||
.callAction=${async () => {
|
||||
this.open = false;
|
||||
}}
|
||||
class="pf-m-secondary">
|
||||
${t`Cancel`}
|
||||
</ak-spinner-button>
|
||||
</footer>`;
|
||||
}}
|
||||
class="pf-m-secondary"
|
||||
>
|
||||
${t`Cancel`}
|
||||
</ak-spinner-button>
|
||||
</footer>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -7,8 +7,7 @@ import { ModalButton } from "../../elements/buttons/ModalButton";
|
||||
|
||||
@customElement("ak-outpost-deployment-modal")
|
||||
export class OutpostDeploymentModal extends ModalButton {
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
outpost?: Outpost;
|
||||
|
||||
renderModalInner(): TemplateResult {
|
||||
@ -16,25 +15,38 @@ export class OutpostDeploymentModal extends ModalButton {
|
||||
<h1 class="pf-c-title pf-m-2xl">${t`Outpost Deployment Info`}</h1>
|
||||
</div>
|
||||
<div class="pf-c-modal-box__body">
|
||||
<p><a target="_blank" href="https://goauthentik.io/docs/outposts/outposts/#deploy">${t`View deployment documentation`}</a></p>
|
||||
<p>
|
||||
<a target="_blank" href="https://goauthentik.io/docs/outposts/outposts/#deploy"
|
||||
>${t`View deployment documentation`}</a
|
||||
>
|
||||
</p>
|
||||
<form class="pf-c-form">
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">AUTHENTIK_HOST</span>
|
||||
</label>
|
||||
<input class="pf-c-form-control" readonly type="text" value="${document.location.origin}" />
|
||||
<input
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
type="text"
|
||||
value="${document.location.origin}"
|
||||
/>
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">AUTHENTIK_TOKEN</span>
|
||||
</label>
|
||||
<div>
|
||||
<ak-token-copy-button identifier="${ifDefined(this.outpost?.tokenIdentifier)}">
|
||||
<ak-token-copy-button
|
||||
identifier="${ifDefined(this.outpost?.tokenIdentifier)}"
|
||||
>
|
||||
${t`Click to copy token`}
|
||||
</ak-token-copy-button>
|
||||
</div>
|
||||
</div>
|
||||
<h3>${t`If your authentik Instance is using a self-signed certificate, set this value.`}</h3>
|
||||
<h3>
|
||||
${t`If your authentik Instance is using a self-signed certificate, set this value.`}
|
||||
</h3>
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">AUTHENTIK_INSECURE</span>
|
||||
@ -44,10 +56,14 @@ export class OutpostDeploymentModal extends ModalButton {
|
||||
</form>
|
||||
</div>
|
||||
<footer class="pf-c-modal-box__footer pf-m-align-left">
|
||||
<button class="pf-c-button pf-m-primary" @click=${() => {this.open = false;}}>
|
||||
<button
|
||||
class="pf-c-button pf-m-primary"
|
||||
@click=${() => {
|
||||
this.open = false;
|
||||
}}
|
||||
>
|
||||
${t`Close`}
|
||||
</button>
|
||||
</footer>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -12,17 +12,18 @@ import { ModelForm } from "../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-outpost-form")
|
||||
export class OutpostForm extends ModelForm<Outpost, string> {
|
||||
|
||||
@property()
|
||||
type: OutpostTypeEnum = OutpostTypeEnum.Proxy;
|
||||
|
||||
loadInstance(pk: string): Promise<Outpost> {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesRetrieve({
|
||||
uuid: pk
|
||||
}).then(o => {
|
||||
this.type = o.type || OutpostTypeEnum.Proxy;
|
||||
return o;
|
||||
});
|
||||
return new OutpostsApi(DEFAULT_CONFIG)
|
||||
.outpostsInstancesRetrieve({
|
||||
uuid: pk,
|
||||
})
|
||||
.then((o) => {
|
||||
this.type = o.type || OutpostTypeEnum.Proxy;
|
||||
return o;
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
@ -37,11 +38,11 @@ export class OutpostForm extends ModelForm<Outpost, string> {
|
||||
if (this.instance) {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesUpdate({
|
||||
uuid: this.instance.pk || "",
|
||||
outpostRequest: data
|
||||
outpostRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesCreate({
|
||||
outpostRequest: data
|
||||
outpostRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -49,102 +50,155 @@ export class OutpostForm extends ModelForm<Outpost, string> {
|
||||
renderProviders(): Promise<TemplateResult[]> {
|
||||
switch (this.type) {
|
||||
case OutpostTypeEnum.Proxy:
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersProxyList({
|
||||
ordering: "pk"
|
||||
}).then(providers => {
|
||||
return providers.results.map(provider => {
|
||||
const selected = Array.from(this.instance?.providers || []).some(sp => {
|
||||
return sp == provider.pk;
|
||||
return new ProvidersApi(DEFAULT_CONFIG)
|
||||
.providersProxyList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((providers) => {
|
||||
return providers.results.map((provider) => {
|
||||
const selected = Array.from(this.instance?.providers || []).some(
|
||||
(sp) => {
|
||||
return sp == provider.pk;
|
||||
},
|
||||
);
|
||||
return html`<option
|
||||
value=${ifDefined(provider.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${provider.verboseName} ${provider.name}
|
||||
</option>`;
|
||||
});
|
||||
return html`<option value=${ifDefined(provider.pk)} ?selected=${selected}>${provider.verboseName} ${provider.name}</option>`;
|
||||
});
|
||||
});
|
||||
case OutpostTypeEnum.Ldap:
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersLdapList({
|
||||
ordering: "pk"
|
||||
}).then(providers => {
|
||||
return providers.results.map(provider => {
|
||||
const selected = Array.from(this.instance?.providers || []).some(sp => {
|
||||
return sp == provider.pk;
|
||||
return new ProvidersApi(DEFAULT_CONFIG)
|
||||
.providersLdapList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((providers) => {
|
||||
return providers.results.map((provider) => {
|
||||
const selected = Array.from(this.instance?.providers || []).some(
|
||||
(sp) => {
|
||||
return sp == provider.pk;
|
||||
},
|
||||
);
|
||||
return html`<option
|
||||
value=${ifDefined(provider.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${provider.verboseName} ${provider.name}
|
||||
</option>`;
|
||||
});
|
||||
return html`<option value=${ifDefined(provider.pk)} ?selected=${selected}>${provider.verboseName} ${provider.name}</option>`;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Type`}
|
||||
?required=${true}
|
||||
name="type">
|
||||
<select class="pf-c-form-control" @change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLSelectElement;
|
||||
this.type = target.selectedOptions[0].value as OutpostTypeEnum;
|
||||
}}>
|
||||
<option value=${OutpostTypeEnum.Proxy} ?selected=${this.instance?.type === OutpostTypeEnum.Proxy}>${t`Proxy`}</option>
|
||||
<option value=${OutpostTypeEnum.Ldap} ?selected=${this.instance?.type === OutpostTypeEnum.Ldap}>${t`LDAP (Technical preview)`}</option>
|
||||
<ak-form-element-horizontal label=${t`Type`} ?required=${true} name="type">
|
||||
<select
|
||||
class="pf-c-form-control"
|
||||
@change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLSelectElement;
|
||||
this.type = target.selectedOptions[0].value as OutpostTypeEnum;
|
||||
}}
|
||||
>
|
||||
<option
|
||||
value=${OutpostTypeEnum.Proxy}
|
||||
?selected=${this.instance?.type === OutpostTypeEnum.Proxy}
|
||||
>
|
||||
${t`Proxy`}
|
||||
</option>
|
||||
<option
|
||||
value=${OutpostTypeEnum.Ldap}
|
||||
?selected=${this.instance?.type === OutpostTypeEnum.Ldap}
|
||||
>
|
||||
${t`LDAP (Technical preview)`}
|
||||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Integration`}
|
||||
name="serviceConnection">
|
||||
<ak-form-element-horizontal label=${t`Integration`} name="serviceConnection">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.serviceConnection === undefined}>---------</option>
|
||||
${until(new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllList({
|
||||
ordering: "pk"
|
||||
}).then(scs => {
|
||||
return scs.results.map(sc => {
|
||||
let selected = this.instance?.serviceConnection === sc.pk;
|
||||
if (scs.results.length === 1 && !this.instance) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(sc.pk)} ?selected=${selected}>
|
||||
${sc.name} (${sc.verboseName})
|
||||
</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.serviceConnection === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new OutpostsApi(DEFAULT_CONFIG)
|
||||
.outpostsServiceConnectionsAllList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((scs) => {
|
||||
return scs.results.map((sc) => {
|
||||
let selected = this.instance?.serviceConnection === sc.pk;
|
||||
if (scs.results.length === 1 && !this.instance) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(sc.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${sc.name} (${sc.verboseName})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Selecting an integration enables the management of the outpost by authentik.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
See <a target="_blank" href="https://goauthentik.io/docs/outposts/outposts">documentation</a>.
|
||||
See
|
||||
<a target="_blank" href="https://goauthentik.io/docs/outposts/outposts"
|
||||
>documentation</a
|
||||
>.
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Providers`}
|
||||
?required=${true}
|
||||
name="providers">
|
||||
<ak-form-element-horizontal label=${t`Providers`} ?required=${true} name="providers">
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(this.renderProviders(), html`<option>${t`Loading...`}</option>`)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`You can only select providers that match the type of the outpost.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Configuration`}
|
||||
name="config">
|
||||
<ak-codemirror mode="yaml" value="${until(new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesDefaultSettingsRetrieve().then(config => {
|
||||
let fc = config.config;
|
||||
if (this.instance) {
|
||||
fc = this.instance.config;
|
||||
}
|
||||
return YAML.stringify(fc);
|
||||
}))}"></ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">${t`Set custom attributes using YAML or JSON.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
See <a target="_blank" href="https://goauthentik.io/docs/outposts/outposts#configuration">documentation</a>.
|
||||
${t`You can only select providers that match the type of the outpost.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Hold control/command to select multiple items.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${t`Configuration`} name="config">
|
||||
<ak-codemirror
|
||||
mode="yaml"
|
||||
value="${until(
|
||||
new OutpostsApi(DEFAULT_CONFIG)
|
||||
.outpostsInstancesDefaultSettingsRetrieve()
|
||||
.then((config) => {
|
||||
let fc = config.config;
|
||||
if (this.instance) {
|
||||
fc = this.instance.config;
|
||||
}
|
||||
return YAML.stringify(fc);
|
||||
}),
|
||||
)}"
|
||||
></ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Set custom attributes using YAML or JSON.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
See
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://goauthentik.io/docs/outposts/outposts#configuration"
|
||||
>documentation</a
|
||||
>.
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,14 +10,13 @@ import { EVENT_REFRESH } from "../../constants";
|
||||
|
||||
@customElement("ak-outpost-health")
|
||||
export class OutpostHealthElement extends LitElement {
|
||||
|
||||
@property()
|
||||
outpostId?: string;
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
outpostHealth?: OutpostHealth[];
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
showVersion = true;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
@ -34,11 +33,13 @@ export class OutpostHealthElement extends LitElement {
|
||||
|
||||
firstUpdated(): void {
|
||||
if (!this.outpostId) return;
|
||||
new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesHealthList({
|
||||
uuid: this.outpostId
|
||||
}).then(health => {
|
||||
this.outpostHealth = health;
|
||||
});
|
||||
new OutpostsApi(DEFAULT_CONFIG)
|
||||
.outpostsInstancesHealthList({
|
||||
uuid: this.outpostId,
|
||||
})
|
||||
.then((health) => {
|
||||
this.outpostHealth = health;
|
||||
});
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
@ -46,29 +47,38 @@ export class OutpostHealthElement extends LitElement {
|
||||
return html`<ak-spinner></ak-spinner>`;
|
||||
}
|
||||
if (this.outpostHealth.length === 0) {
|
||||
return html`
|
||||
<ul>
|
||||
<li role="cell">
|
||||
<ak-label color=${PFColor.Grey} text=${t`Not available`}></ak-label>
|
||||
</li>
|
||||
</ul>`;
|
||||
return html` <ul>
|
||||
<li role="cell">
|
||||
<ak-label color=${PFColor.Grey} text=${t`Not available`}></ak-label>
|
||||
</li>
|
||||
</ul>`;
|
||||
}
|
||||
return html`<ul>${this.outpostHealth.map((h) => {
|
||||
return html`<li>
|
||||
<ul>
|
||||
<li role="cell">
|
||||
<ak-label color=${PFColor.Green} text=${t`Last seen: ${h.lastSeen?.toLocaleTimeString()}`}></ak-label>
|
||||
</li>
|
||||
${this.showVersion ?
|
||||
html`<li role="cell">
|
||||
${h.versionOutdated ?
|
||||
html`<ak-label color=${PFColor.Red}
|
||||
text=${t`${h.version}, should be ${h.versionShould}`}></ak-label>` :
|
||||
html`<ak-label color=${PFColor.Green} text=${t`Version: ${h.version || ""}`}></ak-label>`}
|
||||
</li>` : html``}
|
||||
</ul>
|
||||
</li>`;
|
||||
})}</ul>`;
|
||||
return html`<ul>
|
||||
${this.outpostHealth.map((h) => {
|
||||
return html`<li>
|
||||
<ul>
|
||||
<li role="cell">
|
||||
<ak-label
|
||||
color=${PFColor.Green}
|
||||
text=${t`Last seen: ${h.lastSeen?.toLocaleTimeString()}`}
|
||||
></ak-label>
|
||||
</li>
|
||||
${this.showVersion
|
||||
? html`<li role="cell">
|
||||
${h.versionOutdated
|
||||
? html`<ak-label
|
||||
color=${PFColor.Red}
|
||||
text=${t`${h.version}, should be ${h.versionShould}`}
|
||||
></ak-label>`
|
||||
: html`<ak-label
|
||||
color=${PFColor.Green}
|
||||
text=${t`Version: ${h.version || ""}`}
|
||||
></ak-label>`}
|
||||
</li>`
|
||||
: html``}
|
||||
</ul>
|
||||
</li>`;
|
||||
})}
|
||||
</ul>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -58,90 +58,78 @@ export class OutpostListPage extends TablePage<Outpost> {
|
||||
}
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`<ul>${item.providersObj?.map((p) => {
|
||||
return html`<li><a href="#/core/providers/${p.pk}">${p.name}</a></li>`;
|
||||
})}</ul>`,
|
||||
html`<ul>
|
||||
${item.providersObj?.map((p) => {
|
||||
return html`<li>
|
||||
<a href="#/core/providers/${p.pk}">${p.name}</a>
|
||||
</li>`;
|
||||
})}
|
||||
</ul>`,
|
||||
html`${item.serviceConnectionObj?.name || t`No integration active`}`,
|
||||
html`<ak-outpost-health outpostId=${ifDefined(item.pk)}></ak-outpost-health>`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Outpost`}
|
||||
</span>
|
||||
<ak-outpost-form slot="form" .instancePk=${item.pk}>
|
||||
</ak-outpost-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Outpost`}
|
||||
.usedBy=${() => {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesUsedByList({
|
||||
uuid: item.pk
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesDestroy({
|
||||
uuid: item.pk
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>
|
||||
<ak-outpost-deployment-modal .outpost=${item} size=${PFSize.Medium}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-tertiary">
|
||||
${t`View Deployment Info`}
|
||||
</button>
|
||||
</ak-outpost-deployment-modal>`,
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Outpost`} </span>
|
||||
<ak-outpost-form slot="form" .instancePk=${item.pk}> </ak-outpost-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Outpost`}
|
||||
.usedBy=${() => {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesUsedByList({
|
||||
uuid: item.pk,
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesDestroy({
|
||||
uuid: item.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>
|
||||
<ak-outpost-deployment-modal .outpost=${item} size=${PFSize.Medium}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-tertiary">
|
||||
${t`View Deployment Info`}
|
||||
</button>
|
||||
</ak-outpost-deployment-modal>`,
|
||||
];
|
||||
}
|
||||
|
||||
rowInbuilt(item: Outpost): TemplateResult[] {
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`<ul>${item.providersObj?.map((p) => {
|
||||
return html`<li><a href="#/core/providers/${p.pk}">${p.name}</a></li>`;
|
||||
})}</ul>`,
|
||||
html`<ul>
|
||||
${item.providersObj?.map((p) => {
|
||||
return html`<li>
|
||||
<a href="#/core/providers/${p.pk}">${p.name}</a>
|
||||
</li>`;
|
||||
})}
|
||||
</ul>`,
|
||||
html`${item.serviceConnectionObj?.name || t`No integration active`}`,
|
||||
html`<ak-outpost-health .showVersion=${false} outpostId=${ifDefined(item.pk)}></ak-outpost-health>`,
|
||||
html`<ak-outpost-health
|
||||
.showVersion=${false}
|
||||
outpostId=${ifDefined(item.pk)}
|
||||
></ak-outpost-health>`,
|
||||
html`<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Outpost`}
|
||||
</span>
|
||||
<ak-outpost-form slot="form" .instancePk=${item.pk}>
|
||||
</ak-outpost-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Outpost`} </span>
|
||||
<ak-outpost-form slot="form" .instancePk=${item.pk}> </ak-outpost-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Outpost`}
|
||||
</span>
|
||||
<ak-outpost-form slot="form">
|
||||
</ak-outpost-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Create`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Outpost`} </span>
|
||||
<ak-outpost-form slot="form"> </ak-outpost-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${t`Create`}</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ import { ModelForm } from "../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-service-connection-docker-form")
|
||||
export class ServiceConnectionDockerForm extends ModelForm<DockerServiceConnection, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<DockerServiceConnection> {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsDockerRetrieve({
|
||||
uuid: pk,
|
||||
@ -30,70 +29,109 @@ export class ServiceConnectionDockerForm extends ModelForm<DockerServiceConnecti
|
||||
if (this.instance) {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsDockerUpdate({
|
||||
uuid: this.instance.pk || "",
|
||||
dockerServiceConnectionRequest: data
|
||||
dockerServiceConnectionRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsDockerCreate({
|
||||
dockerServiceConnectionRequest: data
|
||||
dockerServiceConnectionRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="local">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.local, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Local`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.local, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Local`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`If enabled, use the local connection. Required Docker socket/Kubernetes Integration.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`If enabled, use the local connection. Required Docker socket/Kubernetes Integration.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Docker URL`}
|
||||
?required=${true}
|
||||
name="url">
|
||||
<input type="text" value="${ifDefined(this.instance?.url)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system.`}</p>
|
||||
<ak-form-element-horizontal label=${t`Docker URL`} ?required=${true} name="url">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.url)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`TLS Verification Certificate`}
|
||||
name="tlsVerification">
|
||||
name="tlsVerification"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.tlsVerification === undefined}>---------</option>
|
||||
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
|
||||
ordering: "pk"
|
||||
}).then(certs => {
|
||||
return certs.results.map(cert => {
|
||||
return html`<option value=${ifDefined(cert.pk)} ?selected=${this.instance?.tlsVerification === cert.pk}>${cert.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.tlsVerification === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CryptoApi(DEFAULT_CONFIG)
|
||||
.cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((certs) => {
|
||||
return certs.results.map((cert) => {
|
||||
return html`<option
|
||||
value=${ifDefined(cert.pk)}
|
||||
?selected=${this.instance?.tlsVerification === cert.pk}
|
||||
>
|
||||
${cert.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`CA which the endpoint's Certificate is verified against. Can be left empty for no validation.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`CA which the endpoint's Certificate is verified against. Can be left empty for no validation.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`TLS Authentication Certificate`}
|
||||
name="tlsAuthentication">
|
||||
name="tlsAuthentication"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.tlsAuthentication === undefined}>---------</option>
|
||||
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
|
||||
ordering: "pk"
|
||||
}).then(certs => {
|
||||
return certs.results.map(cert => {
|
||||
return html`<option value=${ifDefined(cert.pk)} ?selected=${this.instance?.tlsAuthentication === cert.pk}>${cert.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.tlsAuthentication === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CryptoApi(DEFAULT_CONFIG)
|
||||
.cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((certs) => {
|
||||
return certs.results.map((cert) => {
|
||||
return html`<option
|
||||
value=${ifDefined(cert.pk)}
|
||||
?selected=${this.instance?.tlsAuthentication === cert.pk}
|
||||
>
|
||||
${cert.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Certificate/Key used for authentication. Can be left empty for no authentication.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Certificate/Key used for authentication. Can be left empty for no authentication.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11,8 +11,10 @@ import { first } from "../../utils";
|
||||
import { ModelForm } from "../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-service-connection-kubernetes-form")
|
||||
export class ServiceConnectionKubernetesForm extends ModelForm<KubernetesServiceConnection, string> {
|
||||
|
||||
export class ServiceConnectionKubernetesForm extends ModelForm<
|
||||
KubernetesServiceConnection,
|
||||
string
|
||||
> {
|
||||
loadInstance(pk: string): Promise<KubernetesServiceConnection> {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsKubernetesRetrieve({
|
||||
uuid: pk,
|
||||
@ -31,40 +33,48 @@ export class ServiceConnectionKubernetesForm extends ModelForm<KubernetesService
|
||||
if (this.instance) {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsKubernetesUpdate({
|
||||
uuid: this.instance.pk || "",
|
||||
kubernetesServiceConnectionRequest: data
|
||||
kubernetesServiceConnectionRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsKubernetesCreate({
|
||||
kubernetesServiceConnectionRequest: data
|
||||
kubernetesServiceConnectionRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="local">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.local, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Local`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.local, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Local`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`If enabled, use the local connection. Required Docker socket/Kubernetes Integration.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`If enabled, use the local connection. Required Docker socket/Kubernetes Integration.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Kubeconfig`}
|
||||
name="kubeconfig">
|
||||
<ak-codemirror mode="yaml" value="${YAML.stringify(first(this.instance?.kubeconfig, {}))}">
|
||||
<ak-form-element-horizontal label=${t`Kubeconfig`} name="kubeconfig">
|
||||
<ak-codemirror
|
||||
mode="yaml"
|
||||
value="${YAML.stringify(first(this.instance?.kubeconfig, {}))}"
|
||||
>
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">${t`Set custom attributes using YAML or JSON.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Set custom attributes using YAML or JSON.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -63,86 +63,90 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
|
||||
html`${item.verboseName}`,
|
||||
html`${item.local ? t`Yes` : t`No`}`,
|
||||
html`${until(
|
||||
new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllStateRetrieve({
|
||||
uuid: item.pk || ""
|
||||
}).then((state) => {
|
||||
if (state.healthy) {
|
||||
return html`<ak-label color=${PFColor.Green} text=${ifDefined(state.version)}></ak-label>`;
|
||||
}
|
||||
return html`<ak-label color=${PFColor.Red} text=${t`Unhealthy`}></ak-label>`;
|
||||
}), html`<ak-spinner></ak-spinner>`)}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update ${item.verboseName}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
"instancePk": item.pk
|
||||
new OutpostsApi(DEFAULT_CONFIG)
|
||||
.outpostsServiceConnectionsAllStateRetrieve({
|
||||
uuid: item.pk || "",
|
||||
})
|
||||
.then((state) => {
|
||||
if (state.healthy) {
|
||||
return html`<ak-label
|
||||
color=${PFColor.Green}
|
||||
text=${ifDefined(state.version)}
|
||||
></ak-label>`;
|
||||
}
|
||||
return html`<ak-label
|
||||
color=${PFColor.Red}
|
||||
text=${t`Unhealthy`}
|
||||
></ak-label>`;
|
||||
}),
|
||||
html`<ak-spinner></ak-spinner>`,
|
||||
)}`,
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update ${item.verboseName}`} </span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
instancePk: item.pk,
|
||||
}}
|
||||
type=${ifDefined(item.component)}
|
||||
>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Outpost Service-connection`}
|
||||
.usedBy=${() => {
|
||||
return new OutpostsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).outpostsServiceConnectionsAllUsedByList({
|
||||
uuid: item.pk,
|
||||
});
|
||||
}}
|
||||
type=${ifDefined(item.component)}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Outpost Service-connection`}
|
||||
.usedBy=${() => {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllUsedByList({
|
||||
uuid: item.pk
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllDestroy({
|
||||
uuid: item.pk
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
.delete=${() => {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllDestroy(
|
||||
{
|
||||
uuid: item.pk,
|
||||
},
|
||||
);
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
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`Create`}</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(new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create ${type.name}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
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`Create`}</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(
|
||||
new OutpostsApi(DEFAULT_CONFIG)
|
||||
.outpostsServiceConnectionsAllTypesList()
|
||||
.then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create ${type.name}`} </span>
|
||||
<ak-proxy-form slot="form" type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br />
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}),
|
||||
html`<ak-spinner></ak-spinner>`,
|
||||
)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
@property()
|
||||
target?: string;
|
||||
|
||||
@property({type: Boolean})
|
||||
@property({ type: Boolean })
|
||||
policyOnly = false;
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<PolicyBinding>> {
|
||||
@ -61,52 +61,32 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
|
||||
getObjectEditButton(item: PolicyBinding): TemplateResult {
|
||||
if (item.policy) {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update ${item.policyObj?.name}`}
|
||||
</span>
|
||||
return html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update ${item.policyObj?.name}`} </span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
"instancePk": item.policyObj?.pk
|
||||
instancePk: item.policyObj?.pk,
|
||||
}}
|
||||
type=${ifDefined(item.policyObj?.component)}>
|
||||
type=${ifDefined(item.policyObj?.component)}
|
||||
>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit Policy`}
|
||||
</button>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit Policy`}</button>
|
||||
</ak-forms-modal>`;
|
||||
} else if (item.group) {
|
||||
return html`<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Group`}
|
||||
</span>
|
||||
<ak-group-form slot="form" .instancePk=${item.groupObj?.pk}>
|
||||
</ak-group-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit Group`}
|
||||
</button>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Group`} </span>
|
||||
<ak-group-form slot="form" .instancePk=${item.groupObj?.pk}> </ak-group-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit Group`}</button>
|
||||
</ak-forms-modal>`;
|
||||
} else if (item.user) {
|
||||
return html`<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update User`}
|
||||
</span>
|
||||
<ak-user-form slot="form" .instancePk=${item.userObj?.pk}>
|
||||
</ak-user-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit User`}
|
||||
</button>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update User`} </span>
|
||||
<ak-user-form slot="form" .instancePk=${item.userObj?.pk}> </ak-user-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit User`}</button>
|
||||
</ak-forms-modal>`;
|
||||
} else {
|
||||
return html``;
|
||||
@ -119,55 +99,57 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
html`${item.enabled ? t`Yes` : t`No`}`,
|
||||
html`${item.order}`,
|
||||
html`${item.timeout}`,
|
||||
html`
|
||||
${this.getObjectEditButton(item)}
|
||||
<ak-forms-modal size=${PFSize.Medium}>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Binding`}
|
||||
</span>
|
||||
<ak-policy-binding-form slot="form" .instancePk=${item.pk} targetPk=${ifDefined(this.target)} ?policyOnly=${this.policyOnly}>
|
||||
</ak-policy-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit Binding`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Policy binding`}
|
||||
.usedBy=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsUsedByList({
|
||||
policyBindingUuid: item.pk
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsDestroy({
|
||||
policyBindingUuid: item.pk,
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete Binding`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
html` ${this.getObjectEditButton(item)}
|
||||
<ak-forms-modal size=${PFSize.Medium}>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Binding`} </span>
|
||||
<ak-policy-binding-form
|
||||
slot="form"
|
||||
.instancePk=${item.pk}
|
||||
targetPk=${ifDefined(this.target)}
|
||||
?policyOnly=${this.policyOnly}
|
||||
>
|
||||
</ak-policy-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit Binding`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Policy binding`}
|
||||
.usedBy=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsUsedByList({
|
||||
policyBindingUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsDestroy({
|
||||
policyBindingUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete Binding`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(html`<ak-empty-state header=${t`No Policies bound.`} icon="pf-icon-module">
|
||||
<div slot="body">
|
||||
${t`No policies are currently bound to this object.`}
|
||||
</div>
|
||||
return super.renderEmpty(html`<ak-empty-state
|
||||
header=${t`No Policies bound.`}
|
||||
icon="pf-icon-module"
|
||||
>
|
||||
<div slot="body">${t`No policies are currently bound to this object.`}</div>
|
||||
<div slot="primary">
|
||||
<ak-forms-modal size=${PFSize.Medium}>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Binding`}
|
||||
</span>
|
||||
<ak-policy-binding-form slot="form" targetPk=${ifDefined(this.target)} ?policyOnly=${this.policyOnly}>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Binding`} </span>
|
||||
<ak-policy-binding-form
|
||||
slot="form"
|
||||
targetPk=${ifDefined(this.target)}
|
||||
?policyOnly=${this.policyOnly}
|
||||
>
|
||||
</ak-policy-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Create Binding`}
|
||||
@ -178,50 +160,46 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal size=${PFSize.Medium}>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Binding`}
|
||||
</span>
|
||||
<ak-policy-binding-form slot="form" targetPk=${ifDefined(this.target)} ?policyOnly=${this.policyOnly}>
|
||||
</ak-policy-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Create Binding`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-secondary pf-c-button pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">${t`Create Policy`}</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(new PoliciesApi(DEFAULT_CONFIG).policiesAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create ${type.name}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
return html` <ak-forms-modal size=${PFSize.Medium}>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Binding`} </span>
|
||||
<ak-policy-binding-form
|
||||
slot="form"
|
||||
targetPk=${ifDefined(this.target)}
|
||||
?policyOnly=${this.policyOnly}
|
||||
>
|
||||
</ak-policy-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Create Binding`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-secondary pf-c-button pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">${t`Create Policy`}</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(
|
||||
new PoliciesApi(DEFAULT_CONFIG).policiesAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create ${type.name}`} </span>
|
||||
<ak-proxy-form slot="form" type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br />
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}),
|
||||
html`<ak-spinner></ak-spinner>`,
|
||||
)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,36 +12,39 @@ import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import { ModelForm } from "../../elements/forms/ModelForm";
|
||||
|
||||
enum target {
|
||||
policy, group, user
|
||||
policy,
|
||||
group,
|
||||
user,
|
||||
}
|
||||
|
||||
@customElement("ak-policy-binding-form")
|
||||
export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<PolicyBinding> {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsRetrieve({
|
||||
policyBindingUuid: pk
|
||||
}).then(binding => {
|
||||
if (binding?.policyObj) {
|
||||
this.policyGroupUser = target.policy;
|
||||
}
|
||||
if (binding?.groupObj) {
|
||||
this.policyGroupUser = target.group;
|
||||
}
|
||||
if (binding?.userObj) {
|
||||
this.policyGroupUser = target.user;
|
||||
}
|
||||
return binding;
|
||||
});
|
||||
return new PoliciesApi(DEFAULT_CONFIG)
|
||||
.policiesBindingsRetrieve({
|
||||
policyBindingUuid: pk,
|
||||
})
|
||||
.then((binding) => {
|
||||
if (binding?.policyObj) {
|
||||
this.policyGroupUser = target.policy;
|
||||
}
|
||||
if (binding?.groupObj) {
|
||||
this.policyGroupUser = target.group;
|
||||
}
|
||||
if (binding?.userObj) {
|
||||
this.policyGroupUser = target.user;
|
||||
}
|
||||
return binding;
|
||||
});
|
||||
}
|
||||
|
||||
@property()
|
||||
targetPk?: string;
|
||||
|
||||
@property({type: Number})
|
||||
@property({ type: Number })
|
||||
policyGroupUser: target = target.policy;
|
||||
|
||||
@property({type: Boolean})
|
||||
@property({ type: Boolean })
|
||||
policyOnly = false;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
@ -53,33 +56,39 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(PFToggleGroup, PFContent, css`
|
||||
.pf-c-toggle-group {
|
||||
justify-content: center;
|
||||
}
|
||||
`);
|
||||
return super.styles.concat(
|
||||
PFToggleGroup,
|
||||
PFContent,
|
||||
css`
|
||||
.pf-c-toggle-group {
|
||||
justify-content: center;
|
||||
}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
send = (data: PolicyBinding): Promise<PolicyBinding> => {
|
||||
if (this.instance) {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsUpdate({
|
||||
policyBindingUuid: this.instance.pk || "",
|
||||
policyBindingRequest: data
|
||||
policyBindingRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsCreate({
|
||||
policyBindingRequest: data
|
||||
policyBindingRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
groupPolicies(policies: Policy[]): TemplateResult {
|
||||
return html`
|
||||
${groupBy<Policy>(policies, (p => p.verboseName || "")).map(([group, policies]) => {
|
||||
${groupBy<Policy>(policies, (p) => p.verboseName || "").map(([group, policies]) => {
|
||||
return html`<optgroup label=${group}>
|
||||
${policies.map(p => {
|
||||
const selected = (this.instance?.policy === p.pk);
|
||||
return html`<option ?selected=${selected} value=${ifDefined(p.pk)}>${p.name}</option>`;
|
||||
${policies.map((p) => {
|
||||
const selected = this.instance?.policy === p.pk;
|
||||
return html`<option ?selected=${selected} value=${ifDefined(p.pk)}>
|
||||
${p.name}
|
||||
</option>`;
|
||||
})}
|
||||
</optgroup>`;
|
||||
})}
|
||||
@ -90,48 +99,66 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
if (this.instance) {
|
||||
return Promise.resolve(this.instance.order);
|
||||
}
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsList({
|
||||
target: this.targetPk || "",
|
||||
}).then(bindings => {
|
||||
const orders = bindings.results.map(binding => binding.order);
|
||||
if (orders.length < 1) {
|
||||
return 0;
|
||||
}
|
||||
return Math.max(...orders) + 1;
|
||||
});
|
||||
return new PoliciesApi(DEFAULT_CONFIG)
|
||||
.policiesBindingsList({
|
||||
target: this.targetPk || "",
|
||||
})
|
||||
.then((bindings) => {
|
||||
const orders = bindings.results.map((binding) => binding.order);
|
||||
if (orders.length < 1) {
|
||||
return 0;
|
||||
}
|
||||
return Math.max(...orders) + 1;
|
||||
});
|
||||
}
|
||||
|
||||
renderModeSelector(): TemplateResult {
|
||||
if (this.policyOnly) {
|
||||
this.policyGroupUser = target.policy;
|
||||
return html`
|
||||
<div class="pf-c-toggle-group__item">
|
||||
<button class="pf-c-toggle-group__button pf-m-selected" type="button">
|
||||
<span class="pf-c-toggle-group__text">${t`Policy`}</span>
|
||||
</button>
|
||||
</div>`;
|
||||
return html` <div class="pf-c-toggle-group__item">
|
||||
<button class="pf-c-toggle-group__button pf-m-selected" type="button">
|
||||
<span class="pf-c-toggle-group__text">${t`Policy`}</span>
|
||||
</button>
|
||||
</div>`;
|
||||
}
|
||||
return html`
|
||||
<div class="pf-c-toggle-group__item">
|
||||
<button class="pf-c-toggle-group__button ${this.policyGroupUser === target.policy ? "pf-m-selected": ""}" type="button" @click=${() => {
|
||||
this.policyGroupUser = target.policy;
|
||||
}}>
|
||||
return html` <div class="pf-c-toggle-group__item">
|
||||
<button
|
||||
class="pf-c-toggle-group__button ${this.policyGroupUser === target.policy
|
||||
? "pf-m-selected"
|
||||
: ""}"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.policyGroupUser = target.policy;
|
||||
}}
|
||||
>
|
||||
<span class="pf-c-toggle-group__text">${t`Policy`}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pf-c-divider pf-m-vertical" role="separator"></div>
|
||||
<div class="pf-c-toggle-group__item">
|
||||
<button class="pf-c-toggle-group__button ${this.policyGroupUser === target.group ? "pf-m-selected" : ""}" type="button" @click=${() => {
|
||||
this.policyGroupUser = target.group;
|
||||
}}>
|
||||
<button
|
||||
class="pf-c-toggle-group__button ${this.policyGroupUser === target.group
|
||||
? "pf-m-selected"
|
||||
: ""}"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.policyGroupUser = target.group;
|
||||
}}
|
||||
>
|
||||
<span class="pf-c-toggle-group__text">${t`Group`}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pf-c-divider pf-m-vertical" role="separator"></div>
|
||||
<div class="pf-c-toggle-group__item">
|
||||
<button class="pf-c-toggle-group__button ${this.policyGroupUser === target.user ? "pf-m-selected" : ""}" type="button" @click=${() => {
|
||||
this.policyGroupUser = target.user;
|
||||
}}>
|
||||
<button
|
||||
class="pf-c-toggle-group__button ${this.policyGroupUser === target.user
|
||||
? "pf-m-selected"
|
||||
: ""}"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.policyGroupUser = target.user;
|
||||
}}
|
||||
>
|
||||
<span class="pf-c-toggle-group__text">${t`User`}</span>
|
||||
</button>
|
||||
</div>`;
|
||||
@ -141,89 +168,133 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<div class="pf-c-card pf-m-selectable pf-m-selected">
|
||||
<div class="pf-c-card__body">
|
||||
<div class="pf-c-toggle-group">
|
||||
${this.renderModeSelector()}
|
||||
</div>
|
||||
<div class="pf-c-toggle-group">${this.renderModeSelector()}</div>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Policy`}
|
||||
name="policy"
|
||||
?hidden=${this.policyGroupUser !== target.policy}>
|
||||
?hidden=${this.policyGroupUser !== target.policy}
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.policy === undefined}>---------</option>
|
||||
${until(new PoliciesApi(DEFAULT_CONFIG).policiesAllList({
|
||||
ordering: "pk"
|
||||
}).then(policies => {
|
||||
return this.groupPolicies(policies.results);
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.policy === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new PoliciesApi(DEFAULT_CONFIG)
|
||||
.policiesAllList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((policies) => {
|
||||
return this.groupPolicies(policies.results);
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Group`}
|
||||
name="group"
|
||||
?hidden=${this.policyGroupUser !== target.group}>
|
||||
?hidden=${this.policyGroupUser !== target.group}
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.group === undefined}>---------</option>
|
||||
${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({
|
||||
ordering: "pk"
|
||||
}).then(groups => {
|
||||
return groups.results.map(group => {
|
||||
return html`<option value=${ifDefined(group.pk)} ?selected=${group.pk === this.instance?.group}>${group.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.group === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CoreApi(DEFAULT_CONFIG)
|
||||
.coreGroupsList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((groups) => {
|
||||
return groups.results.map((group) => {
|
||||
return html`<option
|
||||
value=${ifDefined(group.pk)}
|
||||
?selected=${group.pk === this.instance?.group}
|
||||
>
|
||||
${group.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`User`}
|
||||
name="user"
|
||||
?hidden=${this.policyGroupUser !== target.user}>
|
||||
?hidden=${this.policyGroupUser !== target.user}
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.user === undefined}>---------</option>
|
||||
${until(new CoreApi(DEFAULT_CONFIG).coreUsersList({
|
||||
ordering: "pk"
|
||||
}).then(users => {
|
||||
return users.results.map(user => {
|
||||
return html`<option value=${ifDefined(user.pk)} ?selected=${user.pk === this.instance?.user}>${user.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.user === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CoreApi(DEFAULT_CONFIG)
|
||||
.coreUsersList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((users) => {
|
||||
return users.results.map((user) => {
|
||||
return html`<option
|
||||
value=${ifDefined(user.pk)}
|
||||
?selected=${user.pk === this.instance?.user}
|
||||
>
|
||||
${user.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</div>
|
||||
<input required name="target" type="hidden" value=${ifDefined(this.instance?.target || this.targetPk)}>
|
||||
<input
|
||||
required
|
||||
name="target"
|
||||
type="hidden"
|
||||
value=${ifDefined(this.instance?.target || this.targetPk)}
|
||||
/>
|
||||
<ak-form-element-horizontal name="enabled">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.enabled, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Enabled`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.enabled, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Enabled`} </label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="negate">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.negate, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Negate result`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.negate, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Negate result`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Negates the outcome of the binding. Messages are unaffected.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Order`}
|
||||
?required=${true}
|
||||
name="order">
|
||||
<input type="number" value="${until(this.getOrder())}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Order`} ?required=${true} name="order">
|
||||
<input
|
||||
type="number"
|
||||
value="${until(this.getOrder())}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Timeout`}
|
||||
?required=${true}
|
||||
name="timeout">
|
||||
<input type="number" value="${first(this.instance?.timeout, 30)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Timeout`} ?required=${true} name="timeout">
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.timeout, 30)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -52,130 +52,104 @@ export class PolicyListPage extends TablePage<Policy> {
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(t`Name`, "name"),
|
||||
new TableColumn(t`Type`),
|
||||
new TableColumn(""),
|
||||
];
|
||||
return [new TableColumn(t`Name`, "name"), new TableColumn(t`Type`), new TableColumn("")];
|
||||
}
|
||||
|
||||
row(item: Policy): TemplateResult[] {
|
||||
return [
|
||||
html`<div>
|
||||
<div>${item.name}</div>
|
||||
${(item.boundTo || 0) > 0 ?
|
||||
html`<i class="pf-icon pf-icon-ok"></i>
|
||||
<small>
|
||||
${t`Assigned to ${item.boundTo} objects.`}
|
||||
</small>`:
|
||||
html`<i class="pf-icon pf-icon-warning-triangle"></i>
|
||||
<small>${t`Warning: Policy is not assigned.`}</small>`}
|
||||
${(item.boundTo || 0) > 0
|
||||
? html`<i class="pf-icon pf-icon-ok"></i>
|
||||
<small> ${t`Assigned to ${item.boundTo} objects.`} </small>`
|
||||
: html`<i class="pf-icon pf-icon-warning-triangle"></i>
|
||||
<small>${t`Warning: Policy is not assigned.`}</small>`}
|
||||
</div>`,
|
||||
html`${item.verboseName}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update ${item.verboseName}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
"instancePk": item.pk
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update ${item.verboseName}`} </span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
instancePk: item.pk,
|
||||
}}
|
||||
type=${ifDefined(item.component)}
|
||||
>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-modal .closeAfterSuccessfulSubmit=${false}>
|
||||
<span slot="submit"> ${t`Test`} </span>
|
||||
<span slot="header"> ${t`Test Policy`} </span>
|
||||
<ak-policy-test-form slot="form" .policy=${item}> </ak-policy-test-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Test`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Policy`}
|
||||
.usedBy=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesAllUsedByList({
|
||||
policyUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
type=${ifDefined(item.component)}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-modal .closeAfterSuccessfulSubmit=${false}>
|
||||
<span slot="submit">
|
||||
${t`Test`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Test Policy`}
|
||||
</span>
|
||||
<ak-policy-test-form slot="form" .policy=${item}>
|
||||
</ak-policy-test-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Test`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Policy`}
|
||||
.usedBy=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesAllUsedByList({
|
||||
policyUuid: item.pk
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesAllDestroy({
|
||||
policyUuid: item.pk
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
.delete=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesAllDestroy({
|
||||
policyUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
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">
|
||||
<span class="pf-c-dropdown__toggle-text">${t`Create`}</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(new PoliciesApi(DEFAULT_CONFIG).policiesAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create ${type.name}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-confirm
|
||||
successMessage=${t`Successfully cleared policy cache`}
|
||||
errorMessage=${t`Failed to delete policy cache`}
|
||||
action=${t`Clear cache`}
|
||||
.onConfirm=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesAllCacheClearCreate();
|
||||
}}>
|
||||
<span slot="header">
|
||||
${t`Clear Policy cache`}
|
||||
</span>
|
||||
<p slot="body">
|
||||
${t`Are you sure you want to clear the policy cache?
|
||||
${until(
|
||||
new PoliciesApi(DEFAULT_CONFIG).policiesAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create ${type.name}`} </span>
|
||||
<ak-proxy-form slot="form" type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br />
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}),
|
||||
html`<ak-spinner></ak-spinner>`,
|
||||
)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-confirm
|
||||
successMessage=${t`Successfully cleared policy cache`}
|
||||
errorMessage=${t`Failed to delete policy cache`}
|
||||
action=${t`Clear cache`}
|
||||
.onConfirm=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesAllCacheClearCreate();
|
||||
}}
|
||||
>
|
||||
<span slot="header"> ${t`Clear Policy cache`} </span>
|
||||
<p slot="body">
|
||||
${t`Are you sure you want to clear the policy cache?
|
||||
This will cause all policies to be re-evaluated on their next usage.`}
|
||||
</p>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary" type="button">
|
||||
${t`Clear cache`}
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-forms-confirm>`;
|
||||
</p>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary" type="button">
|
||||
${t`Clear cache`}
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-forms-confirm>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -12,14 +12,13 @@ import { first } from "../../utils";
|
||||
|
||||
@customElement("ak-policy-test-form")
|
||||
export class PolicyTestForm extends Form<PolicyTestRequest> {
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
policy?: Policy;
|
||||
|
||||
@property({ attribute: false})
|
||||
@property({ attribute: false })
|
||||
result?: PolicyTestResult;
|
||||
|
||||
@property({ attribute: false})
|
||||
@property({ attribute: false })
|
||||
request?: PolicyTestRequest;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
@ -28,32 +27,37 @@ export class PolicyTestForm extends Form<PolicyTestRequest> {
|
||||
|
||||
send = (data: PolicyTestRequest): Promise<PolicyTestResult> => {
|
||||
this.request = data;
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesAllTestCreate({
|
||||
policyUuid: this.policy?.pk || "",
|
||||
policyTestRequest: data
|
||||
}).then(result => this.result = result);
|
||||
return new PoliciesApi(DEFAULT_CONFIG)
|
||||
.policiesAllTestCreate({
|
||||
policyUuid: this.policy?.pk || "",
|
||||
policyTestRequest: data,
|
||||
})
|
||||
.then((result) => (this.result = result));
|
||||
};
|
||||
|
||||
renderResult(): TemplateResult {
|
||||
return html`
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Passing`}>
|
||||
return html` <ak-form-element-horizontal label=${t`Passing`}>
|
||||
<div class="pf-c-form__group-label">
|
||||
<div class="c-form__horizontal-group">
|
||||
<span class="pf-c-form__label-text">${this.result?.passing ? t`Yes` : t`No`}</span>
|
||||
<span class="pf-c-form__label-text"
|
||||
>${this.result?.passing ? t`Yes` : t`No`}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Messages`}>
|
||||
<ak-form-element-horizontal label=${t`Messages`}>
|
||||
<div class="pf-c-form__group-label">
|
||||
<div class="c-form__horizontal-group">
|
||||
<ul>
|
||||
${(this.result?.messages || []).length > 0 ?
|
||||
this.result?.messages?.map(m => {
|
||||
return html`<li><span class="pf-c-form__label-text">${m}</span></li>`;
|
||||
}) :
|
||||
html`<li><span class="pf-c-form__label-text">-</span></li>`}
|
||||
${(this.result?.messages || []).length > 0
|
||||
? this.result?.messages?.map((m) => {
|
||||
return html`<li>
|
||||
<span class="pf-c-form__label-text">${m}</span>
|
||||
</li>`;
|
||||
})
|
||||
: html`<li>
|
||||
<span class="pf-c-form__label-text">-</span>
|
||||
</li>`}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -62,29 +66,37 @@ export class PolicyTestForm extends Form<PolicyTestRequest> {
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`User`}
|
||||
?required=${true}
|
||||
name="user">
|
||||
<ak-form-element-horizontal label=${t`User`} ?required=${true} name="user">
|
||||
<select class="pf-c-form-control">
|
||||
${until(new CoreApi(DEFAULT_CONFIG).coreUsersList({
|
||||
ordering: "username",
|
||||
}).then(users => {
|
||||
return users.results.map(user => {
|
||||
return html`<option ?selected=${this.request?.user.toString() === user.pk.toString()} value=${user.pk}>${user.username}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new CoreApi(DEFAULT_CONFIG)
|
||||
.coreUsersList({
|
||||
ordering: "username",
|
||||
})
|
||||
.then((users) => {
|
||||
return users.results.map((user) => {
|
||||
return html`<option
|
||||
?selected=${this.request?.user.toString() ===
|
||||
user.pk.toString()}
|
||||
value=${user.pk}
|
||||
>
|
||||
${user.username}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Context`}
|
||||
name="context">
|
||||
<ak-codemirror mode="yaml" value=${YAML.stringify(first(this.request?.context, {}))}>>
|
||||
<ak-form-element-horizontal label=${t`Context`} name="context">
|
||||
<ak-codemirror mode="yaml" value=${YAML.stringify(first(this.request?.context, {}))}
|
||||
>>
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">${t`Set custom attributes using YAML or JSON.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Set custom attributes using YAML or JSON.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
${this.result ? this.renderResult(): html``}
|
||||
${this.result ? this.renderResult() : html``}
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-policy-dummy-form")
|
||||
export class DummyPolicyForm extends ModelForm<DummyPolicy, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<DummyPolicy> {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesDummyRetrieve({
|
||||
policyUuid: pk,
|
||||
@ -30,11 +29,11 @@ export class DummyPolicyForm extends ModelForm<DummyPolicy, string> {
|
||||
if (this.instance) {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesDummyUpdate({
|
||||
policyUuid: this.instance.pk || "",
|
||||
dummyPolicyRequest: data
|
||||
dummyPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesDummyCreate({
|
||||
dummyPolicyRequest: data
|
||||
dummyPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -44,52 +43,69 @@ export class DummyPolicyForm extends ModelForm<DummyPolicy, string> {
|
||||
<div class="form-help-text">
|
||||
${t`A policy used for testing. Always returns the same result as specified below after waiting a random duration.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="executionLogging">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Execution logging`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.executionLogging, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Execution logging`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Policy-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Policy-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal name="result">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.result, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Pass policy?`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.result, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Pass policy?`} </label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Wait (min)`}
|
||||
?required=${true}
|
||||
name="waitMin">
|
||||
<input type="number" value="${first(this.instance?.waitMin, 1)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`The policy takes a random time to execute. This controls the minimum time it will take.`}</p>
|
||||
name="waitMin"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.waitMin, 1)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`The policy takes a random time to execute. This controls the minimum time it will take.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Wait (max)`}
|
||||
?required=${true}
|
||||
name="waitMax">
|
||||
<input type="number" value="${first(this.instance?.waitMax, 5)}" class="pf-c-form-control" required>
|
||||
name="waitMax"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.waitMax, 5)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-policy-event-matcher-form")
|
||||
export class EventMatcherPolicyForm extends ModelForm<EventMatcherPolicy, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<EventMatcherPolicy> {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesEventMatcherRetrieve({
|
||||
policyUuid: pk,
|
||||
@ -31,11 +30,11 @@ export class EventMatcherPolicyForm extends ModelForm<EventMatcherPolicy, string
|
||||
if (this.instance) {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesEventMatcherUpdate({
|
||||
policyUuid: this.instance.pk || "",
|
||||
eventMatcherPolicyRequest: data
|
||||
eventMatcherPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesEventMatcherCreate({
|
||||
eventMatcherPolicyRequest: data
|
||||
eventMatcherPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -45,63 +44,91 @@ export class EventMatcherPolicyForm extends ModelForm<EventMatcherPolicy, string
|
||||
<div class="form-help-text">
|
||||
${t`Matches an event against a set of criteria. If any of the configured values match, the policy passes.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="executionLogging">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Execution logging`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.executionLogging, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Execution logging`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Policy-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Policy-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Action`}
|
||||
name="action">
|
||||
<ak-form-element-horizontal label=${t`Action`} name="action">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.action === undefined}>---------</option>
|
||||
${until(new EventsApi(DEFAULT_CONFIG).eventsEventsActionsList().then(actions => {
|
||||
return actions.map(action => {
|
||||
return html`<option value=${action.component} ?selected=${this.instance?.action === action.component}>${action.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.action === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new EventsApi(DEFAULT_CONFIG)
|
||||
.eventsEventsActionsList()
|
||||
.then((actions) => {
|
||||
return actions.map((action) => {
|
||||
return html`<option
|
||||
value=${action.component}
|
||||
?selected=${this.instance?.action ===
|
||||
action.component}
|
||||
>
|
||||
${action.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Match created events with this action type. When left empty, all action types will be matched.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Match created events with this action type. When left empty, all action types will be matched.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Client IP`}
|
||||
name="clientIp">
|
||||
<input type="text" value="${ifDefined(this.instance?.clientIp || "")}" class="pf-c-form-control">
|
||||
<p class="pf-c-form__helper-text">${t`Matches Event's Client IP (strict matching, for network matching use an Expression Policy.`}</p>
|
||||
<ak-form-element-horizontal label=${t`Client IP`} name="clientIp">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.clientIp || "")}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Matches Event's Client IP (strict matching, for network matching use an Expression Policy.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`App`}
|
||||
name="app">
|
||||
<ak-form-element-horizontal label=${t`App`} name="app">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.app === undefined}>---------</option>
|
||||
${until(new AdminApi(DEFAULT_CONFIG).adminAppsList().then(apps => {
|
||||
return apps.map(app => {
|
||||
return html`<option value=${app.name} ?selected=${this.instance?.app === app.name}>${app.label}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.app === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new AdminApi(DEFAULT_CONFIG).adminAppsList().then((apps) => {
|
||||
return apps.map((app) => {
|
||||
return html`<option
|
||||
value=${app.name}
|
||||
?selected=${this.instance?.app === app.name}
|
||||
>
|
||||
${app.label}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Match events created by selected application. When left empty, all applications are matched.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Match events created by selected application. When left empty, all applications are matched.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-policy-password-expiry-form")
|
||||
export class PasswordExpiryPolicyForm extends ModelForm<PasswordExpiryPolicy, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<PasswordExpiryPolicy> {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordExpiryRetrieve({
|
||||
policyUuid: pk,
|
||||
@ -30,11 +29,11 @@ export class PasswordExpiryPolicyForm extends ModelForm<PasswordExpiryPolicy, st
|
||||
if (this.instance) {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordExpiryUpdate({
|
||||
policyUuid: this.instance.pk || "",
|
||||
passwordExpiryPolicyRequest: data
|
||||
passwordExpiryPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordExpiryCreate({
|
||||
passwordExpiryPolicyRequest: data
|
||||
passwordExpiryPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -44,37 +43,49 @@ export class PasswordExpiryPolicyForm extends ModelForm<PasswordExpiryPolicy, st
|
||||
<div class="form-help-text">
|
||||
${t`Checks if the request's user's password has been changed in the last x days, and denys based on settings.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="executionLogging">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Execution logging`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.executionLogging, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Execution logging`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Policy-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Policy-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Maximum age (in days)`}
|
||||
?required=${true}
|
||||
name="days">
|
||||
<input type="number" value="${ifDefined(this.instance?.days || "")}" class="pf-c-form-control" required>
|
||||
name="days"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${ifDefined(this.instance?.days || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="denyOnly">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.denyOnly, false)}>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.denyOnly, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Only fail the policy, don't invalidate user's password.`}
|
||||
</label>
|
||||
@ -84,5 +95,4 @@ export class PasswordExpiryPolicyForm extends ModelForm<PasswordExpiryPolicy, st
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-policy-expression-form")
|
||||
export class ExpressionPolicyForm extends ModelForm<ExpressionPolicy, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<ExpressionPolicy> {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionRetrieve({
|
||||
policyUuid: pk,
|
||||
@ -31,11 +30,11 @@ export class ExpressionPolicyForm extends ModelForm<ExpressionPolicy, string> {
|
||||
if (this.instance) {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionUpdate({
|
||||
policyUuid: this.instance.pk || "",
|
||||
expressionPolicyRequest: data
|
||||
expressionPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionCreate({
|
||||
expressionPolicyRequest: data
|
||||
expressionPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -45,37 +44,46 @@ export class ExpressionPolicyForm extends ModelForm<ExpressionPolicy, string> {
|
||||
<div class="form-help-text">
|
||||
${t`Executes the python snippet to determine whether to allow or deny a request.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="executionLogging">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Execution logging`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.executionLogging, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Execution logging`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Policy-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Policy-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Expression`}
|
||||
?required=${true}
|
||||
name="expression">
|
||||
<ak-codemirror mode="python" value="${ifDefined(this.instance?.expression)}">
|
||||
name="expression"
|
||||
>
|
||||
<ak-codemirror
|
||||
mode="python"
|
||||
value="${ifDefined(this.instance?.expression)}"
|
||||
>
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Expression using Python.`}
|
||||
<a target="_blank" href="https://goauthentik.io/docs/policies/expression">
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://goauthentik.io/docs/policies/expression"
|
||||
>
|
||||
${t`See documentation for a list of all variables.`}
|
||||
</a>
|
||||
</p>
|
||||
@ -84,5 +92,4 @@ export class ExpressionPolicyForm extends ModelForm<ExpressionPolicy, string> {
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-policy-hibp-form")
|
||||
export class HaveIBeenPwnedPolicyForm extends ModelForm<HaveIBeenPwendPolicy, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<HaveIBeenPwendPolicy> {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesHaveibeenpwnedRetrieve({
|
||||
policyUuid: pk,
|
||||
@ -30,11 +29,11 @@ export class HaveIBeenPwnedPolicyForm extends ModelForm<HaveIBeenPwendPolicy, st
|
||||
if (this.instance) {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesHaveibeenpwnedUpdate({
|
||||
policyUuid: this.instance.pk || "",
|
||||
haveIBeenPwendPolicyRequest: data
|
||||
haveIBeenPwendPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesHaveibeenpwnedCreate({
|
||||
haveIBeenPwendPolicyRequest: data
|
||||
haveIBeenPwendPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -45,45 +44,62 @@ export class HaveIBeenPwnedPolicyForm extends ModelForm<HaveIBeenPwendPolicy, st
|
||||
${t`Checks a value from the policy request against the Have I been Pwned API, and denys the request based upon that.
|
||||
Note that only a part of the hash of the password is sent, the full comparison is done clientside.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="executionLogging">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Execution logging`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.executionLogging, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Execution logging`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Policy-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Policy-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Password field`}
|
||||
?required=${true}
|
||||
name="passwordField">
|
||||
<input type="text" value="${ifDefined(this.instance?.passwordField || "password")}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Field key to check, field keys defined in Prompt stages are available.`}</p>
|
||||
name="passwordField"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.passwordField || "password")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Field key to check, field keys defined in Prompt stages are available.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Allowed count`}
|
||||
?required=${true}
|
||||
name="allowedCount">
|
||||
<input type="number" value="${first(this.instance?.allowedCount, 0)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Allow up to N occurrences in the HIBP database.`}</p>
|
||||
name="allowedCount"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.allowedCount, 0)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Allow up to N occurrences in the HIBP database.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-policy-password-form")
|
||||
export class PasswordPolicyForm extends ModelForm<PasswordPolicy, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<PasswordPolicy> {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordRetrieve({
|
||||
policyUuid: pk,
|
||||
@ -30,11 +29,11 @@ export class PasswordPolicyForm extends ModelForm<PasswordPolicy, string> {
|
||||
if (this.instance) {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordUpdate({
|
||||
policyUuid: this.instance.pk || "",
|
||||
passwordPolicyRequest: data
|
||||
passwordPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordCreate({
|
||||
passwordPolicyRequest: data
|
||||
passwordPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -44,83 +43,131 @@ export class PasswordPolicyForm extends ModelForm<PasswordPolicy, string> {
|
||||
<div class="form-help-text">
|
||||
${t`Checks the value from the policy request against several rules, mostly used to ensure password strength.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="executionLogging">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Execution logging`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.executionLogging, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Execution logging`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Policy-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Policy-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Password field`}
|
||||
?required=${true}
|
||||
name="passwordField">
|
||||
<input type="text" value="${ifDefined(this.instance?.passwordField || "password")}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Field key to check, field keys defined in Prompt stages are available.`}</p>
|
||||
name="passwordField"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.passwordField || "password")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Field key to check, field keys defined in Prompt stages are available.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Minimum length`}
|
||||
?required=${true}
|
||||
name="lengthMin">
|
||||
<input type="number" value="${first(this.instance?.lengthMin, 10)}" class="pf-c-form-control" required>
|
||||
name="lengthMin"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.lengthMin, 10)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Minimum amount of Uppercase Characters`}
|
||||
?required=${true}
|
||||
name="amountUppercase">
|
||||
<input type="number" value="${first(this.instance?.amountUppercase, 2)}" class="pf-c-form-control" required>
|
||||
name="amountUppercase"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.amountUppercase, 2)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Minimum amount of Lowercase Characters`}
|
||||
?required=${true}
|
||||
name="amountLowercase">
|
||||
<input type="number" value="${first(this.instance?.amountLowercase, 2)}" class="pf-c-form-control" required>
|
||||
name="amountLowercase"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.amountLowercase, 2)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Minimum amount of Symbols Characters`}
|
||||
?required=${true}
|
||||
name="amountSymbols">
|
||||
<input type="number" value="${first(this.instance?.amountSymbols, 2)}" class="pf-c-form-control" required>
|
||||
name="amountSymbols"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.amountSymbols, 2)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Error message`}
|
||||
?required=${true}
|
||||
name="errorMessage">
|
||||
<input type="text" value="${ifDefined(this.instance?.errorMessage)}" class="pf-c-form-control" required>
|
||||
name="errorMessage"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.errorMessage)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group>
|
||||
<span slot="header">
|
||||
${t`Advanced settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Advanced settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Symbol charset`}
|
||||
?required=${true}
|
||||
name="symbolCharset">
|
||||
<input type="text" value="${ifDefined(this.instance?.symbolCharset || "!\\\"#$%&'()*+,-./:;<=>?@[]^_`{|}~ ")}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Characters which are considered as symbols.`}</p>
|
||||
name="symbolCharset"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(
|
||||
this.instance?.symbolCharset ||
|
||||
"!\\\"#$%&'()*+,-./:;<=>?@[]^_`{|}~ ",
|
||||
)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Characters which are considered as symbols.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -51,25 +51,22 @@ export class IPReputationListPage extends TablePage<IPReputation> {
|
||||
return [
|
||||
html`${item.ip}`,
|
||||
html`${item.score}`,
|
||||
html`
|
||||
<ak-forms-delete
|
||||
html` <ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`IP Reputation`}
|
||||
.usedBy=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesReputationIpsUsedByList({
|
||||
id: item.pk
|
||||
id: item.pk,
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesReputationIpsDestroy({
|
||||
id: item.pk,
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-policy-reputation-form")
|
||||
export class ReputationPolicyForm extends ModelForm<ReputationPolicy, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<ReputationPolicy> {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesReputationRetrieve({
|
||||
policyUuid: pk,
|
||||
@ -30,11 +29,11 @@ export class ReputationPolicyForm extends ModelForm<ReputationPolicy, string> {
|
||||
if (this.instance) {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUpdate({
|
||||
policyUuid: this.instance.pk || "",
|
||||
reputationPolicyRequest: data
|
||||
reputationPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesReputationCreate({
|
||||
reputationPolicyRequest: data
|
||||
reputationPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -49,53 +48,64 @@ export class ReputationPolicyForm extends ModelForm<ReputationPolicy, string> {
|
||||
doesn't pass when either or both of the selected options are equal or less than the
|
||||
threshold.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="executionLogging">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Execution logging`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.executionLogging, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Execution logging`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Policy-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Policy-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal name="checkIp">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.checkIp, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Check IP`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.checkIp, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Check IP`} </label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="checkUsername">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.checkUsername, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Check Username`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.checkUsername, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Check Username`} </label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Threshold`}
|
||||
?required=${true}
|
||||
name="threshold">
|
||||
<input type="number" value="${ifDefined(this.instance?.threshold || -5)}" class="pf-c-form-control" required>
|
||||
name="threshold"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${ifDefined(this.instance?.threshold || -5)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -51,25 +51,22 @@ export class UserReputationListPage extends TablePage<UserReputation> {
|
||||
return [
|
||||
html`${item.username}`,
|
||||
html`${item.score}`,
|
||||
html`
|
||||
<ak-forms-delete
|
||||
html` <ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`User Reputation`}
|
||||
.usedBy=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUsersUsedByList({
|
||||
id: item.pk
|
||||
id: item.pk,
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUsersDestroy({
|
||||
id: item.pk,
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ import { ModelForm } from "../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-property-mapping-ldap-form")
|
||||
export class PropertyMappingLDAPForm extends ModelForm<LDAPPropertyMapping, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<LDAPPropertyMapping> {
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsLdapRetrieve({
|
||||
pmUuid: pk,
|
||||
@ -29,44 +28,53 @@ export class PropertyMappingLDAPForm extends ModelForm<LDAPPropertyMapping, stri
|
||||
if (this.instance) {
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsLdapUpdate({
|
||||
pmUuid: this.instance.pk || "",
|
||||
lDAPPropertyMappingRequest: data
|
||||
lDAPPropertyMappingRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsLdapCreate({
|
||||
lDAPPropertyMappingRequest: data
|
||||
lDAPPropertyMappingRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Object field`}
|
||||
?required=${true}
|
||||
name="objectField">
|
||||
<input type="text" value="${ifDefined(this.instance?.objectField)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Field of the user object this value is written to.`}</p>
|
||||
name="objectField"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.objectField)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Field of the user object this value is written to.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Expression`}
|
||||
?required=${true}
|
||||
name="expression">
|
||||
<ak-form-element-horizontal label=${t`Expression`} ?required=${true} name="expression">
|
||||
<ak-codemirror mode="python" value="${ifDefined(this.instance?.expression)}">
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Expression using Python.`}
|
||||
<a target="_blank" href="https://goauthentik.io/docs/property-mappings/expression/">
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://goauthentik.io/docs/property-mappings/expression/"
|
||||
>
|
||||
${t`See documentation for a list of all variables.`}
|
||||
</a>
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> {
|
||||
@property()
|
||||
order = "name";
|
||||
|
||||
@property({type: Boolean})
|
||||
@property({ type: Boolean })
|
||||
hideManaged = false;
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<PropertyMapping>> {
|
||||
@ -62,108 +62,104 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> {
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`${item.verboseName}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update ${item.verboseName}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
"instancePk": item.pk
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update ${item.verboseName}`} </span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
instancePk: item.pk,
|
||||
}}
|
||||
type=${ifDefined(item.component)}
|
||||
>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-modal .closeAfterSuccessfulSubmit=${false}>
|
||||
<span slot="submit"> ${t`Test`} </span>
|
||||
<span slot="header"> ${t`Test Property Mapping`} </span>
|
||||
<ak-property-mapping-test-form slot="form" .mapping=${item}>
|
||||
</ak-property-mapping-test-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Test`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Property Mapping`}
|
||||
.usedBy=${() => {
|
||||
return new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsAllUsedByList({
|
||||
pmUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
type=${ifDefined(item.component)}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-modal .closeAfterSuccessfulSubmit=${false}>
|
||||
<span slot="submit">
|
||||
${t`Test`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Test Property Mapping`}
|
||||
</span>
|
||||
<ak-property-mapping-test-form slot="form" .mapping=${item}>
|
||||
</ak-property-mapping-test-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Test`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Property Mapping`}
|
||||
.usedBy=${() => {
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllUsedByList({
|
||||
pmUuid: item.pk
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllDestroy({
|
||||
pmUuid: item.pk
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
.delete=${() => {
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllDestroy({
|
||||
pmUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
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`Create`}</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(new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create ${type.name}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
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`Create`}</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(
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsAllTypesList()
|
||||
.then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create ${type.name}`} </span>
|
||||
<ak-proxy-form slot="form" type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br />
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}),
|
||||
html`<ak-spinner></ak-spinner>`,
|
||||
)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
}
|
||||
|
||||
renderToolbarAfter(): TemplateResult {
|
||||
return html`
|
||||
<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group">
|
||||
<div class="pf-c-check">
|
||||
<input class="pf-c-check__input" type="checkbox" id="hide-managed" name="hide-managed" ?checked=${this.hideManaged} @change=${() => {
|
||||
this.hideManaged = !this.hideManaged;
|
||||
this.page = 1;
|
||||
this.fetch();
|
||||
}} />
|
||||
<label class="pf-c-check__label" for="hide-managed">${t`Hide managed mappings`}</label>
|
||||
<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group">
|
||||
<div class="pf-c-check">
|
||||
<input
|
||||
class="pf-c-check__input"
|
||||
type="checkbox"
|
||||
id="hide-managed"
|
||||
name="hide-managed"
|
||||
?checked=${this.hideManaged}
|
||||
@change=${() => {
|
||||
this.hideManaged = !this.hideManaged;
|
||||
this.page = 1;
|
||||
this.fetch();
|
||||
}}
|
||||
/>
|
||||
<label class="pf-c-check__label" for="hide-managed"
|
||||
>${t`Hide managed mappings`}</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,54 +28,63 @@ export class PropertyMappingLDAPForm extends ModelForm<SAMLPropertyMapping, stri
|
||||
if (this.instance) {
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlUpdate({
|
||||
pmUuid: this.instance.pk || "",
|
||||
sAMLPropertyMappingRequest: data
|
||||
sAMLPropertyMappingRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlCreate({
|
||||
sAMLPropertyMappingRequest: data
|
||||
sAMLPropertyMappingRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`SAML Attribute Name`}
|
||||
?required=${true}
|
||||
name="samlName">
|
||||
<input type="text" value="${ifDefined(this.instance?.samlName)}" class="pf-c-form-control" required>
|
||||
name="samlName"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.samlName)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Attribute name used for SAML Assertions. Can be a URN OID, a schema reference, or a any other string. If this property mapping is used for NameID Property, this field is discarded.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Friendly Name`}
|
||||
name="friendlyName">
|
||||
<input type="text" value="${ifDefined(this.instance?.friendlyName || "")}" class="pf-c-form-control">
|
||||
<ak-form-element-horizontal label=${t`Friendly Name`} name="friendlyName">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.friendlyName || "")}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Optionally set the 'FriendlyName' value of the Assertion attribute.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Expression`}
|
||||
?required=${true}
|
||||
name="expression">
|
||||
<ak-form-element-horizontal label=${t`Expression`} ?required=${true} name="expression">
|
||||
<ak-codemirror mode="python" value="${ifDefined(this.instance?.expression)}">
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Expression using Python.`}
|
||||
<a target="_blank" href="https://goauthentik.io/docs/property-mappings/expression/">
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://goauthentik.io/docs/property-mappings/expression/"
|
||||
>
|
||||
${t`See documentation for a list of all variables.`}
|
||||
</a>
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ import "../../elements/CodeMirror";
|
||||
|
||||
@customElement("ak-property-mapping-scope-form")
|
||||
export class PropertyMappingScopeForm extends ModelForm<ScopeMapping, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<ScopeMapping> {
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScopeRetrieve({
|
||||
pmUuid: pk,
|
||||
@ -29,51 +28,64 @@ export class PropertyMappingScopeForm extends ModelForm<ScopeMapping, string> {
|
||||
if (this.instance) {
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScopeUpdate({
|
||||
pmUuid: this.instance.pk || "",
|
||||
scopeMappingRequest: data
|
||||
scopeMappingRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScopeCreate({
|
||||
scopeMappingRequest: data
|
||||
scopeMappingRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Scope name`}
|
||||
?required=${true}
|
||||
name="scopeName">
|
||||
<input type="text" value="${ifDefined(this.instance?.scopeName)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Scope which the client can specify to access these properties.`}</p>
|
||||
<ak-form-element-horizontal label=${t`Scope name`} ?required=${true} name="scopeName">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.scopeName)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Scope which the client can specify to access these properties.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Description`}
|
||||
?required=${true}
|
||||
name="description">
|
||||
<input type="text" value="${ifDefined(this.instance?.description)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Description shown to the user when consenting. If left empty, the user won't be informed.`}</p>
|
||||
name="description"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.description)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Description shown to the user when consenting. If left empty, the user won't be informed.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Expression`}
|
||||
?required=${true}
|
||||
name="expression">
|
||||
<ak-form-element-horizontal label=${t`Expression`} ?required=${true} name="expression">
|
||||
<ak-codemirror mode="python" value="${ifDefined(this.instance?.expression)}">
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Expression using Python.`}
|
||||
<a target="_blank" href="https://goauthentik.io/docs/property-mappings/expression/">
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://goauthentik.io/docs/property-mappings/expression/"
|
||||
>
|
||||
${t`See documentation for a list of all variables.`}
|
||||
</a>
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { CoreApi, PolicyTestRequest, PropertyMapping, PropertymappingsApi, PropertyMappingTestResult } from "authentik-api";
|
||||
import {
|
||||
CoreApi,
|
||||
PolicyTestRequest,
|
||||
PropertyMapping,
|
||||
PropertymappingsApi,
|
||||
PropertyMappingTestResult,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -13,11 +19,10 @@ import { first } from "../../utils";
|
||||
|
||||
@customElement("ak-property-mapping-test-form")
|
||||
export class PolicyTestForm extends Form<PolicyTestRequest> {
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
mapping?: PropertyMapping;
|
||||
|
||||
@property({ attribute: false})
|
||||
@property({ attribute: false })
|
||||
result?: PropertyMappingTestResult;
|
||||
|
||||
@property({ attribute: false })
|
||||
@ -29,52 +34,62 @@ export class PolicyTestForm extends Form<PolicyTestRequest> {
|
||||
|
||||
send = (data: PolicyTestRequest): Promise<PropertyMappingTestResult> => {
|
||||
this.request = data;
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllTestCreate({
|
||||
pmUuid: this.mapping?.pk || "",
|
||||
policyTestRequest: data,
|
||||
formatResult: true,
|
||||
}).then(result => this.result = result);
|
||||
return new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsAllTestCreate({
|
||||
pmUuid: this.mapping?.pk || "",
|
||||
policyTestRequest: data,
|
||||
formatResult: true,
|
||||
})
|
||||
.then((result) => (this.result = result));
|
||||
};
|
||||
|
||||
renderResult(): TemplateResult {
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${t`Result`}>
|
||||
${this.result?.successful ?
|
||||
html`<ak-codemirror mode="javascript" ?readOnly=${true} value="${ifDefined(this.result?.result)}">
|
||||
</ak-codemirror>`:
|
||||
html`
|
||||
<div class="pf-c-form__group-label">
|
||||
<div class="c-form__horizontal-group">
|
||||
<span class="pf-c-form__label-text">${this.result?.result}</span>
|
||||
</div>
|
||||
</div>`}
|
||||
return html`<ak-form-element-horizontal label=${t`Result`}>
|
||||
${this.result?.successful
|
||||
? html`<ak-codemirror
|
||||
mode="javascript"
|
||||
?readOnly=${true}
|
||||
value="${ifDefined(this.result?.result)}"
|
||||
>
|
||||
</ak-codemirror>`
|
||||
: html` <div class="pf-c-form__group-label">
|
||||
<div class="c-form__horizontal-group">
|
||||
<span class="pf-c-form__label-text">${this.result?.result}</span>
|
||||
</div>
|
||||
</div>`}
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`User`}
|
||||
?required=${true}
|
||||
name="user">
|
||||
<ak-form-element-horizontal label=${t`User`} ?required=${true} name="user">
|
||||
<select class="pf-c-form-control">
|
||||
${until(new CoreApi(DEFAULT_CONFIG).coreUsersList({
|
||||
ordering: "username",
|
||||
}).then(users => {
|
||||
return users.results.map(user => {
|
||||
return html`<option ?selected=${this.request?.user.toString() === user.pk.toString()} value=${user.pk}>${user.username}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new CoreApi(DEFAULT_CONFIG)
|
||||
.coreUsersList({
|
||||
ordering: "username",
|
||||
})
|
||||
.then((users) => {
|
||||
return users.results.map((user) => {
|
||||
return html`<option
|
||||
?selected=${this.request?.user.toString() ===
|
||||
user.pk.toString()}
|
||||
value=${user.pk}
|
||||
>
|
||||
${user.username}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Context`}
|
||||
name="context">
|
||||
<ak-codemirror mode="yaml" value=${YAML.stringify(first(this.request?.context, {}))}>>
|
||||
<ak-form-element-horizontal label=${t`Context`} name="context">
|
||||
<ak-codemirror mode="yaml" value=${YAML.stringify(first(this.request?.context, {}))}
|
||||
>>
|
||||
</ak-codemirror>
|
||||
</ak-form-element-horizontal>
|
||||
${this.result ? this.renderResult(): html``}
|
||||
${this.result ? this.renderResult() : html``}
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -58,88 +58,76 @@ export class ProviderListPage extends TablePage<Provider> {
|
||||
|
||||
row(item: Provider): TemplateResult[] {
|
||||
return [
|
||||
html`<a href="#/core/providers/${item.pk}">
|
||||
${item.name}
|
||||
</a>`,
|
||||
item.assignedApplicationName ?
|
||||
html`<i class="pf-icon pf-icon-ok pf-m-success"></i>
|
||||
${t`Assigned to application `}
|
||||
<a href="#/core/applications/${item.assignedApplicationSlug}">${item.assignedApplicationName}</a>` :
|
||||
html`<i class="pf-icon pf-icon-warning-triangle pf-m-warning"></i>
|
||||
${t`Warning: Provider not assigned to any application.`}`,
|
||||
html`<a href="#/core/providers/${item.pk}"> ${item.name} </a>`,
|
||||
item.assignedApplicationName
|
||||
? html`<i class="pf-icon pf-icon-ok pf-m-success"></i>
|
||||
${t`Assigned to application `}
|
||||
<a href="#/core/applications/${item.assignedApplicationSlug}"
|
||||
>${item.assignedApplicationName}</a
|
||||
>`
|
||||
: html`<i class="pf-icon pf-icon-warning-triangle pf-m-warning"></i>
|
||||
${t`Warning: Provider not assigned to any application.`}`,
|
||||
html`${item.verboseName}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update ${item.verboseName}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
"instancePk": item.pk
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update ${item.verboseName}`} </span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
instancePk: item.pk,
|
||||
}}
|
||||
type=${ifDefined(item.component)}
|
||||
>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Provider`}
|
||||
.usedBy=${() => {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersAllUsedByList({
|
||||
id: item.pk,
|
||||
});
|
||||
}}
|
||||
type=${ifDefined(item.component)}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Provider`}
|
||||
.usedBy=${() => {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersAllUsedByList({
|
||||
id: item.pk
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersAllDestroy({
|
||||
id: item.pk
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
.delete=${() => {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersAllDestroy({
|
||||
id: item.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
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`Create`}</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(new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create ${type.name}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
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`Create`}</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(
|
||||
new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create ${type.name}`} </span>
|
||||
<ak-proxy-form slot="form" type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br />
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}),
|
||||
html`<ak-spinner></ak-spinner>`,
|
||||
)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -14,12 +14,13 @@ import { ifDefined } from "lit-html/directives/if-defined";
|
||||
|
||||
@customElement("ak-provider-view")
|
||||
export class ProviderViewPage extends LitElement {
|
||||
|
||||
@property({type: Number})
|
||||
@property({ type: Number })
|
||||
set providerID(value: number) {
|
||||
new ProvidersApi(DEFAULT_CONFIG).providersAllRetrieve({
|
||||
id: value,
|
||||
}).then((prov) => (this.provider = prov));
|
||||
new ProvidersApi(DEFAULT_CONFIG)
|
||||
.providersAllRetrieve({
|
||||
id: value,
|
||||
})
|
||||
.then((prov) => (this.provider = prov));
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
@ -31,13 +32,21 @@ export class ProviderViewPage extends LitElement {
|
||||
}
|
||||
switch (this.provider?.component) {
|
||||
case "ak-provider-saml-form":
|
||||
return html`<ak-provider-saml-view providerID=${ifDefined(this.provider.pk)}></ak-provider-saml-view>`;
|
||||
return html`<ak-provider-saml-view
|
||||
providerID=${ifDefined(this.provider.pk)}
|
||||
></ak-provider-saml-view>`;
|
||||
case "ak-provider-oauth2-form":
|
||||
return html`<ak-provider-oauth2-view providerID=${ifDefined(this.provider.pk)}></ak-provider-oauth2-view>`;
|
||||
return html`<ak-provider-oauth2-view
|
||||
providerID=${ifDefined(this.provider.pk)}
|
||||
></ak-provider-oauth2-view>`;
|
||||
case "ak-provider-proxy-form":
|
||||
return html`<ak-provider-proxy-view providerID=${ifDefined(this.provider.pk)}></ak-provider-proxy-view>`;
|
||||
return html`<ak-provider-proxy-view
|
||||
providerID=${ifDefined(this.provider.pk)}
|
||||
></ak-provider-proxy-view>`;
|
||||
case "ak-provider-ldap-form":
|
||||
return html`<ak-provider-ldap-view providerID=${ifDefined(this.provider.pk)}></ak-provider-ldap-view>`;
|
||||
return html`<ak-provider-ldap-view
|
||||
providerID=${ifDefined(this.provider.pk)}
|
||||
></ak-provider-ldap-view>`;
|
||||
default:
|
||||
return html`<p>Invalid provider type ${this.provider?.component}</p>`;
|
||||
}
|
||||
@ -45,10 +54,11 @@ export class ProviderViewPage extends LitElement {
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<ak-page-header
|
||||
icon="pf-icon pf-icon-integration"
|
||||
header=${ifDefined(this.provider?.name)}
|
||||
description=${ifDefined(this.provider?.verboseName)}>
|
||||
</ak-page-header>
|
||||
${this.renderProvider()}`;
|
||||
icon="pf-icon pf-icon-integration"
|
||||
header=${ifDefined(this.provider?.name)}
|
||||
description=${ifDefined(this.provider?.verboseName)}
|
||||
>
|
||||
</ak-page-header>
|
||||
${this.renderProvider()}`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,12 +10,11 @@ import "../../pages/applications/ApplicationForm";
|
||||
|
||||
@customElement("ak-provider-related-application")
|
||||
export class RelatedApplicationButton extends LitElement {
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFButton];
|
||||
}
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
provider?: Provider;
|
||||
|
||||
render(): TemplateResult {
|
||||
@ -25,18 +24,10 @@ export class RelatedApplicationButton extends LitElement {
|
||||
</a>`;
|
||||
}
|
||||
return html`<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Application`}
|
||||
</span>
|
||||
<ak-application-form slot="form" .provider=${this.provider?.pk}>
|
||||
</ak-application-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Create`}
|
||||
</button>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Application`} </span>
|
||||
<ak-application-form slot="form" .provider=${this.provider?.pk}> </ak-application-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${t`Create`}</button>
|
||||
</ak-forms-modal>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
import { FlowsApi, ProvidersApi, LDAPProvider, CoreApi, FlowsInstancesListDesignationEnum, CryptoApi } from "authentik-api";
|
||||
import {
|
||||
FlowsApi,
|
||||
ProvidersApi,
|
||||
LDAPProvider,
|
||||
CoreApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
CryptoApi,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -12,7 +19,6 @@ import { first } from "../../../utils";
|
||||
|
||||
@customElement("ak-provider-ldap-form")
|
||||
export class LDAPProviderFormPage extends ModelForm<LDAPProvider, number> {
|
||||
|
||||
loadInstance(pk: number): Promise<LDAPProvider> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersLdapRetrieve({
|
||||
id: pk,
|
||||
@ -31,109 +37,165 @@ export class LDAPProviderFormPage extends ModelForm<LDAPProvider, number> {
|
||||
if (this.instance) {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersLdapUpdate({
|
||||
id: this.instance.pk || 0,
|
||||
lDAPProviderRequest: data
|
||||
lDAPProviderRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersLdapCreate({
|
||||
lDAPProviderRequest: data
|
||||
lDAPProviderRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Bind flow`}
|
||||
?required=${true}
|
||||
name="authorizationFlow">
|
||||
name="authorizationFlow"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
${until(tenant().then(t => {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authentication,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
let selected = flow.pk === t.flowAuthentication;
|
||||
if (this.instance?.authorizationFlow === flow.pk) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
tenant().then((t) => {
|
||||
return new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authentication,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
let selected = flow.pk === t.flowAuthentication;
|
||||
if (this.instance?.authorizationFlow === flow.pk) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow used for users to authenticate. Currently only identification and password stages are supported.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow used for users to authenticate. Currently only identification and password stages are supported.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Group`}
|
||||
name="searchGroup">
|
||||
<ak-form-element-horizontal label=${t`Group`} name="searchGroup">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.searchGroup === undefined}>---------</option>
|
||||
${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => {
|
||||
return groups.results.map(group => {
|
||||
return html`<option value=${ifDefined(group.pk)} ?selected=${this.instance?.searchGroup === group.pk}>${group.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.searchGroup === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then((groups) => {
|
||||
return groups.results.map((group) => {
|
||||
return html`<option
|
||||
value=${ifDefined(group.pk)}
|
||||
?selected=${this.instance?.searchGroup === group.pk}
|
||||
>
|
||||
${group.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Users in the selected group can do search queries. If no group is selected, no LDAP Searches are allowed.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Users in the selected group can do search queries. If no group is selected, no LDAP Searches are allowed.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Protocol settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Protocol settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Base DN`}
|
||||
?required=${true}
|
||||
name="baseDn">
|
||||
<input type="text" value="${first(this.instance?.baseDn, "DC=ldap,DC=goauthentik,DC=io")}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`LDAP DN under which bind requests and search requests can be made.`}</p>
|
||||
<ak-form-element-horizontal label=${t`Base DN`} ?required=${true} name="baseDn">
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.baseDn, "DC=ldap,DC=goauthentik,DC=io")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`LDAP DN under which bind requests and search requests can be made.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`TLS Server name`}
|
||||
name="tlsServerName">
|
||||
<input type="text" value="${first(this.instance?.tlsServerName, "")}" class="pf-c-form-control">
|
||||
<p class="pf-c-form__helper-text">${t`Server name for which this provider's certificate is valid for.`}</p>
|
||||
<ak-form-element-horizontal label=${t`TLS Server name`} name="tlsServerName">
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.tlsServerName, "")}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Server name for which this provider's certificate is valid for.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Certificate`}
|
||||
name="certificate">
|
||||
<ak-form-element-horizontal label=${t`Certificate`} name="certificate">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.certificate === undefined}>---------</option>
|
||||
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
hasKey: true,
|
||||
}).then(keys => {
|
||||
return keys.results.map(key => {
|
||||
return html`<option value=${ifDefined(key.pk)} ?selected=${this.instance?.certificate === key.pk}>${key.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.certificate === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CryptoApi(DEFAULT_CONFIG)
|
||||
.cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
hasKey: true,
|
||||
})
|
||||
.then((keys) => {
|
||||
return keys.results.map((key) => {
|
||||
return html`<option
|
||||
value=${ifDefined(key.pk)}
|
||||
?selected=${this.instance?.certificate === key.pk}
|
||||
>
|
||||
${key.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`UID start number`}
|
||||
?required=${true}
|
||||
name="uidStartNumber">
|
||||
<input type="number" value="${first(this.instance?.uidStartNumber, 2000)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber`}</p>
|
||||
name="uidStartNumber"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.uidStartNumber, 2000)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`GID start number`}
|
||||
?required=${true}
|
||||
name="gidStartNumber">
|
||||
<input type="number" value="${first(this.instance?.gidStartNumber, 4000)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`The start for gidNumbers, this number is added to a number generated from the group.Pk to make sure that the numbers aren't too low for POSIX groups. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber`}</p>
|
||||
name="gidStartNumber"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.gidStartNumber, 4000)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`The start for gidNumbers, this number is added to a number generated from the group.Pk to make sure that the numbers aren't too low for POSIX groups. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -25,24 +25,37 @@ import { EVENT_REFRESH } from "../../../constants";
|
||||
|
||||
@customElement("ak-provider-ldap-view")
|
||||
export class LDAPProviderViewPage extends LitElement {
|
||||
|
||||
@property()
|
||||
set args(value: { [key: string]: number }) {
|
||||
this.providerID = value.id;
|
||||
}
|
||||
|
||||
@property({type: Number})
|
||||
@property({ type: Number })
|
||||
set providerID(value: number) {
|
||||
new ProvidersApi(DEFAULT_CONFIG).providersLdapRetrieve({
|
||||
id: value,
|
||||
}).then((prov) => (this.provider = prov));
|
||||
new ProvidersApi(DEFAULT_CONFIG)
|
||||
.providersLdapRetrieve({
|
||||
id: value,
|
||||
})
|
||||
.then((prov) => (this.provider = prov));
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
provider?: LDAPProvider;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFButton, PFPage, PFFlex, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, AKGlobal];
|
||||
return [
|
||||
PFBase,
|
||||
PFButton,
|
||||
PFPage,
|
||||
PFFlex,
|
||||
PFDisplay,
|
||||
PFGallery,
|
||||
PFContent,
|
||||
PFCard,
|
||||
PFDescriptionList,
|
||||
PFSizing,
|
||||
AKGlobal,
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -58,72 +71,90 @@ export class LDAPProviderViewPage extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
return html`<ak-tabs>
|
||||
<section slot="page-overview" data-tab-title="${t`Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list pf-m-3-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`Name`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.name}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Assigned to application`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-provider-related-application .provider=${this.provider}></ak-provider-related-application>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Base DN`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.baseDn}</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update LDAP Provider`}
|
||||
</span>
|
||||
<ak-provider-ldap-form
|
||||
slot="form"
|
||||
.instancePk=${this.provider.pk}>
|
||||
</ak-provider-ldap-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
<section
|
||||
slot="page-overview"
|
||||
data-tab-title="${t`Overview`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list pf-m-3-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`Name`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.name}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Assigned to application`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-provider-related-application
|
||||
.provider=${this.provider}
|
||||
></ak-provider-related-application>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Base DN`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.baseDn}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update LDAP Provider`} </span>
|
||||
<ak-provider-ldap-form
|
||||
slot="form"
|
||||
.instancePk=${this.provider.pk}
|
||||
>
|
||||
</ak-provider-ldap-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section slot="page-changelog" data-tab-title="${t`Changelog`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.provider.pk || ""}
|
||||
targetModelApp="authentik_providers_ldap"
|
||||
targetModelName="LDAPProvider">
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-changelog"
|
||||
data-tab-title="${t`Changelog`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.provider.pk || ""}
|
||||
targetModelApp="authentik_providers_ldap"
|
||||
targetModelName="LDAPProvider"
|
||||
>
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</section>
|
||||
</ak-tabs>`;
|
||||
</div>
|
||||
</section>
|
||||
</ak-tabs>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,15 @@
|
||||
import { CryptoApi, FlowsApi, OAuth2Provider, ClientTypeEnum, IssuerModeEnum, JwtAlgEnum, SubModeEnum, PropertymappingsApi, ProvidersApi, FlowsInstancesListDesignationEnum } from "authentik-api";
|
||||
import {
|
||||
CryptoApi,
|
||||
FlowsApi,
|
||||
OAuth2Provider,
|
||||
ClientTypeEnum,
|
||||
IssuerModeEnum,
|
||||
JwtAlgEnum,
|
||||
SubModeEnum,
|
||||
PropertymappingsApi,
|
||||
ProvidersApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -12,17 +23,18 @@ import { first, randomString } from "../../../utils";
|
||||
|
||||
@customElement("ak-provider-oauth2-form")
|
||||
export class OAuth2ProviderFormPage extends ModelForm<OAuth2Provider, number> {
|
||||
|
||||
loadInstance(pk: number): Promise<OAuth2Provider> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersOauth2Retrieve({
|
||||
id: pk,
|
||||
}).then(provider => {
|
||||
this.showClientSecret = provider.clientType === ClientTypeEnum.Confidential;
|
||||
return provider;
|
||||
});
|
||||
return new ProvidersApi(DEFAULT_CONFIG)
|
||||
.providersOauth2Retrieve({
|
||||
id: pk,
|
||||
})
|
||||
.then((provider) => {
|
||||
this.showClientSecret = provider.clientType === ClientTypeEnum.Confidential;
|
||||
return provider;
|
||||
});
|
||||
}
|
||||
|
||||
@property({type: Boolean})
|
||||
@property({ type: Boolean })
|
||||
showClientSecret = true;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
@ -37,82 +49,122 @@ export class OAuth2ProviderFormPage extends ModelForm<OAuth2Provider, number> {
|
||||
if (this.instance) {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersOauth2Update({
|
||||
id: this.instance.pk || 0,
|
||||
oAuth2ProviderRequest: data
|
||||
oAuth2ProviderRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersOauth2Create({
|
||||
oAuth2ProviderRequest: data
|
||||
oAuth2ProviderRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Authorization flow`}
|
||||
?required=${true}
|
||||
name="authorizationFlow">
|
||||
name="authorizationFlow"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authorization,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${this.instance?.authorizationFlow === flow.pk}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authorization,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${this.instance?.authorizationFlow === flow.pk}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow used when authorizing this provider.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow used when authorizing this provider.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Protocol settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Protocol settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Client type`}
|
||||
?required=${true}
|
||||
name="clientType">
|
||||
<select class="pf-c-form-control" @change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLSelectElement;
|
||||
if (target.selectedOptions[0].value === ClientTypeEnum.Public) {
|
||||
this.showClientSecret = false;
|
||||
} else {
|
||||
this.showClientSecret = true;
|
||||
}
|
||||
}}>
|
||||
<option value=${ClientTypeEnum.Confidential} ?selected=${this.instance?.clientType === ClientTypeEnum.Confidential}>
|
||||
name="clientType"
|
||||
>
|
||||
<select
|
||||
class="pf-c-form-control"
|
||||
@change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLSelectElement;
|
||||
if (target.selectedOptions[0].value === ClientTypeEnum.Public) {
|
||||
this.showClientSecret = false;
|
||||
} else {
|
||||
this.showClientSecret = true;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<option
|
||||
value=${ClientTypeEnum.Confidential}
|
||||
?selected=${this.instance?.clientType ===
|
||||
ClientTypeEnum.Confidential}
|
||||
>
|
||||
${t`Confidential`}
|
||||
</option>
|
||||
<option value=${ClientTypeEnum.Public} ?selected=${this.instance?.clientType === ClientTypeEnum.Public}>
|
||||
<option
|
||||
value=${ClientTypeEnum.Public}
|
||||
?selected=${this.instance?.clientType === ClientTypeEnum.Public}
|
||||
>
|
||||
${t`Public`}
|
||||
</option>
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Confidential clients are capable of maintaining the confidentiality of their credentials. Public clients are incapable.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Confidential clients are capable of maintaining the confidentiality of their credentials. Public clients are incapable.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Client ID`}
|
||||
?required=${true}
|
||||
name="clientId">
|
||||
<input type="text" value="${first(this.instance?.clientId, randomString(40))}" class="pf-c-form-control" required>
|
||||
name="clientId"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.clientId, randomString(40))}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
?hidden=${!this.showClientSecret}
|
||||
label=${t`Client Secret`}
|
||||
name="clientSecret">
|
||||
<input type="text" value="${first(this.instance?.clientSecret, randomString(128))}" class="pf-c-form-control">
|
||||
name="clientSecret"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.clientSecret, randomString(128))}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Redirect URIs/Origins`}
|
||||
name="redirectUris">
|
||||
<textarea class="pf-c-form-control">${this.instance?.redirectUris}</textarea>
|
||||
name="redirectUris"
|
||||
>
|
||||
<textarea class="pf-c-form-control">
|
||||
${this.instance?.redirectUris}</textarea
|
||||
>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Valid redirect URLs after a successful authorization flow. Also specify any origins here for Implicit flows.`}
|
||||
</p>
|
||||
@ -124,98 +176,167 @@ export class OAuth2ProviderFormPage extends ModelForm<OAuth2Provider, number> {
|
||||
</ak-form-group>
|
||||
|
||||
<ak-form-group>
|
||||
<span slot="header">
|
||||
${t`Advanced protocol settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Advanced protocol settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Access code validity`}
|
||||
?required=${true}
|
||||
name="accessCodeValidity">
|
||||
<input type="text" value="${first(this.instance?.accessCodeValidity, "minutes=1")}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Configure how long access codes are valid for.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`(Format: hours=-1;minutes=-2;seconds=-3).`}</p>
|
||||
name="accessCodeValidity"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.accessCodeValidity, "minutes=1")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Configure how long access codes are valid for.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`(Format: hours=-1;minutes=-2;seconds=-3).`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Token validity`}
|
||||
?required=${true}
|
||||
name="tokenValidity">
|
||||
<input type="text" value="${first(this.instance?.tokenValidity, "minutes=10")}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Configure how long refresh tokens and their id_tokens are valid for.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`(Format: hours=-1;minutes=-2;seconds=-3).`}</p>
|
||||
name="tokenValidity"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.tokenValidity, "minutes=10")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Configure how long refresh tokens and their id_tokens are valid for.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`(Format: hours=-1;minutes=-2;seconds=-3).`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`JWT Algorithm`}
|
||||
?required=${true}
|
||||
name="jwtAlg">
|
||||
name="jwtAlg"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${JwtAlgEnum.Rs256} ?selected=${this.instance?.jwtAlg === JwtAlgEnum.Rs256}>
|
||||
<option
|
||||
value=${JwtAlgEnum.Rs256}
|
||||
?selected=${this.instance?.jwtAlg === JwtAlgEnum.Rs256}
|
||||
>
|
||||
${t`RS256 (Asymmetric Encryption)`}
|
||||
</option>
|
||||
<option value=${JwtAlgEnum.Hs256} ?selected=${this.instance?.jwtAlg === JwtAlgEnum.Hs256}>
|
||||
<option
|
||||
value=${JwtAlgEnum.Hs256}
|
||||
?selected=${this.instance?.jwtAlg === JwtAlgEnum.Hs256}
|
||||
>
|
||||
${t`HS256 (Symmetric Encryption)`}
|
||||
</option>
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Algorithm used to sign the JWT Tokens.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Algorithm used to sign the JWT Tokens.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Scopes`}
|
||||
name="propertyMappings">
|
||||
<ak-form-element-horizontal label=${t`Scopes`} name="propertyMappings">
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScopeList({
|
||||
ordering: "scope_name"
|
||||
}).then(scopes => {
|
||||
return scopes.results.map(scope => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappings) {
|
||||
selected = scope.managed?.startsWith("goauthentik.io/providers/oauth2/scope-") || false;
|
||||
} else {
|
||||
selected = Array.from(this.instance?.propertyMappings).some(su => {
|
||||
return su == scope.pk;
|
||||
${until(
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsScopeList({
|
||||
ordering: "scope_name",
|
||||
})
|
||||
.then((scopes) => {
|
||||
return scopes.results.map((scope) => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappings) {
|
||||
selected =
|
||||
scope.managed?.startsWith(
|
||||
"goauthentik.io/providers/oauth2/scope-",
|
||||
) || false;
|
||||
} else {
|
||||
selected = Array.from(
|
||||
this.instance?.propertyMappings,
|
||||
).some((su) => {
|
||||
return su == scope.pk;
|
||||
});
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(scope.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${scope.name}
|
||||
</option>`;
|
||||
});
|
||||
}
|
||||
return html`<option value=${ifDefined(scope.pk)} ?selected=${selected}>${scope.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Select which scopes can be used by the client. The client stil has to specify the scope to access the data.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Select which scopes can be used by the client. The client stil has to specify the scope to access the data.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Hold control/command to select multiple items.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`RSA Key`}
|
||||
name="rsaKey">
|
||||
<ak-form-element-horizontal label=${t`RSA Key`} name="rsaKey">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.rsaKey === undefined}>---------</option>
|
||||
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
hasKey: true,
|
||||
}).then(keys => {
|
||||
return keys.results.map(key => {
|
||||
let selected = this.instance?.rsaKey === key.pk;
|
||||
if (keys.results.length === 1) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(key.pk)} ?selected=${selected}>${key.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.rsaKey === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CryptoApi(DEFAULT_CONFIG)
|
||||
.cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
hasKey: true,
|
||||
})
|
||||
.then((keys) => {
|
||||
return keys.results.map((key) => {
|
||||
let selected = this.instance?.rsaKey === key.pk;
|
||||
if (keys.results.length === 1) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(key.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${key.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Key used to sign the tokens. Only required when JWT Algorithm is set to RS256.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Key used to sign the tokens. Only required when JWT Algorithm is set to RS256.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Subject mode`}
|
||||
?required=${true}
|
||||
name="subMode">
|
||||
name="subMode"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value="${SubModeEnum.HashedUserId}" ?selected=${this.instance?.subMode === SubModeEnum.HashedUserId}>
|
||||
<option
|
||||
value="${SubModeEnum.HashedUserId}"
|
||||
?selected=${this.instance?.subMode === SubModeEnum.HashedUserId}
|
||||
>
|
||||
${t`Based on the Hashed User ID`}
|
||||
</option>
|
||||
<option value="${SubModeEnum.UserUsername}" ?selected=${this.instance?.subMode === SubModeEnum.UserUsername}>
|
||||
<option
|
||||
value="${SubModeEnum.UserUsername}"
|
||||
?selected=${this.instance?.subMode === SubModeEnum.UserUsername}
|
||||
>
|
||||
${t`Based on the username`}
|
||||
</option>
|
||||
<option value="${SubModeEnum.UserEmail}" ?selected=${this.instance?.subMode === SubModeEnum.UserEmail}>
|
||||
<option
|
||||
value="${SubModeEnum.UserEmail}"
|
||||
?selected=${this.instance?.subMode === SubModeEnum.UserEmail}
|
||||
>
|
||||
${t`Based on the User's Email. This is recommended over the UPN method.`}
|
||||
</option>
|
||||
<option value="${SubModeEnum.UserUpn}" ?selected=${this.instance?.subMode === SubModeEnum.UserUpn}>
|
||||
<option
|
||||
value="${SubModeEnum.UserUpn}"
|
||||
?selected=${this.instance?.subMode === SubModeEnum.UserUpn}
|
||||
>
|
||||
${t`Based on the User's UPN, only works if user has a 'upn' attribute set. Use this method only if you have different UPN and Mail domains.`}
|
||||
</option>
|
||||
</select>
|
||||
@ -225,22 +346,36 @@ export class OAuth2ProviderFormPage extends ModelForm<OAuth2Provider, number> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="includeClaimsInIdToken">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.includeClaimsInIdToken, true)}>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.includeClaimsInIdToken, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Include claims in id_token`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Include User claims from scopes in the id_token, for applications that don't access the userinfo endpoint.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Include User claims from scopes in the id_token, for applications that don't access the userinfo endpoint.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Issuer mode`}
|
||||
?required=${true}
|
||||
name="issuerMode">
|
||||
name="issuerMode"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value="${IssuerModeEnum.PerProvider}" ?selected=${this.instance?.issuerMode === IssuerModeEnum.PerProvider}>
|
||||
<option
|
||||
value="${IssuerModeEnum.PerProvider}"
|
||||
?selected=${this.instance?.issuerMode ===
|
||||
IssuerModeEnum.PerProvider}
|
||||
>
|
||||
${t`Each provider has a different issuer, based on the application slug.`}
|
||||
</option>
|
||||
<option value="${IssuerModeEnum.Global}" ?selected=${this.instance?.issuerMode === IssuerModeEnum.Global}>
|
||||
<option
|
||||
value="${IssuerModeEnum.Global}"
|
||||
?selected=${this.instance?.issuerMode === IssuerModeEnum.Global}
|
||||
>
|
||||
${t`Same identifier is used for all providers`}
|
||||
</option>
|
||||
</select>
|
||||
@ -252,5 +387,4 @@ export class OAuth2ProviderFormPage extends ModelForm<OAuth2Provider, number> {
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -28,17 +28,16 @@ import { EVENT_REFRESH } from "../../../constants";
|
||||
|
||||
@customElement("ak-provider-oauth2-view")
|
||||
export class OAuth2ProviderViewPage extends LitElement {
|
||||
|
||||
@property({type: Number})
|
||||
@property({ type: Number })
|
||||
set providerID(value: number) {
|
||||
const api = new ProvidersApi(DEFAULT_CONFIG);
|
||||
api.providersOauth2Retrieve({
|
||||
id: value
|
||||
id: value,
|
||||
}).then((prov) => {
|
||||
this.provider = prov;
|
||||
});
|
||||
api.providersOauth2SetupUrlsRetrieve({
|
||||
id: value
|
||||
id: value,
|
||||
}).then((prov) => {
|
||||
this.providerUrls = prov;
|
||||
});
|
||||
@ -51,7 +50,21 @@ export class OAuth2ProviderViewPage extends LitElement {
|
||||
providerUrls?: OAuth2ProviderSetupURLs;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFButton, PFPage, PFFlex, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, PFForm, PFFormControl, AKGlobal];
|
||||
return [
|
||||
PFBase,
|
||||
PFButton,
|
||||
PFPage,
|
||||
PFFlex,
|
||||
PFDisplay,
|
||||
PFGallery,
|
||||
PFContent,
|
||||
PFCard,
|
||||
PFDescriptionList,
|
||||
PFSizing,
|
||||
PFForm,
|
||||
PFFormControl,
|
||||
AKGlobal,
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -67,137 +80,227 @@ export class OAuth2ProviderViewPage extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
return html`<ak-tabs>
|
||||
<section slot="page-overview" data-tab-title="${t`Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<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`Name`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.name}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Assigned to application`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-provider-related-application .provider=${this.provider}></ak-provider-related-application>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Client type`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${convertToTitle(this.provider.clientType || "")}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Client ID`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.clientId}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Redirect URIs`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.redirectUris}</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update OAuth2 Provider`}
|
||||
</span>
|
||||
<ak-provider-oauth2-form
|
||||
slot="form"
|
||||
.instancePk=${this.provider.pk || 0}>
|
||||
</ak-provider-oauth2-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
<section
|
||||
slot="page-overview"
|
||||
data-tab-title="${t`Overview`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<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`Name`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.name}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Assigned to application`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-provider-related-application
|
||||
.provider=${this.provider}
|
||||
></ak-provider-related-application>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Client type`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${convertToTitle(this.provider.clientType || "")}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Client ID`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.clientId}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Redirect URIs`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.redirectUris}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update OAuth2 Provider`} </span>
|
||||
<ak-provider-oauth2-form
|
||||
slot="form"
|
||||
.instancePk=${this.provider.pk || 0}
|
||||
>
|
||||
</ak-provider-oauth2-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section slot="page-changelog" data-tab-title="${t`Changelog`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.provider.pk || ""}
|
||||
targetModelApp="authentik_providers_oauth2"
|
||||
targetModelName="oauth2provider">
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-changelog"
|
||||
data-tab-title="${t`Changelog`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.provider.pk || ""}
|
||||
targetModelApp="authentik_providers_oauth2"
|
||||
targetModelName="oauth2provider"
|
||||
>
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</section>
|
||||
<section slot="page-metadata" data-tab-title="${t`Metadata`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<form class="pf-c-form">
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">${t`OpenID Configuration URL`}</span>
|
||||
</label>
|
||||
<input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.providerInfo || "-"}" />
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">${t`OpenID Configuration Issuer`}</span>
|
||||
</label>
|
||||
<input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.issuer || "-"}" />
|
||||
</div>
|
||||
<hr>
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">${t`Authorize URL`}</span>
|
||||
</label>
|
||||
<input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.authorize || "-"}" />
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">${t`Token URL`}</span>
|
||||
</label>
|
||||
<input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.token || "-"}" />
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">${t`Userinfo URL`}</span>
|
||||
</label>
|
||||
<input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.userInfo || "-"}" />
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">${t`Logout URL`}</span>
|
||||
</label>
|
||||
<input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.logout || "-"}" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-metadata"
|
||||
data-tab-title="${t`Metadata`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<form class="pf-c-form">
|
||||
<div class="pf-c-form__group">
|
||||
<label
|
||||
class="pf-c-form__label"
|
||||
for="help-text-simple-form-name"
|
||||
>
|
||||
<span class="pf-c-form__label-text"
|
||||
>${t`OpenID Configuration URL`}</span
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
type="text"
|
||||
value="${this.providerUrls?.providerInfo || "-"}"
|
||||
/>
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<label
|
||||
class="pf-c-form__label"
|
||||
for="help-text-simple-form-name"
|
||||
>
|
||||
<span class="pf-c-form__label-text"
|
||||
>${t`OpenID Configuration Issuer`}</span
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
type="text"
|
||||
value="${this.providerUrls?.issuer || "-"}"
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="pf-c-form__group">
|
||||
<label
|
||||
class="pf-c-form__label"
|
||||
for="help-text-simple-form-name"
|
||||
>
|
||||
<span class="pf-c-form__label-text"
|
||||
>${t`Authorize URL`}</span
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
type="text"
|
||||
value="${this.providerUrls?.authorize || "-"}"
|
||||
/>
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<label
|
||||
class="pf-c-form__label"
|
||||
for="help-text-simple-form-name"
|
||||
>
|
||||
<span class="pf-c-form__label-text"
|
||||
>${t`Token URL`}</span
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
type="text"
|
||||
value="${this.providerUrls?.token || "-"}"
|
||||
/>
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<label
|
||||
class="pf-c-form__label"
|
||||
for="help-text-simple-form-name"
|
||||
>
|
||||
<span class="pf-c-form__label-text"
|
||||
>${t`Userinfo URL`}</span
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
type="text"
|
||||
value="${this.providerUrls?.userInfo || "-"}"
|
||||
/>
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<label
|
||||
class="pf-c-form__label"
|
||||
for="help-text-simple-form-name"
|
||||
>
|
||||
<span class="pf-c-form__label-text"
|
||||
>${t`Logout URL`}</span
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
type="text"
|
||||
value="${this.providerUrls?.logout || "-"}"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</ak-tabs>`;
|
||||
</div>
|
||||
</section>
|
||||
</ak-tabs>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
import { CryptoApi, FlowsApi, FlowsInstancesListDesignationEnum, ProvidersApi, ProxyMode, ProxyProvider } from "authentik-api";
|
||||
import {
|
||||
CryptoApi,
|
||||
FlowsApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
ProvidersApi,
|
||||
ProxyMode,
|
||||
ProxyProvider,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { css, CSSResult, customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -15,29 +22,35 @@ import { first } from "../../../utils";
|
||||
|
||||
@customElement("ak-provider-proxy-form")
|
||||
export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(PFToggleGroup, PFContent, PFSpacing, css`
|
||||
.pf-c-toggle-group {
|
||||
justify-content: center;
|
||||
}
|
||||
`);
|
||||
return super.styles.concat(
|
||||
PFToggleGroup,
|
||||
PFContent,
|
||||
PFSpacing,
|
||||
css`
|
||||
.pf-c-toggle-group {
|
||||
justify-content: center;
|
||||
}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
loadInstance(pk: number): Promise<ProxyProvider> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersProxyRetrieve({
|
||||
id: pk,
|
||||
}).then(provider => {
|
||||
this.showHttpBasic = first(provider.basicAuthEnabled, true);
|
||||
this.mode = first(provider.mode, ProxyMode.Proxy);
|
||||
return provider;
|
||||
});
|
||||
return new ProvidersApi(DEFAULT_CONFIG)
|
||||
.providersProxyRetrieve({
|
||||
id: pk,
|
||||
})
|
||||
.then((provider) => {
|
||||
this.showHttpBasic = first(provider.basicAuthEnabled, true);
|
||||
this.mode = first(provider.mode, ProxyMode.Proxy);
|
||||
return provider;
|
||||
});
|
||||
}
|
||||
|
||||
@property({type: Boolean})
|
||||
@property({ type: Boolean })
|
||||
showHttpBasic = true;
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
mode: ProxyMode = ProxyMode.Proxy;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
@ -53,11 +66,11 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
|
||||
if (this.instance) {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersProxyUpdate({
|
||||
id: this.instance.pk || 0,
|
||||
proxyProviderRequest: data
|
||||
proxyProviderRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersProxyCreate({
|
||||
proxyProviderRequest: data
|
||||
proxyProviderRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -68,40 +81,73 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
|
||||
}
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${t`HTTP-Basic Username Key`}
|
||||
name="basicAuthUserAttribute">
|
||||
<input type="text" value="${ifDefined(this.instance?.basicAuthUserAttribute)}" class="pf-c-form-control">
|
||||
<p class="pf-c-form__helper-text">${t`User/Group Attribute used for the user part of the HTTP-Basic Header. If not set, the user's Email address is used.`}</p>
|
||||
name="basicAuthUserAttribute"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.basicAuthUserAttribute)}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`User/Group Attribute used for the user part of the HTTP-Basic Header. If not set, the user's Email address is used.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`HTTP-Basic Password Key`}
|
||||
name="basicAuthPasswordAttribute">
|
||||
<input type="text" value="${ifDefined(this.instance?.basicAuthPasswordAttribute)}" class="pf-c-form-control">
|
||||
<p class="pf-c-form__helper-text">${t`User/Group Attribute used for the password part of the HTTP-Basic Header.`}</p>
|
||||
name="basicAuthPasswordAttribute"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.basicAuthPasswordAttribute)}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`User/Group Attribute used for the password part of the HTTP-Basic Header.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
|
||||
renderModeSelector(): TemplateResult {
|
||||
return html`
|
||||
<div class="pf-c-toggle-group__item">
|
||||
<button class="pf-c-toggle-group__button ${this.mode === ProxyMode.Proxy ? "pf-m-selected" : ""}" type="button" @click=${() => {
|
||||
this.mode = ProxyMode.Proxy;
|
||||
}}>
|
||||
return html` <div class="pf-c-toggle-group__item">
|
||||
<button
|
||||
class="pf-c-toggle-group__button ${this.mode === ProxyMode.Proxy
|
||||
? "pf-m-selected"
|
||||
: ""}"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.mode = ProxyMode.Proxy;
|
||||
}}
|
||||
>
|
||||
<span class="pf-c-toggle-group__text">${t`Proxy`}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pf-c-divider pf-m-vertical" role="separator"></div>
|
||||
<div class="pf-c-toggle-group__item">
|
||||
<button class="pf-c-toggle-group__button ${this.mode === ProxyMode.ForwardSingle ? "pf-m-selected" : ""}" type="button" @click=${() => {
|
||||
this.mode = ProxyMode.ForwardSingle;
|
||||
}}>
|
||||
<span class="pf-c-toggle-group__text">${t`Forward auth (single application)`}</span>
|
||||
<button
|
||||
class="pf-c-toggle-group__button ${this.mode === ProxyMode.ForwardSingle
|
||||
? "pf-m-selected"
|
||||
: ""}"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.mode = ProxyMode.ForwardSingle;
|
||||
}}
|
||||
>
|
||||
<span class="pf-c-toggle-group__text"
|
||||
>${t`Forward auth (single application)`}</span
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pf-c-divider pf-m-vertical" role="separator"></div>
|
||||
<div class="pf-c-toggle-group__item">
|
||||
<button class="pf-c-toggle-group__button ${this.mode === ProxyMode.ForwardDomain ? "pf-m-selected" : ""}" type="button" @click=${() => {
|
||||
this.mode = ProxyMode.ForwardDomain;
|
||||
}}>
|
||||
<button
|
||||
class="pf-c-toggle-group__button ${this.mode === ProxyMode.ForwardDomain
|
||||
? "pf-m-selected"
|
||||
: ""}"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.mode = ProxyMode.ForwardDomain;
|
||||
}}
|
||||
>
|
||||
<span class="pf-c-toggle-group__text">${t`Forward auth (domain level)`}</span>
|
||||
</button>
|
||||
</div>`;
|
||||
@ -110,146 +156,213 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
|
||||
renderSettings(): TemplateResult {
|
||||
switch (this.mode) {
|
||||
case ProxyMode.Proxy:
|
||||
return html`
|
||||
<p class="pf-u-mb-xl">
|
||||
return html` <p class="pf-u-mb-xl">
|
||||
${t`This provider will behave like a transparent reverse-proxy, except requests must be authenticated. If your upstream application uses HTTPS, make sure to connect to the outpost using HTTPS as well.`}
|
||||
</p>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`External host`}
|
||||
?required=${true}
|
||||
name="externalHost">
|
||||
<input type="text" value="${ifDefined(this.instance?.externalHost)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`The external URL you'll access the application at. Include any non-standard port.`}</p>
|
||||
name="externalHost"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.externalHost)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`The external URL you'll access the application at. Include any non-standard port.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Internal host`}
|
||||
?required=${true}
|
||||
name="internalHost">
|
||||
<input type="text" value="${ifDefined(this.instance?.internalHost)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Upstream host that the requests are forwarded to.`}</p>
|
||||
name="internalHost"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.internalHost)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Upstream host that the requests are forwarded to.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="internalHostSslValidation">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.internalHostSslValidation, true)}>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.internalHostSslValidation, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Internal host SSL Validation`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Validate SSL Certificates of upstream servers.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Validate SSL Certificates of upstream servers.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
case ProxyMode.ForwardSingle:
|
||||
return html`
|
||||
<p class="pf-u-mb-xl">
|
||||
return html` <p class="pf-u-mb-xl">
|
||||
${t`Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /akprox must be routed to the outpost (when using a manged outpost, this is done for you).`}
|
||||
</p>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`External host`}
|
||||
?required=${true}
|
||||
name="externalHost">
|
||||
<input type="text" value="${ifDefined(this.instance?.externalHost)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`The external URL you'll access the application at. Include any non-standard port.`}</p>
|
||||
name="externalHost"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.externalHost)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`The external URL you'll access the application at. Include any non-standard port.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
case ProxyMode.ForwardDomain:
|
||||
return html`
|
||||
<p class="pf-u-mb-xl">
|
||||
return html` <p class="pf-u-mb-xl">
|
||||
${t`Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.`}
|
||||
</p>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`External host`}
|
||||
?required=${true}
|
||||
name="externalHost">
|
||||
<input type="text" value="${first(this.instance?.externalHost, window.location.origin)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`The external URL you'll authenticate at. Can be the same domain as authentik.`}</p>
|
||||
name="externalHost"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.externalHost, window.location.origin)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`The external URL you'll authenticate at. Can be the same domain as authentik.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Cookie domain`}
|
||||
name="cookieDomain">
|
||||
<input type="text" value="${ifDefined(this.instance?.cookieDomain)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'.`}</p>
|
||||
<ak-form-element-horizontal label=${t`Cookie domain`} name="cookieDomain">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.cookieDomain)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Authorization flow`}
|
||||
?required=${true}
|
||||
name="authorizationFlow">
|
||||
name="authorizationFlow"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authorization,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${this.instance?.authorizationFlow === flow.pk}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authorization,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${this.instance?.authorizationFlow === flow.pk}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow used when authorizing this provider.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow used when authorizing this provider.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<div class="pf-c-card pf-m-selectable pf-m-selected">
|
||||
<div class="pf-c-card__body">
|
||||
<div class="pf-c-toggle-group">
|
||||
${this.renderModeSelector()}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
${this.renderSettings()}
|
||||
<div class="pf-c-toggle-group">${this.renderModeSelector()}</div>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">${this.renderSettings()}</div>
|
||||
</div>
|
||||
|
||||
<ak-form-group>
|
||||
<span slot="header">
|
||||
${t`Advanced protocol settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Advanced protocol settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Certificate`}
|
||||
name="certificate">
|
||||
<ak-form-element-horizontal label=${t`Certificate`} name="certificate">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.certificate === undefined}>---------</option>
|
||||
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
hasKey: true,
|
||||
}).then(keys => {
|
||||
return keys.results.map(key => {
|
||||
return html`<option value=${ifDefined(key.pk)} ?selected=${this.instance?.certificate === key.pk}>${key.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.certificate === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CryptoApi(DEFAULT_CONFIG)
|
||||
.cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
hasKey: true,
|
||||
})
|
||||
.then((keys) => {
|
||||
return keys.results.map((key) => {
|
||||
return html`<option
|
||||
value=${ifDefined(key.pk)}
|
||||
?selected=${this.instance?.certificate === key.pk}
|
||||
>
|
||||
${key.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Skip path regex`}
|
||||
name="skipPathRegex">
|
||||
<textarea class="pf-c-form-control">${this.instance?.skipPathRegex}</textarea>
|
||||
<p class="pf-c-form__helper-text">${t`Regular expressions for which authentication is not required. Each new line is interpreted as a new Regular Expression.`}</p>
|
||||
<ak-form-element-horizontal label=${t`Skip path regex`} name="skipPathRegex">
|
||||
<textarea class="pf-c-form-control">
|
||||
${this.instance?.skipPathRegex}</textarea
|
||||
>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Regular expressions for which authentication is not required. Each new line is interpreted as a new Regular Expression.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-element-horizontal name="basicAuthEnabled">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.basicAuthEnabled, false)} @change=${(ev: Event) => {
|
||||
const el = ev.target as HTMLInputElement;
|
||||
this.showHttpBasic = el.checked;
|
||||
}}>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.basicAuthEnabled, false)}
|
||||
@change=${(ev: Event) => {
|
||||
const el = ev.target as HTMLInputElement;
|
||||
this.showHttpBasic = el.checked;
|
||||
}}
|
||||
/>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Set HTTP-Basic Authentication`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Set a custom HTTP-Basic Authentication header based on values from authentik.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Set a custom HTTP-Basic Authentication header based on values from authentik.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
${this.renderHttpBasic()}
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -24,24 +24,35 @@ import { convertToTitle } from "../../../utils";
|
||||
|
||||
@customElement("ak-provider-proxy-view")
|
||||
export class ProxyProviderViewPage extends LitElement {
|
||||
|
||||
@property()
|
||||
set args(value: { [key: string]: number }) {
|
||||
this.providerID = value.id;
|
||||
}
|
||||
|
||||
@property({type: Number})
|
||||
@property({ type: Number })
|
||||
set providerID(value: number) {
|
||||
new ProvidersApi(DEFAULT_CONFIG).providersProxyRetrieve({
|
||||
id: value,
|
||||
}).then((prov) => (this.provider = prov));
|
||||
new ProvidersApi(DEFAULT_CONFIG)
|
||||
.providersProxyRetrieve({
|
||||
id: value,
|
||||
})
|
||||
.then((prov) => (this.provider = prov));
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
provider?: ProxyProvider;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFButton, PFPage, PFGrid, PFGallery, PFContent, PFCard, PFDescriptionList, AKGlobal];
|
||||
return [
|
||||
PFBase,
|
||||
PFButton,
|
||||
PFPage,
|
||||
PFGrid,
|
||||
PFGallery,
|
||||
PFContent,
|
||||
PFCard,
|
||||
PFDescriptionList,
|
||||
AKGlobal,
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -57,128 +68,165 @@ export class ProxyProviderViewPage extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
return html`<ak-tabs>
|
||||
<section slot="page-overview" data-tab-title="${t`Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-l-grid pf-m-gutter">
|
||||
<div class="pf-l-grid__item pf-m-6-col">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list pf-m-3-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`Name`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.name}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Assigned to application`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-provider-related-application .provider=${this.provider}></ak-provider-related-application>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Internal Host`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.internalHost}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`External Host`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.externalHost}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Basic-Auth`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.basicAuthEnabled ?
|
||||
html`<span class="pf-c-button__icon pf-m-start">
|
||||
<i class="fas fa-check-circle" aria-hidden="true"></i>
|
||||
</span>${t`Yes`}`:
|
||||
html`<span class="pf-c-button__icon pf-m-start">
|
||||
<i class="fas fa-times-circle" aria-hidden="true"></i>
|
||||
</span>${t`No`}`
|
||||
}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Mode`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${convertToTitle(this.provider.mode || "")}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Proxy Provider`}
|
||||
</span>
|
||||
<ak-provider-proxy-form
|
||||
slot="form"
|
||||
.instancePk=${this.provider.pk || 0}>
|
||||
</ak-provider-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
<section
|
||||
slot="page-overview"
|
||||
data-tab-title="${t`Overview`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-l-grid pf-m-gutter">
|
||||
<div class="pf-l-grid__item pf-m-6-col">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list pf-m-3-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`Name`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.name}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Assigned to application`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-provider-related-application
|
||||
.provider=${this.provider}
|
||||
></ak-provider-related-application>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Internal Host`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.internalHost}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`External Host`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.externalHost}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Basic-Auth`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.basicAuthEnabled
|
||||
? html`<span
|
||||
class="pf-c-button__icon pf-m-start"
|
||||
>
|
||||
<i
|
||||
class="fas fa-check-circle"
|
||||
aria-hidden="true"
|
||||
></i
|
||||
> </span
|
||||
>${t`Yes`}`
|
||||
: html`<span
|
||||
class="pf-c-button__icon pf-m-start"
|
||||
>
|
||||
<i
|
||||
class="fas fa-times-circle"
|
||||
aria-hidden="true"
|
||||
></i
|
||||
> </span
|
||||
>${t`No`}`}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Mode`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${convertToTitle(this.provider.mode || "")}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">
|
||||
${t`Protocol Settings`}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list pf-m-3-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`Allowed Redirect URIs`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.redirectUris}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Proxy Provider`} </span>
|
||||
<ak-provider-proxy-form
|
||||
slot="form"
|
||||
.instancePk=${this.provider.pk || 0}
|
||||
>
|
||||
</ak-provider-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section slot="page-changelog" data-tab-title="${t`Changelog`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.provider.pk || ""}
|
||||
targetModelApp="authentik_providers_proxy"
|
||||
targetModelName="proxyprovider">
|
||||
</ak-object-changelog>
|
||||
<div class="pf-l-grid__item pf-m-6-col">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`Protocol Settings`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list pf-m-3-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`Allowed Redirect URIs`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.redirectUris}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</ak-tabs>`;
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-changelog"
|
||||
data-tab-title="${t`Changelog`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.provider.pk || ""}
|
||||
targetModelApp="authentik_providers_proxy"
|
||||
targetModelName="proxyprovider"
|
||||
>
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</ak-tabs>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,14 @@
|
||||
import { CryptoApi, FlowsApi, SAMLProvider, ProvidersApi, PropertymappingsApi, SpBindingEnum, DigestAlgorithmEnum, SignatureAlgorithmEnum, FlowsInstancesListDesignationEnum } from "authentik-api";
|
||||
import {
|
||||
CryptoApi,
|
||||
FlowsApi,
|
||||
SAMLProvider,
|
||||
ProvidersApi,
|
||||
PropertymappingsApi,
|
||||
SpBindingEnum,
|
||||
DigestAlgorithmEnum,
|
||||
SignatureAlgorithmEnum,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -11,7 +21,6 @@ import "../../../elements/forms/FormGroup";
|
||||
|
||||
@customElement("ak-provider-saml-form")
|
||||
export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
|
||||
|
||||
loadInstance(pk: number): Promise<SAMLProvider> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersSamlRetrieve({
|
||||
id: pk,
|
||||
@ -30,193 +39,331 @@ export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
|
||||
if (this.instance) {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersSamlUpdate({
|
||||
id: this.instance.pk || 0,
|
||||
sAMLProviderRequest: data
|
||||
sAMLProviderRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersSamlCreate({
|
||||
sAMLProviderRequest: data
|
||||
sAMLProviderRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Authorization flow`}
|
||||
?required=${true}
|
||||
name="authorizationFlow">
|
||||
name="authorizationFlow"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authorization,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${this.instance?.authorizationFlow === flow.pk}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authorization,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${this.instance?.authorizationFlow === flow.pk}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow used when authorizing this provider.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow used when authorizing this provider.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Protocol settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Protocol settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`ACS URL`}
|
||||
?required=${true}
|
||||
name="acsUrl">
|
||||
<input type="text" value="${ifDefined(this.instance?.acsUrl)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`ACS URL`} ?required=${true} name="acsUrl">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.acsUrl)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Issuer`}
|
||||
?required=${true}
|
||||
name="issuer">
|
||||
<input type="text" value="${this.instance?.issuer || "authentik"}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Issuer`} ?required=${true} name="issuer">
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.issuer || "authentik"}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Service Provider Binding`}
|
||||
?required=${true}
|
||||
name="spBinding">
|
||||
name="spBinding"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${SpBindingEnum.Redirect} ?selected=${this.instance?.spBinding === SpBindingEnum.Redirect}>
|
||||
<option
|
||||
value=${SpBindingEnum.Redirect}
|
||||
?selected=${this.instance?.spBinding === SpBindingEnum.Redirect}
|
||||
>
|
||||
${t`Redirect`}
|
||||
</option>
|
||||
<option value=${SpBindingEnum.Post} ?selected=${this.instance?.spBinding === SpBindingEnum.Post}>
|
||||
<option
|
||||
value=${SpBindingEnum.Post}
|
||||
?selected=${this.instance?.spBinding === SpBindingEnum.Post}
|
||||
>
|
||||
${t`Post`}
|
||||
</option>
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Determines how authentik sends the response back to the Service Provider.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Determines how authentik sends the response back to the Service Provider.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Audience`}
|
||||
name="audience">
|
||||
<input type="text" value="${ifDefined(this.instance?.audience)}" class="pf-c-form-control">
|
||||
<ak-form-element-horizontal label=${t`Audience`} name="audience">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.audience)}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
|
||||
<ak-form-group>
|
||||
<span slot="header">
|
||||
${t`Advanced protocol settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Advanced protocol settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Signing Certificate`}
|
||||
name="signingKp">
|
||||
<ak-form-element-horizontal label=${t`Signing Certificate`} name="signingKp">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.signingKp === undefined}>---------</option>
|
||||
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
hasKey: true,
|
||||
}).then(keys => {
|
||||
return keys.results.map(key => {
|
||||
return html`<option value=${ifDefined(key.pk)} ?selected=${this.instance?.signingKp === key.pk}>${key.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.signingKp === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CryptoApi(DEFAULT_CONFIG)
|
||||
.cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
hasKey: true,
|
||||
})
|
||||
.then((keys) => {
|
||||
return keys.results.map((key) => {
|
||||
return html`<option
|
||||
value=${ifDefined(key.pk)}
|
||||
?selected=${this.instance?.signingKp === key.pk}
|
||||
>
|
||||
${key.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Certificate used to sign outgoing Responses going to the Service Provider.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Certificate used to sign outgoing Responses going to the Service Provider.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Verification Certificate`}
|
||||
name="verificationKp">
|
||||
name="verificationKp"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.verificationKp === undefined}>---------</option>
|
||||
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
}).then(keys => {
|
||||
return keys.results.map(key => {
|
||||
return html`<option value=${ifDefined(key.pk)} ?selected=${this.instance?.verificationKp === key.pk}>${key.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option
|
||||
value=""
|
||||
?selected=${this.instance?.verificationKp === undefined}
|
||||
>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CryptoApi(DEFAULT_CONFIG)
|
||||
.cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((keys) => {
|
||||
return keys.results.map((key) => {
|
||||
return html`<option
|
||||
value=${ifDefined(key.pk)}
|
||||
?selected=${this.instance?.verificationKp ===
|
||||
key.pk}
|
||||
>
|
||||
${key.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Property mappings`}
|
||||
?required=${true}
|
||||
name="propertyMappings">
|
||||
name="propertyMappings"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlList({
|
||||
ordering: "saml_name"
|
||||
}).then(mappings => {
|
||||
return mappings.results.map(mapping => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappings) {
|
||||
selected = mapping.managed?.startsWith("goauthentik.io/providers/saml") || false;
|
||||
} else {
|
||||
selected = Array.from(this.instance?.propertyMappings).some(su => {
|
||||
return su == mapping.pk;
|
||||
${until(
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsSamlList({
|
||||
ordering: "saml_name",
|
||||
})
|
||||
.then((mappings) => {
|
||||
return mappings.results.map((mapping) => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappings) {
|
||||
selected =
|
||||
mapping.managed?.startsWith(
|
||||
"goauthentik.io/providers/saml",
|
||||
) || false;
|
||||
} else {
|
||||
selected = Array.from(
|
||||
this.instance?.propertyMappings,
|
||||
).some((su) => {
|
||||
return su == mapping.pk;
|
||||
});
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
});
|
||||
}
|
||||
return html`<option value=${ifDefined(mapping.pk)} ?selected=${selected}>${mapping.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Hold control/command to select multiple items.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`NameID Property Mapping`}
|
||||
name="nameIdMapping">
|
||||
name="nameIdMapping"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.nameIdMapping === undefined}>---------</option>
|
||||
${until(new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlList({
|
||||
ordering: "saml_name"
|
||||
}).then(mappings => {
|
||||
return mappings.results.map(mapping => {
|
||||
return html`<option value=${ifDefined(mapping.pk)} ?selected=${this.instance?.nameIdMapping === mapping.pk}>${mapping.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option
|
||||
value=""
|
||||
?selected=${this.instance?.nameIdMapping === undefined}
|
||||
>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsSamlList({
|
||||
ordering: "saml_name",
|
||||
})
|
||||
.then((mappings) => {
|
||||
return mappings.results.map((mapping) => {
|
||||
return html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${this.instance?.nameIdMapping ===
|
||||
mapping.pk}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Assertion valid not before`}
|
||||
?required=${true}
|
||||
name="assertionValidNotBefore">
|
||||
<input type="text" value="${this.instance?.assertionValidNotBefore || "minutes=-5"}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Configure the maximum allowed time drift for an asseration.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`(Format: hours=-1;minutes=-2;seconds=-3).`}</p>
|
||||
name="assertionValidNotBefore"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.assertionValidNotBefore || "minutes=-5"}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Configure the maximum allowed time drift for an asseration.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`(Format: hours=-1;minutes=-2;seconds=-3).`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Assertion valid not on or after`}
|
||||
?required=${true}
|
||||
name="assertionValidNotOnOrAfter">
|
||||
<input type="text" value="${this.instance?.assertionValidNotOnOrAfter || "minutes=5"}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).`}</p>
|
||||
name="assertionValidNotOnOrAfter"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.assertionValidNotOnOrAfter || "minutes=5"}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Session valid not on or after`}
|
||||
?required=${true}
|
||||
name="sessionValidNotOnOrAfter">
|
||||
<input type="text" value="${this.instance?.sessionValidNotOnOrAfter || "minutes=86400"}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).`}</p>
|
||||
name="sessionValidNotOnOrAfter"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.sessionValidNotOnOrAfter || "minutes=86400"}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Digest algorithm`}
|
||||
?required=${true}
|
||||
name="digestAlgorithm">
|
||||
name="digestAlgorithm"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${DigestAlgorithmEnum._200009Xmldsigsha1} ?selected=${this.instance?.digestAlgorithm === DigestAlgorithmEnum._200009Xmldsigsha1}>
|
||||
<option
|
||||
value=${DigestAlgorithmEnum._200009Xmldsigsha1}
|
||||
?selected=${this.instance?.digestAlgorithm ===
|
||||
DigestAlgorithmEnum._200009Xmldsigsha1}
|
||||
>
|
||||
${t`SHA1`}
|
||||
</option>
|
||||
<option value=${DigestAlgorithmEnum._200104Xmlencsha256} ?selected=${this.instance?.digestAlgorithm === DigestAlgorithmEnum._200104Xmlencsha256 || this.instance?.digestAlgorithm === undefined}>
|
||||
<option
|
||||
value=${DigestAlgorithmEnum._200104Xmlencsha256}
|
||||
?selected=${this.instance?.digestAlgorithm ===
|
||||
DigestAlgorithmEnum._200104Xmlencsha256 ||
|
||||
this.instance?.digestAlgorithm === undefined}
|
||||
>
|
||||
${t`SHA256`}
|
||||
</option>
|
||||
<option value=${DigestAlgorithmEnum._200104XmldsigMoresha384} ?selected=${this.instance?.digestAlgorithm === DigestAlgorithmEnum._200104XmldsigMoresha384}>
|
||||
<option
|
||||
value=${DigestAlgorithmEnum._200104XmldsigMoresha384}
|
||||
?selected=${this.instance?.digestAlgorithm ===
|
||||
DigestAlgorithmEnum._200104XmldsigMoresha384}
|
||||
>
|
||||
${t`SHA384`}
|
||||
</option>
|
||||
<option value=${DigestAlgorithmEnum._200104Xmlencsha512} ?selected=${this.instance?.digestAlgorithm === DigestAlgorithmEnum._200104Xmlencsha512}>
|
||||
<option
|
||||
value=${DigestAlgorithmEnum._200104Xmlencsha512}
|
||||
?selected=${this.instance?.digestAlgorithm ===
|
||||
DigestAlgorithmEnum._200104Xmlencsha512}
|
||||
>
|
||||
${t`SHA512`}
|
||||
</option>
|
||||
</select>
|
||||
@ -224,21 +371,43 @@ export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Signature algorithm`}
|
||||
?required=${true}
|
||||
name="signatureAlgorithm">
|
||||
name="signatureAlgorithm"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${SignatureAlgorithmEnum._200009XmldsigrsaSha1} ?selected=${this.instance?.signatureAlgorithm === SignatureAlgorithmEnum._200009XmldsigrsaSha1}>
|
||||
<option
|
||||
value=${SignatureAlgorithmEnum._200009XmldsigrsaSha1}
|
||||
?selected=${this.instance?.signatureAlgorithm ===
|
||||
SignatureAlgorithmEnum._200009XmldsigrsaSha1}
|
||||
>
|
||||
${t`RSA-SHA1`}
|
||||
</option>
|
||||
<option value=${SignatureAlgorithmEnum._200104XmldsigMorersaSha256} ?selected=${this.instance?.signatureAlgorithm === SignatureAlgorithmEnum._200104XmldsigMorersaSha256 || this.instance?.signatureAlgorithm === undefined}>
|
||||
<option
|
||||
value=${SignatureAlgorithmEnum._200104XmldsigMorersaSha256}
|
||||
?selected=${this.instance?.signatureAlgorithm ===
|
||||
SignatureAlgorithmEnum._200104XmldsigMorersaSha256 ||
|
||||
this.instance?.signatureAlgorithm === undefined}
|
||||
>
|
||||
${t`RSA-SHA256`}
|
||||
</option>
|
||||
<option value=${SignatureAlgorithmEnum._200104XmldsigMorersaSha384} ?selected=${this.instance?.signatureAlgorithm === SignatureAlgorithmEnum._200104XmldsigMorersaSha384}>
|
||||
<option
|
||||
value=${SignatureAlgorithmEnum._200104XmldsigMorersaSha384}
|
||||
?selected=${this.instance?.signatureAlgorithm ===
|
||||
SignatureAlgorithmEnum._200104XmldsigMorersaSha384}
|
||||
>
|
||||
${t`RSA-SHA384`}
|
||||
</option>
|
||||
<option value=${SignatureAlgorithmEnum._200104XmldsigMorersaSha512} ?selected=${this.instance?.signatureAlgorithm === SignatureAlgorithmEnum._200104XmldsigMorersaSha512}>
|
||||
<option
|
||||
value=${SignatureAlgorithmEnum._200104XmldsigMorersaSha512}
|
||||
?selected=${this.instance?.signatureAlgorithm ===
|
||||
SignatureAlgorithmEnum._200104XmldsigMorersaSha512}
|
||||
>
|
||||
${t`RSA-SHA512`}
|
||||
</option>
|
||||
<option value=${SignatureAlgorithmEnum._200009XmldsigdsaSha1} ?selected=${this.instance?.signatureAlgorithm === SignatureAlgorithmEnum._200009XmldsigdsaSha1}>
|
||||
<option
|
||||
value=${SignatureAlgorithmEnum._200009XmldsigdsaSha1}
|
||||
?selected=${this.instance?.signatureAlgorithm ===
|
||||
SignatureAlgorithmEnum._200009XmldsigdsaSha1}
|
||||
>
|
||||
${t`DSA-SHA1`}
|
||||
</option>
|
||||
</select>
|
||||
@ -247,5 +416,4 @@ export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
import { FlowsApi, FlowsInstancesListDesignationEnum, ProvidersApi, SAMLProvider } from "authentik-api";
|
||||
import {
|
||||
FlowsApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
ProvidersApi,
|
||||
SAMLProvider,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -9,7 +14,6 @@ import "../../../elements/forms/HorizontalFormElement";
|
||||
|
||||
@customElement("ak-provider-saml-import-form")
|
||||
export class SAMLProviderImportForm extends Form<SAMLProvider> {
|
||||
|
||||
getSuccessMessage(): string {
|
||||
return t`Successfully imported provider.`;
|
||||
}
|
||||
@ -29,35 +33,39 @@ export class SAMLProviderImportForm extends Form<SAMLProvider> {
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input type="text" class="pf-c-form-control" required />
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Authorization flow`}
|
||||
?required=${true}
|
||||
name="authorizationFlow">
|
||||
name="authorizationFlow"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authorization,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
return html`<option value=${flow.slug}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authorization,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
return html`<option value=${flow.slug}>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow used when authorizing this provider.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow used when authorizing this provider.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Metadata`}
|
||||
name="flow">
|
||||
<input type="file" value="" class="pf-c-form-control">
|
||||
<ak-form-element-horizontal label=${t`Metadata`} name="flow">
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -28,24 +28,37 @@ import { ifDefined } from "lit-html/directives/if-defined";
|
||||
|
||||
@customElement("ak-provider-saml-view")
|
||||
export class SAMLProviderViewPage extends LitElement {
|
||||
|
||||
@property()
|
||||
set args(value: { [key: string]: number }) {
|
||||
this.providerID = value.id;
|
||||
}
|
||||
|
||||
@property({type: Number})
|
||||
@property({ type: Number })
|
||||
set providerID(value: number) {
|
||||
new ProvidersApi(DEFAULT_CONFIG).providersSamlRetrieve({
|
||||
id: value,
|
||||
}).then((prov) => (this.provider = prov));
|
||||
new ProvidersApi(DEFAULT_CONFIG)
|
||||
.providersSamlRetrieve({
|
||||
id: value,
|
||||
})
|
||||
.then((prov) => (this.provider = prov));
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
provider?: SAMLProvider;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFPage, PFButton, PFFlex, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, AKGlobal];
|
||||
return [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFButton,
|
||||
PFFlex,
|
||||
PFDisplay,
|
||||
PFGallery,
|
||||
PFContent,
|
||||
PFCard,
|
||||
PFDescriptionList,
|
||||
PFSizing,
|
||||
AKGlobal,
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -61,118 +74,163 @@ export class SAMLProviderViewPage extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
return html`<ak-tabs>
|
||||
<section slot="page-overview" data-tab-title="${t`Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list pf-m-3-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`Name`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.name}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Assigned to application`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-provider-related-application .provider=${this.provider}></ak-provider-related-application>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`ACS URL`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.acsUrl}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Audience`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.audience}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Issuer`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.issuer}</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update SAML Provider`}
|
||||
</span>
|
||||
<ak-provider-saml-form
|
||||
slot="form"
|
||||
.instancePk=${this.provider.pk || 0}>
|
||||
</ak-provider-saml-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
<section
|
||||
slot="page-overview"
|
||||
data-tab-title="${t`Overview`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list pf-m-3-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`Name`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.name}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Assigned to application`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-provider-related-application
|
||||
.provider=${this.provider}
|
||||
></ak-provider-related-application>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`ACS URL`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.acsUrl}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Audience`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.audience}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Issuer`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.issuer}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update SAML Provider`} </span>
|
||||
<ak-provider-saml-form
|
||||
slot="form"
|
||||
.instancePk=${this.provider.pk || 0}
|
||||
>
|
||||
</ak-provider-saml-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section slot="page-changelog" data-tab-title="${t`Changelog`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.provider.pk || ""}
|
||||
targetModelApp="authentik_providers_saml"
|
||||
targetModelName="samlprovider">
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-changelog"
|
||||
data-tab-title="${t`Changelog`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.provider.pk || ""}
|
||||
targetModelApp="authentik_providers_saml"
|
||||
targetModelName="samlprovider"
|
||||
>
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</section>
|
||||
${this.provider.assignedApplicationName ? html`
|
||||
<section slot="page-metadata" data-tab-title="${t`Metadata`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
${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>`;
|
||||
}))}
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<a class="pf-c-button pf-m-primary" target="_blank"
|
||||
href=${this.provider.metadataDownloadUrl}>
|
||||
${t`Download`}
|
||||
</a>
|
||||
<ak-action-button
|
||||
class="pf-m-secondary"
|
||||
.apiRequest=${() => {
|
||||
const fullUrl = window.location.origin + this.provider?.metadataDownloadUrl;
|
||||
return navigator.clipboard.writeText(fullUrl);
|
||||
}}>
|
||||
${t`Copy download URL`}
|
||||
</ak-action-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>` : html``}
|
||||
</ak-tabs>`;
|
||||
</div>
|
||||
</section>
|
||||
${this.provider.assignedApplicationName
|
||||
? html` <section
|
||||
slot="page-metadata"
|
||||
data-tab-title="${t`Metadata`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
${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>`;
|
||||
}),
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<a
|
||||
class="pf-c-button pf-m-primary"
|
||||
target="_blank"
|
||||
href=${this.provider.metadataDownloadUrl}
|
||||
>
|
||||
${t`Download`}
|
||||
</a>
|
||||
<ak-action-button
|
||||
class="pf-m-secondary"
|
||||
.apiRequest=${() => {
|
||||
const fullUrl =
|
||||
window.location.origin +
|
||||
this.provider?.metadataDownloadUrl;
|
||||
return navigator.clipboard.writeText(fullUrl);
|
||||
}}
|
||||
>
|
||||
${t`Copy download URL`}
|
||||
</ak-action-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>`
|
||||
: html``}
|
||||
</ak-tabs>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,14 +14,15 @@ import { ifDefined } from "lit-html/directives/if-defined";
|
||||
|
||||
@customElement("ak-source-view")
|
||||
export class SourceViewPage extends LitElement {
|
||||
|
||||
@property({ type: String })
|
||||
set sourceSlug(slug: string) {
|
||||
new SourcesApi(DEFAULT_CONFIG).sourcesAllRetrieve({
|
||||
slug: slug
|
||||
}).then((source) => {
|
||||
this.source = source;
|
||||
});
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesAllRetrieve({
|
||||
slug: slug,
|
||||
})
|
||||
.then((source) => {
|
||||
this.source = source;
|
||||
});
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
@ -33,13 +34,21 @@ export class SourceViewPage extends LitElement {
|
||||
}
|
||||
switch (this.source?.component) {
|
||||
case "ak-source-ldap-form":
|
||||
return html`<ak-source-ldap-view sourceSlug=${this.source.slug}></ak-source-ldap-view>`;
|
||||
return html`<ak-source-ldap-view
|
||||
sourceSlug=${this.source.slug}
|
||||
></ak-source-ldap-view>`;
|
||||
case "ak-source-oauth-form":
|
||||
return html`<ak-source-oauth-view sourceSlug=${this.source.slug}></ak-source-oauth-view>`;
|
||||
return html`<ak-source-oauth-view
|
||||
sourceSlug=${this.source.slug}
|
||||
></ak-source-oauth-view>`;
|
||||
case "ak-source-saml-form":
|
||||
return html`<ak-source-saml-view sourceSlug=${this.source.slug}></ak-source-saml-view>`;
|
||||
return html`<ak-source-saml-view
|
||||
sourceSlug=${this.source.slug}
|
||||
></ak-source-saml-view>`;
|
||||
case "ak-source-plex-form":
|
||||
return html`<ak-source-plex-view sourceSlug=${this.source.slug}></ak-source-plex-view>`;
|
||||
return html`<ak-source-plex-view
|
||||
sourceSlug=${this.source.slug}
|
||||
></ak-source-plex-view>`;
|
||||
default:
|
||||
return html`<p>Invalid source type ${this.source.component}</p>`;
|
||||
}
|
||||
@ -47,10 +56,11 @@ export class SourceViewPage extends LitElement {
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<ak-page-header
|
||||
icon="pf-icon pf-icon-middleware"
|
||||
header=${ifDefined(this.source?.name)}
|
||||
description=${ifDefined(this.source?.verboseName)}>
|
||||
</ak-page-header>
|
||||
${this.renderSource()}`;
|
||||
icon="pf-icon pf-icon-middleware"
|
||||
header=${ifDefined(this.source?.name)}
|
||||
description=${ifDefined(this.source?.verboseName)}
|
||||
>
|
||||
</ak-page-header>
|
||||
${this.renderSource()}`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,11 +47,7 @@ export class SourceListPage extends TablePage<Source> {
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(t`Name`, "name"),
|
||||
new TableColumn(t`Type`),
|
||||
new TableColumn(""),
|
||||
];
|
||||
return [new TableColumn(t`Name`, "name"), new TableColumn(t`Type`), new TableColumn("")];
|
||||
}
|
||||
|
||||
row(item: Source): TemplateResult[] {
|
||||
@ -64,42 +60,35 @@ export class SourceListPage extends TablePage<Source> {
|
||||
${item.enabled ? html`` : html`<small>${t`Disabled`}</small>`}
|
||||
</a>`,
|
||||
html`${item.verboseName}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update ${item.verboseName}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
"instancePk": item.slug
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update ${item.verboseName}`} </span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
instancePk: item.slug,
|
||||
}}
|
||||
type=${ifDefined(item.component)}
|
||||
>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Source`}
|
||||
.usedBy=${() => {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesAllUsedByList({
|
||||
slug: item.slug,
|
||||
});
|
||||
}}
|
||||
type=${ifDefined(item.component)}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Source`}
|
||||
.usedBy=${() => {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesAllUsedByList({
|
||||
slug: item.slug
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesAllDestroy({
|
||||
slug: item.slug
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
.delete=${() => {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesAllDestroy({
|
||||
slug: item.slug,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
@ -115,41 +104,39 @@ export class SourceListPage extends TablePage<Source> {
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
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`Create`}</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(new SourcesApi(DEFAULT_CONFIG).sourcesAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create ${type.name}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
"modelName": type.modelName
|
||||
}}
|
||||
type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
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`Create`}</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(
|
||||
new SourcesApi(DEFAULT_CONFIG).sourcesAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create ${type.name}`} </span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
modelName: type.modelName,
|
||||
}}
|
||||
type=${type.component}
|
||||
>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br />
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}),
|
||||
html`<ak-spinner></ak-spinner>`,
|
||||
)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-source-ldap-form")
|
||||
export class LDAPSourceForm extends ModelForm<LDAPSource, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<LDAPSource> {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesLdapRetrieve({
|
||||
slug: pk,
|
||||
@ -31,199 +30,313 @@ export class LDAPSourceForm extends ModelForm<LDAPSource, string> {
|
||||
if (this.instance) {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesLdapPartialUpdate({
|
||||
slug: this.instance.slug,
|
||||
patchedLDAPSourceRequest: data
|
||||
patchedLDAPSourceRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesLdapCreate({
|
||||
lDAPSourceRequest: data as unknown as LDAPSourceRequest
|
||||
lDAPSourceRequest: data as unknown as LDAPSourceRequest,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Slug`}
|
||||
?required=${true}
|
||||
name="slug">
|
||||
<input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Slug`} ?required=${true} name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="enabled">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.enabled, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Enabled`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.enabled, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Enabled`} </label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="syncUsers">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.syncUsers, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Sync users`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.syncUsers, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Sync users`} </label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="syncUsersPassword">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.syncUsersPassword, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`User password writeback`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.syncUsersPassword, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`User password writeback`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Login password is synced from LDAP into authentik automatically. Enable this option only to write password changes in authentik back to LDAP.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Login password is synced from LDAP into authentik automatically. Enable this option only to write password changes in authentik back to LDAP.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="syncGroups">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.syncGroups, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Sync groups`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.syncGroups, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Sync groups`} </label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Connection settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Connection settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Server URI`}
|
||||
?required=${true}
|
||||
name="serverUri">
|
||||
<input type="text" placeholder="ldap://1.2.3.4" value="${ifDefined(this.instance?.serverUri)}" class="pf-c-form-control" required>
|
||||
name="serverUri"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="ldap://1.2.3.4"
|
||||
value="${ifDefined(this.instance?.serverUri)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="startTls">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.startTls, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Enable StartTLS`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.startTls, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Enable StartTLS`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`To use SSL instead, use 'ldaps://' and disable this option.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`To use SSL instead, use 'ldaps://' and disable this option.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Bind CN`}
|
||||
?required=${true}
|
||||
name="bindCn">
|
||||
<input type="text" value="${ifDefined(this.instance?.bindCn)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Bind CN`} ?required=${true} name="bindCn">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.bindCn)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Bind Password`}
|
||||
?required=${true}
|
||||
?writeOnly=${this.instance !== undefined}
|
||||
name="bindPassword">
|
||||
<input type="text" value="" class="pf-c-form-control" required>
|
||||
name="bindPassword"
|
||||
>
|
||||
<input type="text" value="" class="pf-c-form-control" required />
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Base DN`}
|
||||
?required=${true}
|
||||
name="baseDn">
|
||||
<input type="text" value="${ifDefined(this.instance?.baseDn)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Base DN`} ?required=${true} name="baseDn">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.baseDn)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group>
|
||||
<span slot="header">
|
||||
${t`Advanced settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Advanced settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`User Property Mappings`}
|
||||
?required=${true}
|
||||
name="propertyMappings">
|
||||
name="propertyMappings"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsLdapList({
|
||||
ordering: "managed,object_field"
|
||||
}).then(mappings => {
|
||||
return mappings.results.map(mapping => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappings) {
|
||||
selected = mapping.managed?.startsWith("goauthentik.io/sources/ldap/default") || mapping.managed?.startsWith("goauthentik.io/sources/ldap/ms") || false;
|
||||
} else {
|
||||
selected = Array.from(this.instance?.propertyMappings).some(su => {
|
||||
return su == mapping.pk;
|
||||
${until(
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsLdapList({
|
||||
ordering: "managed,object_field",
|
||||
})
|
||||
.then((mappings) => {
|
||||
return mappings.results.map((mapping) => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappings) {
|
||||
selected =
|
||||
mapping.managed?.startsWith(
|
||||
"goauthentik.io/sources/ldap/default",
|
||||
) ||
|
||||
mapping.managed?.startsWith(
|
||||
"goauthentik.io/sources/ldap/ms",
|
||||
) ||
|
||||
false;
|
||||
} else {
|
||||
selected = Array.from(
|
||||
this.instance?.propertyMappings,
|
||||
).some((su) => {
|
||||
return su == mapping.pk;
|
||||
});
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
});
|
||||
}
|
||||
return html`<option value=${ifDefined(mapping.pk)} ?selected=${selected}>${mapping.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Property mappings used to user creation.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Property mappings used to user creation.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Hold control/command to select multiple items.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Group Property Mappings`}
|
||||
?required=${true}
|
||||
name="propertyMappingsGroup">
|
||||
name="propertyMappingsGroup"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsLdapList({
|
||||
ordering: "object_field"
|
||||
}).then(mappings => {
|
||||
return mappings.results.map(mapping => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappingsGroup) {
|
||||
selected = mapping.managed === "goauthentik.io/sources/ldap/default-name";
|
||||
} else {
|
||||
selected = Array.from(this.instance?.propertyMappingsGroup).some(su => {
|
||||
return su == mapping.pk;
|
||||
${until(
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsLdapList({
|
||||
ordering: "object_field",
|
||||
})
|
||||
.then((mappings) => {
|
||||
return mappings.results.map((mapping) => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappingsGroup) {
|
||||
selected =
|
||||
mapping.managed ===
|
||||
"goauthentik.io/sources/ldap/default-name";
|
||||
} else {
|
||||
selected = Array.from(
|
||||
this.instance?.propertyMappingsGroup,
|
||||
).some((su) => {
|
||||
return su == mapping.pk;
|
||||
});
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
});
|
||||
}
|
||||
return html`<option value=${ifDefined(mapping.pk)} ?selected=${selected}>${mapping.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Property mappings used to group creation.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Property mappings used to group creation.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Hold control/command to select multiple items.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Addition User DN`}
|
||||
name="additionalUserDn">
|
||||
<input type="text" value="${ifDefined(this.instance?.additionalUserDn)}" class="pf-c-form-control">
|
||||
<p class="pf-c-form__helper-text">${t`Additional user DN, prepended to the Base DN.`}</p>
|
||||
name="additionalUserDn"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.additionalUserDn)}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Additional user DN, prepended to the Base DN.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Addition Group DN`}
|
||||
name="additionalGroupDn">
|
||||
<input type="text" value="${ifDefined(this.instance?.additionalGroupDn)}" class="pf-c-form-control">
|
||||
<p class="pf-c-form__helper-text">${t`Additional group DN, prepended to the Base DN.`}</p>
|
||||
name="additionalGroupDn"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.additionalGroupDn)}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Additional group DN, prepended to the Base DN.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`User object filter`}
|
||||
?required=${true}
|
||||
name="userObjectFilter">
|
||||
<input type="text" value="${this.instance?.userObjectFilter || "(objectClass=person)"}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Consider Objects matching this filter to be Users.`}</p>
|
||||
name="userObjectFilter"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.userObjectFilter || "(objectClass=person)"}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Consider Objects matching this filter to be Users.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Group object filter`}
|
||||
?required=${true}
|
||||
name="groupObjectFilter">
|
||||
<input type="text" value="${this.instance?.groupObjectFilter || "(objectClass=group)"}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Consider Objects matching this filter to be Groups.`}</p>
|
||||
name="groupObjectFilter"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.groupObjectFilter || "(objectClass=group)"}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Consider Objects matching this filter to be Groups.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Group membership field`}
|
||||
?required=${true}
|
||||
name="groupMembershipField">
|
||||
<input type="text" value="${this.instance?.groupMembershipField || "member"}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Field which contains members of a group.`}</p>
|
||||
name="groupMembershipField"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.groupMembershipField || "member"}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Field which contains members of a group.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Object uniqueness field`}
|
||||
?required=${true}
|
||||
name="objectUniquenessField">
|
||||
<input type="text" value="${this.instance?.objectUniquenessField || "objectSid"}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Field which contains a unique Identifier.`}</p>
|
||||
name="objectUniquenessField"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.objectUniquenessField || "objectSid"}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Field which contains a unique Identifier.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -27,21 +27,34 @@ import { EVENT_REFRESH } from "../../../constants";
|
||||
|
||||
@customElement("ak-source-ldap-view")
|
||||
export class LDAPSourceViewPage extends LitElement {
|
||||
|
||||
@property({ type: String })
|
||||
set sourceSlug(slug: string) {
|
||||
new SourcesApi(DEFAULT_CONFIG).sourcesLdapRetrieve({
|
||||
slug: slug
|
||||
}).then((source) => {
|
||||
this.source = source;
|
||||
});
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesLdapRetrieve({
|
||||
slug: slug,
|
||||
})
|
||||
.then((source) => {
|
||||
this.source = source;
|
||||
});
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
source!: LDAPSource;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFPage, PFButton, PFFlex, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, AKGlobal];
|
||||
return [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFButton,
|
||||
PFFlex,
|
||||
PFDisplay,
|
||||
PFGallery,
|
||||
PFContent,
|
||||
PFCard,
|
||||
PFDescriptionList,
|
||||
PFSizing,
|
||||
AKGlobal,
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -57,120 +70,155 @@ export class LDAPSourceViewPage extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
return html`<ak-tabs>
|
||||
<section slot="page-overview" data-tab-title="${t`Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<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`Name`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.source.name}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Server URI`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.source.serverUri}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Base DN`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ul>
|
||||
<li>${this.source.baseDn}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update LDAP Source`}
|
||||
</span>
|
||||
<ak-source-ldap-form
|
||||
slot="form"
|
||||
.instancePk=${this.source.slug}>
|
||||
</ak-source-ldap-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
<section
|
||||
slot="page-overview"
|
||||
data-tab-title="${t`Overview`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<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`Name`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.source.name}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Server URI`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.source.serverUri}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Base DN`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ul>
|
||||
<li>${this.source.baseDn}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update LDAP Source`} </span>
|
||||
<ak-source-ldap-form
|
||||
slot="form"
|
||||
.instancePk=${this.source.slug}
|
||||
>
|
||||
</ak-source-ldap-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section slot="page-changelog" data-tab-title="${t`Changelog`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.source.pk || ""}
|
||||
targetModelApp="authentik_sources_ldap"
|
||||
targetModelName="ldapsource">
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-changelog"
|
||||
data-tab-title="${t`Changelog`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.source.pk || ""}
|
||||
targetModelApp="authentik_sources_ldap"
|
||||
targetModelName="ldapsource"
|
||||
>
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</section>
|
||||
<section slot="page-sync" data-tab-title="${t`Sync`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">
|
||||
<p>${t`Sync status`}</p>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
${until(new SourcesApi(DEFAULT_CONFIG).sourcesLdapSyncStatusRetrieve({
|
||||
slug: this.source.slug
|
||||
}).then((ls) => {
|
||||
let header = html``;
|
||||
if (ls.status === StatusEnum.Warning) {
|
||||
header = html`<p>${t`Task finished with warnings`}</p>`;
|
||||
} else if (status === StatusEnum.Error) {
|
||||
header = html`<p>${t`Task finished with errors`}</p>`;
|
||||
} else {
|
||||
header = html`<p>${t`Last sync: ${ls.taskFinishTimestamp.toLocaleString()}`}</p>`;
|
||||
}
|
||||
return html`
|
||||
${header}
|
||||
<ul>
|
||||
${ls.messages.map(m => {
|
||||
return html`<li>${m}</li>`;
|
||||
})}
|
||||
</ul>
|
||||
`;
|
||||
}).catch(() => {
|
||||
return html`<p>${t`Not synced yet.`}</p>`;
|
||||
}), "loading")}
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-action-button
|
||||
.apiRequest=${() => {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesLdapPartialUpdate({
|
||||
slug: this.source?.slug || "",
|
||||
patchedLDAPSourceRequest: this.source,
|
||||
});
|
||||
}}>
|
||||
${t`Retry Task`}
|
||||
</ak-action-button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-sync"
|
||||
data-tab-title="${t`Sync`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">
|
||||
<p>${t`Sync status`}</p>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
${until(
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesLdapSyncStatusRetrieve({
|
||||
slug: this.source.slug,
|
||||
})
|
||||
.then((ls) => {
|
||||
let header = html``;
|
||||
if (ls.status === StatusEnum.Warning) {
|
||||
header = html`<p>
|
||||
${t`Task finished with warnings`}
|
||||
</p>`;
|
||||
} else if (status === StatusEnum.Error) {
|
||||
header = html`<p>
|
||||
${t`Task finished with errors`}
|
||||
</p>`;
|
||||
} else {
|
||||
header = html`<p>
|
||||
${t`Last sync: ${ls.taskFinishTimestamp.toLocaleString()}`}
|
||||
</p>`;
|
||||
}
|
||||
return html`
|
||||
${header}
|
||||
<ul>
|
||||
${ls.messages.map((m) => {
|
||||
return html`<li>${m}</li>`;
|
||||
})}
|
||||
</ul>
|
||||
`;
|
||||
})
|
||||
.catch(() => {
|
||||
return html`<p>${t`Not synced yet.`}</p>`;
|
||||
}),
|
||||
"loading",
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-action-button
|
||||
.apiRequest=${() => {
|
||||
return new SourcesApi(
|
||||
DEFAULT_CONFIG,
|
||||
).sourcesLdapPartialUpdate({
|
||||
slug: this.source?.slug || "",
|
||||
patchedLDAPSourceRequest: this.source,
|
||||
});
|
||||
}}
|
||||
>
|
||||
${t`Retry Task`}
|
||||
</ak-action-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</ak-tabs>`;
|
||||
</div>
|
||||
</section>
|
||||
</ak-tabs>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
import { OAuthSource, SourcesApi, FlowsApi, UserMatchingModeEnum, OAuthSourceRequest, FlowsInstancesListDesignationEnum } from "authentik-api";
|
||||
import {
|
||||
OAuthSource,
|
||||
SourcesApi,
|
||||
FlowsApi,
|
||||
UserMatchingModeEnum,
|
||||
OAuthSourceRequest,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -12,23 +19,24 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-source-oauth-form")
|
||||
export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<OAuthSource> {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesOauthRetrieve({
|
||||
slug: pk,
|
||||
}).then(source => {
|
||||
this.showUrlOptions = first(source.type?.urlsCustomizable, false);
|
||||
return source;
|
||||
});
|
||||
return new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesOauthRetrieve({
|
||||
slug: pk,
|
||||
})
|
||||
.then((source) => {
|
||||
this.showUrlOptions = first(source.type?.urlsCustomizable, false);
|
||||
return source;
|
||||
});
|
||||
}
|
||||
|
||||
@property()
|
||||
modelName?: string;
|
||||
|
||||
@property({type: Boolean})
|
||||
@property({ type: Boolean })
|
||||
showUrlOptions = false;
|
||||
|
||||
@property({type: Boolean})
|
||||
@property({ type: Boolean })
|
||||
showRequestTokenURL = false;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
@ -43,11 +51,11 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
|
||||
if (this.instance?.slug) {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesOauthPartialUpdate({
|
||||
slug: this.instance.slug,
|
||||
patchedOAuthSourceRequest: data
|
||||
patchedOAuthSourceRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesOauthCreate({
|
||||
oAuthSourceRequest: data as unknown as OAuthSourceRequest
|
||||
oAuthSourceRequest: data as unknown as OAuthSourceRequest,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -56,194 +64,301 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
|
||||
if (!this.showUrlOptions) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ak-form-group>
|
||||
<span slot="header">
|
||||
${t`URL settings`}
|
||||
</span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Authorization URL`}
|
||||
?required=${true}
|
||||
name="authorizationUrl">
|
||||
<input type="text" value="${first(this.instance?.authorizationUrl, "")}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`URL the user is redirect to to consent the authorization.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Access token URL`}
|
||||
?required=${true}
|
||||
name="accessTokenUrl">
|
||||
<input type="text" value="${first(this.instance?.accessTokenUrl, "")}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`URL used by authentik to retrieve tokens.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Profile URL`}
|
||||
?required=${true}
|
||||
name="profileUrl">
|
||||
<input type="text" value="${first(this.instance?.profileUrl, "")}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`URL used by authentik to get user information.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
${this.showRequestTokenURL ? html`<ak-form-element-horizontal
|
||||
label=${t`Request token URL`}
|
||||
name="requestTokenUrl">
|
||||
<input type="text" value="${first(this.instance?.requestTokenUrl, "")}" class="pf-c-form-control">
|
||||
<p class="pf-c-form__helper-text">${t`URL used to request the initial token. This URL is only required for OAuth 1.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
` : html``}
|
||||
</div>
|
||||
</ak-form-group>`;
|
||||
return html` <ak-form-group>
|
||||
<span slot="header"> ${t`URL settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Authorization URL`}
|
||||
?required=${true}
|
||||
name="authorizationUrl"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.authorizationUrl, "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`URL the user is redirect to to consent the authorization.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Access token URL`}
|
||||
?required=${true}
|
||||
name="accessTokenUrl"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.accessTokenUrl, "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`URL used by authentik to retrieve tokens.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Profile URL`}
|
||||
?required=${true}
|
||||
name="profileUrl"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.profileUrl, "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`URL used by authentik to get user information.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
${this.showRequestTokenURL
|
||||
? html`<ak-form-element-horizontal
|
||||
label=${t`Request token URL`}
|
||||
name="requestTokenUrl"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.requestTokenUrl, "")}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`URL used to request the initial token. This URL is only required for OAuth 1.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal> `
|
||||
: html``}
|
||||
</div>
|
||||
</ak-form-group>`;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Slug`}
|
||||
?required=${true}
|
||||
name="slug">
|
||||
<input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Slug`} ?required=${true} name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="enabled">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.enabled, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Enabled`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.enabled, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Enabled`} </label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`User matching mode`}
|
||||
?required=${true}
|
||||
name="userMatchingMode">
|
||||
name="userMatchingMode"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${UserMatchingModeEnum.Identifier} ?selected=${this.instance?.userMatchingMode === UserMatchingModeEnum.Identifier}>
|
||||
<option
|
||||
value=${UserMatchingModeEnum.Identifier}
|
||||
?selected=${this.instance?.userMatchingMode ===
|
||||
UserMatchingModeEnum.Identifier}
|
||||
>
|
||||
${t`Link users on unique identifier`}
|
||||
</option>
|
||||
<option value=${UserMatchingModeEnum.UsernameLink} ?selected=${this.instance?.userMatchingMode === UserMatchingModeEnum.UsernameLink}>
|
||||
<option
|
||||
value=${UserMatchingModeEnum.UsernameLink}
|
||||
?selected=${this.instance?.userMatchingMode ===
|
||||
UserMatchingModeEnum.UsernameLink}
|
||||
>
|
||||
${t`Link to a user with identical email address. Can have security implications when a source doesn't validate email addresses`}
|
||||
</option>
|
||||
<option value=${UserMatchingModeEnum.UsernameDeny} ?selected=${this.instance?.userMatchingMode === UserMatchingModeEnum.UsernameDeny}>
|
||||
<option
|
||||
value=${UserMatchingModeEnum.UsernameDeny}
|
||||
?selected=${this.instance?.userMatchingMode ===
|
||||
UserMatchingModeEnum.UsernameDeny}
|
||||
>
|
||||
${t`Use the user's email address, but deny enrollment when the email address already exists.`}
|
||||
</option>
|
||||
<option value=${UserMatchingModeEnum.EmailLink} ?selected=${this.instance?.userMatchingMode === UserMatchingModeEnum.EmailLink}>
|
||||
<option
|
||||
value=${UserMatchingModeEnum.EmailLink}
|
||||
?selected=${this.instance?.userMatchingMode ===
|
||||
UserMatchingModeEnum.EmailLink}
|
||||
>
|
||||
${t`Link to a user with identical username address. Can have security implications when a username is used with another source.`}
|
||||
</option>
|
||||
<option value=${UserMatchingModeEnum.EmailDeny} ?selected=${this.instance?.userMatchingMode === UserMatchingModeEnum.EmailDeny}>
|
||||
<option
|
||||
value=${UserMatchingModeEnum.EmailDeny}
|
||||
?selected=${this.instance?.userMatchingMode ===
|
||||
UserMatchingModeEnum.EmailDeny}
|
||||
>
|
||||
${t`Use the user's username, but deny enrollment when the username already exists.`}
|
||||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Protocol settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Protocol settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Consumer key`}
|
||||
?required=${true}
|
||||
name="consumerKey">
|
||||
<input type="text" value="${ifDefined(this.instance?.consumerKey)}" class="pf-c-form-control" required>
|
||||
name="consumerKey"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.consumerKey)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Consumer secret`}
|
||||
?required=${true}
|
||||
?writeOnly=${this.instance !== undefined}
|
||||
name="consumerSecret">
|
||||
<input type="text" value="" class="pf-c-form-control" required>
|
||||
name="consumerSecret"
|
||||
>
|
||||
<input type="text" value="" class="pf-c-form-control" required />
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Provider type`}
|
||||
name="providerType">
|
||||
<select class="pf-c-form-control" @change=${(ev: Event) => {
|
||||
const el = (ev.target as HTMLSelectElement);
|
||||
const selected = el.selectedOptions[0];
|
||||
this.showUrlOptions = "data-urls-custom" in selected.attributes;
|
||||
this.showRequestTokenURL = "data-request-token" in selected.attributes;
|
||||
if (!this.instance) {
|
||||
this.instance = {} as OAuthSource;
|
||||
}
|
||||
this.instance.providerType = selected.value;
|
||||
}}>
|
||||
${until(new SourcesApi(DEFAULT_CONFIG).sourcesOauthSourceTypesList().then(types => {
|
||||
return types.map(type => {
|
||||
let selected = this.instance?.providerType === type.slug;
|
||||
const modelSlug = this.modelName?.replace("oauthsource", "").replace("-", "");
|
||||
const typeSlug = type.slug.replace("-", "");
|
||||
if (!this.instance?.pk) {
|
||||
if (modelSlug === typeSlug) {
|
||||
selected = true;
|
||||
this.showUrlOptions = type.urlsCustomizable;
|
||||
this.showRequestTokenURL = type.requestTokenUrl !== null;
|
||||
}
|
||||
}
|
||||
return html`<option
|
||||
?data-urls-custom=${type.urlsCustomizable}
|
||||
?data-request-token=${type.requestTokenUrl}
|
||||
value=${type.slug}
|
||||
?selected=${selected}>
|
||||
${type.name}
|
||||
</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<ak-form-element-horizontal label=${t`Provider type`} name="providerType">
|
||||
<select
|
||||
class="pf-c-form-control"
|
||||
@change=${(ev: Event) => {
|
||||
const el = ev.target as HTMLSelectElement;
|
||||
const selected = el.selectedOptions[0];
|
||||
this.showUrlOptions = "data-urls-custom" in selected.attributes;
|
||||
this.showRequestTokenURL =
|
||||
"data-request-token" in selected.attributes;
|
||||
if (!this.instance) {
|
||||
this.instance = {} as OAuthSource;
|
||||
}
|
||||
this.instance.providerType = selected.value;
|
||||
}}
|
||||
>
|
||||
${until(
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesOauthSourceTypesList()
|
||||
.then((types) => {
|
||||
return types.map((type) => {
|
||||
let selected =
|
||||
this.instance?.providerType === type.slug;
|
||||
const modelSlug = this.modelName
|
||||
?.replace("oauthsource", "")
|
||||
.replace("-", "");
|
||||
const typeSlug = type.slug.replace("-", "");
|
||||
if (!this.instance?.pk) {
|
||||
if (modelSlug === typeSlug) {
|
||||
selected = true;
|
||||
this.showUrlOptions = type.urlsCustomizable;
|
||||
this.showRequestTokenURL =
|
||||
type.requestTokenUrl !== null;
|
||||
}
|
||||
}
|
||||
return html`<option
|
||||
?data-urls-custom=${type.urlsCustomizable}
|
||||
?data-request-token=${type.requestTokenUrl}
|
||||
value=${type.slug}
|
||||
?selected=${selected}
|
||||
>
|
||||
${type.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
${this.renderUrlOptions()}
|
||||
<ak-form-group>
|
||||
<span slot="header">
|
||||
${t`Flow settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Flow settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Authentication flow`}
|
||||
?required=${true}
|
||||
name="authenticationFlow">
|
||||
name="authenticationFlow"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authentication,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
let selected = this.instance?.authenticationFlow === flow.pk;
|
||||
if (!this.instance?.pk && !this.instance?.authenticationFlow && flow.slug === "default-source-authentication") {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation:
|
||||
FlowsInstancesListDesignationEnum.Authentication,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
let selected =
|
||||
this.instance?.authenticationFlow === flow.pk;
|
||||
if (
|
||||
!this.instance?.pk &&
|
||||
!this.instance?.authenticationFlow &&
|
||||
flow.slug === "default-source-authentication"
|
||||
) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow to use when authenticating existing users.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow to use when authenticating existing users.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Enrollment flow`}
|
||||
?required=${true}
|
||||
name="enrollmentFlow">
|
||||
name="enrollmentFlow"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Enrollment,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
let selected = this.instance?.enrollmentFlow === flow.pk;
|
||||
if (!this.instance?.pk && !this.instance?.enrollmentFlow && flow.slug === "default-source-enrollment") {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Enrollment,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
let selected =
|
||||
this.instance?.enrollmentFlow === flow.pk;
|
||||
if (
|
||||
!this.instance?.pk &&
|
||||
!this.instance?.enrollmentFlow &&
|
||||
flow.slug === "default-source-enrollment"
|
||||
) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow to use when enrolling new users.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow to use when enrolling new users.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -26,21 +26,34 @@ import { EVENT_REFRESH } from "../../../constants";
|
||||
|
||||
@customElement("ak-source-oauth-view")
|
||||
export class OAuthSourceViewPage extends LitElement {
|
||||
|
||||
@property({ type: String })
|
||||
set sourceSlug(value: string) {
|
||||
new SourcesApi(DEFAULT_CONFIG).sourcesOauthRetrieve({
|
||||
slug: value
|
||||
}).then((source) => {
|
||||
this.source = source;
|
||||
});
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesOauthRetrieve({
|
||||
slug: value,
|
||||
})
|
||||
.then((source) => {
|
||||
this.source = source;
|
||||
});
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
source?: OAuthSource;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFPage, PFButton, PFFlex, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, AKGlobal];
|
||||
return [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFButton,
|
||||
PFFlex,
|
||||
PFDisplay,
|
||||
PFGallery,
|
||||
PFContent,
|
||||
PFCard,
|
||||
PFDescriptionList,
|
||||
PFSizing,
|
||||
AKGlobal,
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -56,104 +69,142 @@ export class OAuthSourceViewPage extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
return html`<ak-tabs>
|
||||
<section slot="page-overview" data-tab-title="${t`Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<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`Name`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.source.name}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Provider Type`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.source.providerType}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Callback URL`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<code class="pf-c-description-list__text">${this.source.callbackUrl}</code>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Access Key`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.source.consumerKey}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Authorization URL`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.source.type?.authorizationUrl || this.source.authorizationUrl}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Token URL`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.source.type?.accessTokenUrl || this.source.accessTokenUrl}</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update OAuth Source`}
|
||||
</span>
|
||||
<ak-source-oauth-form
|
||||
slot="form"
|
||||
.instancePk=${this.source.slug}>
|
||||
</ak-source-oauth-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
<section
|
||||
slot="page-overview"
|
||||
data-tab-title="${t`Overview`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<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`Name`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.source.name}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Provider Type`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.source.providerType}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Callback URL`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<code class="pf-c-description-list__text"
|
||||
>${this.source.callbackUrl}</code
|
||||
>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Access Key`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.source.consumerKey}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Authorization URL`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.source.type?.authorizationUrl ||
|
||||
this.source.authorizationUrl}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Token URL`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.source.type?.accessTokenUrl ||
|
||||
this.source.accessTokenUrl}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update OAuth Source`} </span>
|
||||
<ak-source-oauth-form
|
||||
slot="form"
|
||||
.instancePk=${this.source.slug}
|
||||
>
|
||||
</ak-source-oauth-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section slot="page-changelog" data-tab-title="${t`Changelog`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.source.pk || ""}
|
||||
targetModelApp="authentik_sources_oauth"
|
||||
targetModelName="oauthsource">
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div slot="page-policy-binding" data-tab-title="${t`Policy Bindings`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`These bindings control which users can access this source.
|
||||
You can only use policies here as access is checked before the user is authenticated.`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-bound-policies-list .target=${this.source.pk} ?policyOnly=${true}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-changelog"
|
||||
data-tab-title="${t`Changelog`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.source.pk || ""}
|
||||
targetModelApp="authentik_sources_oauth"
|
||||
targetModelName="oauthsource"
|
||||
>
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>`;
|
||||
</section>
|
||||
<div
|
||||
slot="page-policy-binding"
|
||||
data-tab-title="${t`Policy Bindings`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">
|
||||
${t`These bindings control which users can access this source.
|
||||
You can only use policies here as access is checked before the user is authenticated.`}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-bound-policies-list .target=${this.source.pk} ?policyOnly=${true}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { PlexSource, SourcesApi, FlowsApi, UserMatchingModeEnum, FlowsInstancesListDesignationEnum } from "authentik-api";
|
||||
import {
|
||||
PlexSource,
|
||||
SourcesApi,
|
||||
FlowsApi,
|
||||
UserMatchingModeEnum,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -8,32 +14,32 @@ import "../../../elements/forms/HorizontalFormElement";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { first, randomString } from "../../../utils";
|
||||
import { PlexAPIClient, PlexResource, popupCenterScreen} from "../../../flows/sources/plex/API";
|
||||
import { PlexAPIClient, PlexResource, popupCenterScreen } from "../../../flows/sources/plex/API";
|
||||
import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
|
||||
@customElement("ak-source-plex-form")
|
||||
export class PlexSourceForm extends ModelForm<PlexSource, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<PlexSource> {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesPlexRetrieve({
|
||||
slug: pk,
|
||||
}).then(source => {
|
||||
this.plexToken = source.plexToken;
|
||||
this.loadServers();
|
||||
return source;
|
||||
});
|
||||
return new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesPlexRetrieve({
|
||||
slug: pk,
|
||||
})
|
||||
.then((source) => {
|
||||
this.plexToken = source.plexToken;
|
||||
this.loadServers();
|
||||
return source;
|
||||
});
|
||||
}
|
||||
|
||||
@property()
|
||||
plexToken?: string;
|
||||
|
||||
@property({attribute: false})
|
||||
@property({ attribute: false })
|
||||
plexResources?: PlexResource[];
|
||||
|
||||
get defaultInstance(): PlexSource | undefined {
|
||||
return {
|
||||
clientId: randomString(40)
|
||||
clientId: randomString(40),
|
||||
} as PlexSource;
|
||||
}
|
||||
|
||||
@ -50,11 +56,11 @@ export class PlexSourceForm extends ModelForm<PlexSource, string> {
|
||||
if (this.instance?.slug) {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesPlexUpdate({
|
||||
slug: this.instance.slug,
|
||||
plexSourceRequest: data
|
||||
plexSourceRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesPlexCreate({
|
||||
plexSourceRequest: data
|
||||
plexSourceRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -62,7 +68,7 @@ export class PlexSourceForm extends ModelForm<PlexSource, string> {
|
||||
async doAuth(): Promise<void> {
|
||||
const authInfo = await PlexAPIClient.getPin(this.instance?.clientId || "");
|
||||
const authWindow = popupCenterScreen(authInfo.authUrl, "plex auth", 550, 700);
|
||||
PlexAPIClient.pinPoll(this.instance?.clientId || "", authInfo.pin.id).then(token => {
|
||||
PlexAPIClient.pinPoll(this.instance?.clientId || "", authInfo.pin.id).then((token) => {
|
||||
authWindow?.close();
|
||||
this.plexToken = token;
|
||||
this.loadServers();
|
||||
@ -78,22 +84,32 @@ export class PlexSourceForm extends ModelForm<PlexSource, string> {
|
||||
|
||||
renderSettings(): TemplateResult {
|
||||
if (!this.plexToken) {
|
||||
return html`
|
||||
<button class="pf-c-button pf-m-primary" type="button" @click=${() => {
|
||||
return html` <button
|
||||
class="pf-c-button pf-m-primary"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.doAuth();
|
||||
}}>
|
||||
${t`Load servers`}
|
||||
</button>`;
|
||||
}}
|
||||
>
|
||||
${t`Load servers`}
|
||||
</button>`;
|
||||
}
|
||||
return html`
|
||||
<button class="pf-c-button pf-m-secondary" type="button" @click=${() => {
|
||||
this.doAuth();
|
||||
}}>
|
||||
${t`Re-authenticate with plex`}
|
||||
return html` <button
|
||||
class="pf-c-button pf-m-secondary"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.doAuth();
|
||||
}}
|
||||
>
|
||||
${t`Re-authenticate with plex`}
|
||||
</button>
|
||||
<ak-form-element-horizontal name="allowFriends">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.allowFriends, true)}>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.allowFriends, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Allow friends to authenticate via Plex, even if you don't share any servers`}
|
||||
</label>
|
||||
@ -102,127 +118,201 @@ export class PlexSourceForm extends ModelForm<PlexSource, string> {
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Allowed servers`}
|
||||
?required=${true}
|
||||
name="allowedServers">
|
||||
name="allowedServers"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.plexResources?.map(r => {
|
||||
const selected = Array.from(this.instance?.allowedServers || []).some(server => {
|
||||
return server == r.clientIdentifier;
|
||||
});
|
||||
return html`<option value=${r.clientIdentifier} ?selected=${selected}>${r.name}</option>`;
|
||||
${this.plexResources?.map((r) => {
|
||||
const selected = Array.from(this.instance?.allowedServers || []).some(
|
||||
(server) => {
|
||||
return server == r.clientIdentifier;
|
||||
},
|
||||
);
|
||||
return html`<option value=${r.clientIdentifier} ?selected=${selected}>
|
||||
${r.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Select which server a user has to be a member of to be allowed to authenticate.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Select which server a user has to be a member of to be allowed to authenticate.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Hold control/command to select multiple items.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Slug`}
|
||||
?required=${true}
|
||||
name="slug">
|
||||
<input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Slug`} ?required=${true} name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="enabled">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.enabled, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Enabled`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.enabled, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Enabled`} </label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`User matching mode`}
|
||||
?required=${true}
|
||||
name="userMatchingMode">
|
||||
name="userMatchingMode"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${UserMatchingModeEnum.Identifier} ?selected=${this.instance?.userMatchingMode === UserMatchingModeEnum.Identifier}>
|
||||
<option
|
||||
value=${UserMatchingModeEnum.Identifier}
|
||||
?selected=${this.instance?.userMatchingMode ===
|
||||
UserMatchingModeEnum.Identifier}
|
||||
>
|
||||
${t`Link users on unique identifier`}
|
||||
</option>
|
||||
<option value=${UserMatchingModeEnum.UsernameLink} ?selected=${this.instance?.userMatchingMode === UserMatchingModeEnum.UsernameLink}>
|
||||
<option
|
||||
value=${UserMatchingModeEnum.UsernameLink}
|
||||
?selected=${this.instance?.userMatchingMode ===
|
||||
UserMatchingModeEnum.UsernameLink}
|
||||
>
|
||||
${t`Link to a user with identical email address. Can have security implications when a source doesn't validate email addresses`}
|
||||
</option>
|
||||
<option value=${UserMatchingModeEnum.UsernameDeny} ?selected=${this.instance?.userMatchingMode === UserMatchingModeEnum.UsernameDeny}>
|
||||
<option
|
||||
value=${UserMatchingModeEnum.UsernameDeny}
|
||||
?selected=${this.instance?.userMatchingMode ===
|
||||
UserMatchingModeEnum.UsernameDeny}
|
||||
>
|
||||
${t`Use the user's email address, but deny enrollment when the email address already exists.`}
|
||||
</option>
|
||||
<option value=${UserMatchingModeEnum.EmailLink} ?selected=${this.instance?.userMatchingMode === UserMatchingModeEnum.EmailLink}>
|
||||
<option
|
||||
value=${UserMatchingModeEnum.EmailLink}
|
||||
?selected=${this.instance?.userMatchingMode ===
|
||||
UserMatchingModeEnum.EmailLink}
|
||||
>
|
||||
${t`Link to a user with identical username address. Can have security implications when a username is used with another source.`}
|
||||
</option>
|
||||
<option value=${UserMatchingModeEnum.EmailDeny} ?selected=${this.instance?.userMatchingMode === UserMatchingModeEnum.EmailDeny}>
|
||||
<option
|
||||
value=${UserMatchingModeEnum.EmailDeny}
|
||||
?selected=${this.instance?.userMatchingMode ===
|
||||
UserMatchingModeEnum.EmailDeny}
|
||||
>
|
||||
${t`Use the user's username, but deny enrollment when the username already exists.`}
|
||||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Protocol settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Protocol settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Client ID`}
|
||||
?required=${true}
|
||||
name="clientId">
|
||||
<input type="text" value="${first(this.instance?.clientId)}" class="pf-c-form-control" required>
|
||||
name="clientId"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.clientId)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
${this.renderSettings()}
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group>
|
||||
<span slot="header">
|
||||
${t`Flow settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Flow settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Authentication flow`}
|
||||
?required=${true}
|
||||
name="authenticationFlow">
|
||||
name="authenticationFlow"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authentication,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
let selected = this.instance?.authenticationFlow === flow.pk;
|
||||
if (!this.instance?.pk && !this.instance?.authenticationFlow && flow.slug === "default-source-authentication") {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation:
|
||||
FlowsInstancesListDesignationEnum.Authentication,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
let selected =
|
||||
this.instance?.authenticationFlow === flow.pk;
|
||||
if (
|
||||
!this.instance?.pk &&
|
||||
!this.instance?.authenticationFlow &&
|
||||
flow.slug === "default-source-authentication"
|
||||
) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow to use when authenticating existing users.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow to use when authenticating existing users.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Enrollment flow`}
|
||||
?required=${true}
|
||||
name="enrollmentFlow">
|
||||
name="enrollmentFlow"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Enrollment,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
let selected = this.instance?.enrollmentFlow === flow.pk;
|
||||
if (!this.instance?.pk && !this.instance?.enrollmentFlow && flow.slug === "default-source-enrollment") {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Enrollment,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
let selected =
|
||||
this.instance?.enrollmentFlow === flow.pk;
|
||||
if (
|
||||
!this.instance?.pk &&
|
||||
!this.instance?.enrollmentFlow &&
|
||||
flow.slug === "default-source-enrollment"
|
||||
) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow to use when enrolling new users.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow to use when enrolling new users.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -26,21 +26,34 @@ import { EVENT_REFRESH } from "../../../constants";
|
||||
|
||||
@customElement("ak-source-plex-view")
|
||||
export class PlexSourceViewPage extends LitElement {
|
||||
|
||||
@property({ type: String })
|
||||
set sourceSlug(value: string) {
|
||||
new SourcesApi(DEFAULT_CONFIG).sourcesPlexRetrieve({
|
||||
slug: value
|
||||
}).then((source) => {
|
||||
this.source = source;
|
||||
});
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesPlexRetrieve({
|
||||
slug: value,
|
||||
})
|
||||
.then((source) => {
|
||||
this.source = source;
|
||||
});
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
source?: PlexSource;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFPage, PFButton, PFFlex, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, AKGlobal];
|
||||
return [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFButton,
|
||||
PFFlex,
|
||||
PFDisplay,
|
||||
PFGallery,
|
||||
PFContent,
|
||||
PFCard,
|
||||
PFDescriptionList,
|
||||
PFSizing,
|
||||
AKGlobal,
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -56,64 +69,80 @@ export class PlexSourceViewPage extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
return html`<ak-tabs>
|
||||
<section slot="page-overview" data-tab-title="${t`Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<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`Name`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.source.name}</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update Plex Source`}
|
||||
</span>
|
||||
<ak-source-plex-form
|
||||
slot="form"
|
||||
.instancePk=${this.source.slug}>
|
||||
</ak-source-plex-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
<section
|
||||
slot="page-overview"
|
||||
data-tab-title="${t`Overview`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<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`Name`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.source.name}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Plex Source`} </span>
|
||||
<ak-source-plex-form
|
||||
slot="form"
|
||||
.instancePk=${this.source.slug}
|
||||
>
|
||||
</ak-source-plex-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section slot="page-changelog" data-tab-title="${t`Changelog`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.source.pk || ""}
|
||||
targetModelApp="authentik_sources_plex"
|
||||
targetModelName="plexsource">
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div slot="page-policy-binding" data-tab-title="${t`Policy Bindings`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`These bindings control which users can access this source.
|
||||
You can only use policies here as access is checked before the user is authenticated.`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-bound-policies-list .target=${this.source.pk} ?policyOnly=${true}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-changelog"
|
||||
data-tab-title="${t`Changelog`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.source.pk || ""}
|
||||
targetModelApp="authentik_sources_plex"
|
||||
targetModelName="plexsource"
|
||||
>
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>`;
|
||||
</section>
|
||||
<div
|
||||
slot="page-policy-binding"
|
||||
data-tab-title="${t`Policy Bindings`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">
|
||||
${t`These bindings control which users can access this source.
|
||||
You can only use policies here as access is checked before the user is authenticated.`}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-bound-policies-list .target=${this.source.pk} ?policyOnly=${true}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,14 @@
|
||||
import { SAMLSource, SourcesApi, BindingTypeEnum, NameIdPolicyEnum, CryptoApi, DigestAlgorithmEnum, SignatureAlgorithmEnum, FlowsApi, FlowsInstancesListDesignationEnum } from "authentik-api";
|
||||
import {
|
||||
SAMLSource,
|
||||
SourcesApi,
|
||||
BindingTypeEnum,
|
||||
NameIdPolicyEnum,
|
||||
CryptoApi,
|
||||
DigestAlgorithmEnum,
|
||||
SignatureAlgorithmEnum,
|
||||
FlowsApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -12,7 +22,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-source-saml-form")
|
||||
export class SAMLSourceForm extends ModelForm<SAMLSource, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<SAMLSource> {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesSamlRetrieve({
|
||||
slug: pk,
|
||||
@ -31,127 +40,190 @@ export class SAMLSourceForm extends ModelForm<SAMLSource, string> {
|
||||
if (this.instance) {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesSamlUpdate({
|
||||
slug: this.instance.slug,
|
||||
sAMLSourceRequest: data
|
||||
sAMLSourceRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesSamlCreate({
|
||||
sAMLSourceRequest: data
|
||||
sAMLSourceRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Slug`}
|
||||
?required=${true}
|
||||
name="slug">
|
||||
<input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Slug`} ?required=${true} name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="enabled">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.enabled, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Enabled`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.enabled, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Enabled`} </label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Protocol settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Protocol settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`SSO URL`}
|
||||
?required=${true}
|
||||
name="ssoUrl">
|
||||
<input type="text" value="${ifDefined(this.instance?.ssoUrl)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`URL that the initial Login request is sent to.`}</p>
|
||||
<ak-form-element-horizontal label=${t`SSO URL`} ?required=${true} name="ssoUrl">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.ssoUrl)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`URL that the initial Login request is sent to.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`SLO URL`}
|
||||
name="sloUrl">
|
||||
<input type="text" value="${ifDefined(this.instance?.sloUrl || "")}" class="pf-c-form-control">
|
||||
<p class="pf-c-form__helper-text">${t`Optional URL if the IDP supports Single-Logout.`}</p>
|
||||
<ak-form-element-horizontal label=${t`SLO URL`} name="sloUrl">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.sloUrl || "")}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Optional URL if the IDP supports Single-Logout.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Issuer`}
|
||||
name="issuer">
|
||||
<input type="text" value="${ifDefined(this.instance?.issuer)}" class="pf-c-form-control">
|
||||
<p class="pf-c-form__helper-text">${t`Also known as Entity ID. Defaults the Metadata URL.`}</p>
|
||||
<ak-form-element-horizontal label=${t`Issuer`} name="issuer">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.issuer)}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Also known as Entity ID. Defaults the Metadata URL.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Binding Type`}
|
||||
?required=${true}
|
||||
name="bindingType">
|
||||
name="bindingType"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${BindingTypeEnum.Redirect} ?selected=${this.instance?.bindingType === BindingTypeEnum.Redirect}>
|
||||
<option
|
||||
value=${BindingTypeEnum.Redirect}
|
||||
?selected=${this.instance?.bindingType === BindingTypeEnum.Redirect}
|
||||
>
|
||||
${t`Redirect binding`}
|
||||
</option>
|
||||
<option value=${BindingTypeEnum.PostAuto} ?selected=${this.instance?.bindingType === BindingTypeEnum.PostAuto}>
|
||||
<option
|
||||
value=${BindingTypeEnum.PostAuto}
|
||||
?selected=${this.instance?.bindingType === BindingTypeEnum.PostAuto}
|
||||
>
|
||||
${t`Post binding (auto-submit)`}
|
||||
</option>
|
||||
<option value=${BindingTypeEnum.Post} ?selected=${this.instance?.bindingType === BindingTypeEnum.Post}>
|
||||
<option
|
||||
value=${BindingTypeEnum.Post}
|
||||
?selected=${this.instance?.bindingType === BindingTypeEnum.Post}
|
||||
>
|
||||
${t`Post binding`}
|
||||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Signing keypair`}
|
||||
name="signingKp">
|
||||
<ak-form-element-horizontal label=${t`Signing keypair`} name="signingKp">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.signingKp === undefined}>---------</option>
|
||||
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
}).then(keys => {
|
||||
return keys.results.map(key => {
|
||||
return html`<option value=${ifDefined(key.pk)} ?selected=${this.instance?.signingKp === key.pk}>${key.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.signingKp === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new CryptoApi(DEFAULT_CONFIG)
|
||||
.cryptoCertificatekeypairsList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((keys) => {
|
||||
return keys.results.map((key) => {
|
||||
return html`<option
|
||||
value=${ifDefined(key.pk)}
|
||||
?selected=${this.instance?.signingKp === key.pk}
|
||||
>
|
||||
${key.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Keypair which is used to sign outgoing requests. Leave empty to disable signing.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Keypair which is used to sign outgoing requests. Leave empty to disable signing.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group>
|
||||
<span slot="header">
|
||||
${t`Advanced protocol settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Advanced protocol settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal name="allowIdpInitiated">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.allowIdpInitiated, false)}>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.allowIdpInitiated, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label">
|
||||
${t` Allow IDP-initiated logins`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Allows authentication flows initiated by the IdP. This can be a security risk, as no validation of the request ID is done.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Allows authentication flows initiated by the IdP. This can be a security risk, as no validation of the request ID is done.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`NameID Policy`}
|
||||
?required=${true}
|
||||
name="nameIdPolicy">
|
||||
name="nameIdPolicy"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${NameIdPolicyEnum._20nameidFormatpersistent} ?selected=${this.instance?.nameIdPolicy === NameIdPolicyEnum._20nameidFormatpersistent}>
|
||||
<option
|
||||
value=${NameIdPolicyEnum._20nameidFormatpersistent}
|
||||
?selected=${this.instance?.nameIdPolicy ===
|
||||
NameIdPolicyEnum._20nameidFormatpersistent}
|
||||
>
|
||||
${t`Persistent`}
|
||||
</option>
|
||||
<option value=${NameIdPolicyEnum._11nameidFormatemailAddress} ?selected=${this.instance?.nameIdPolicy === NameIdPolicyEnum._11nameidFormatemailAddress}>
|
||||
<option
|
||||
value=${NameIdPolicyEnum._11nameidFormatemailAddress}
|
||||
?selected=${this.instance?.nameIdPolicy ===
|
||||
NameIdPolicyEnum._11nameidFormatemailAddress}
|
||||
>
|
||||
${t`Email address`}
|
||||
</option>
|
||||
<option value=${NameIdPolicyEnum._20nameidFormatWindowsDomainQualifiedName} ?selected=${this.instance?.nameIdPolicy === NameIdPolicyEnum._20nameidFormatWindowsDomainQualifiedName}>
|
||||
<option
|
||||
value=${NameIdPolicyEnum._20nameidFormatWindowsDomainQualifiedName}
|
||||
?selected=${this.instance?.nameIdPolicy ===
|
||||
NameIdPolicyEnum._20nameidFormatWindowsDomainQualifiedName}
|
||||
>
|
||||
${t`Windows`}
|
||||
</option>
|
||||
<option value=${NameIdPolicyEnum._20nameidFormatX509SubjectName} ?selected=${this.instance?.nameIdPolicy === NameIdPolicyEnum._20nameidFormatX509SubjectName}>
|
||||
<option
|
||||
value=${NameIdPolicyEnum._20nameidFormatX509SubjectName}
|
||||
?selected=${this.instance?.nameIdPolicy ===
|
||||
NameIdPolicyEnum._20nameidFormatX509SubjectName}
|
||||
>
|
||||
${t`X509 Subject`}
|
||||
</option>
|
||||
<option value=${NameIdPolicyEnum._20nameidFormattransient} ?selected=${this.instance?.nameIdPolicy === NameIdPolicyEnum._20nameidFormattransient}>
|
||||
<option
|
||||
value=${NameIdPolicyEnum._20nameidFormattransient}
|
||||
?selected=${this.instance?.nameIdPolicy ===
|
||||
NameIdPolicyEnum._20nameidFormattransient}
|
||||
>
|
||||
${t`Transient`}
|
||||
</option>
|
||||
</select>
|
||||
@ -159,25 +231,51 @@ export class SAMLSourceForm extends ModelForm<SAMLSource, string> {
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Delete temporary users after`}
|
||||
?required=${true}
|
||||
name="temporaryUserDeleteAfter">
|
||||
<input type="text" value="${this.instance?.temporaryUserDeleteAfter || "days=1"}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3).`}</p>
|
||||
name="temporaryUserDeleteAfter"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.temporaryUserDeleteAfter || "days=1"}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3).`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Digest algorithm`}
|
||||
?required=${true}
|
||||
name="digestAlgorithm">
|
||||
name="digestAlgorithm"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${DigestAlgorithmEnum._200009Xmldsigsha1} ?selected=${this.instance?.digestAlgorithm === DigestAlgorithmEnum._200009Xmldsigsha1}>
|
||||
<option
|
||||
value=${DigestAlgorithmEnum._200009Xmldsigsha1}
|
||||
?selected=${this.instance?.digestAlgorithm ===
|
||||
DigestAlgorithmEnum._200009Xmldsigsha1}
|
||||
>
|
||||
${t`SHA1`}
|
||||
</option>
|
||||
<option value=${DigestAlgorithmEnum._200104Xmlencsha256} ?selected=${this.instance?.digestAlgorithm === DigestAlgorithmEnum._200104Xmlencsha256 || this.instance?.digestAlgorithm === undefined}>
|
||||
<option
|
||||
value=${DigestAlgorithmEnum._200104Xmlencsha256}
|
||||
?selected=${this.instance?.digestAlgorithm ===
|
||||
DigestAlgorithmEnum._200104Xmlencsha256 ||
|
||||
this.instance?.digestAlgorithm === undefined}
|
||||
>
|
||||
${t`SHA256`}
|
||||
</option>
|
||||
<option value=${DigestAlgorithmEnum._200104XmldsigMoresha384} ?selected=${this.instance?.digestAlgorithm === DigestAlgorithmEnum._200104XmldsigMoresha384}>
|
||||
<option
|
||||
value=${DigestAlgorithmEnum._200104XmldsigMoresha384}
|
||||
?selected=${this.instance?.digestAlgorithm ===
|
||||
DigestAlgorithmEnum._200104XmldsigMoresha384}
|
||||
>
|
||||
${t`SHA384`}
|
||||
</option>
|
||||
<option value=${DigestAlgorithmEnum._200104Xmlencsha512} ?selected=${this.instance?.digestAlgorithm === DigestAlgorithmEnum._200104Xmlencsha512}>
|
||||
<option
|
||||
value=${DigestAlgorithmEnum._200104Xmlencsha512}
|
||||
?selected=${this.instance?.digestAlgorithm ===
|
||||
DigestAlgorithmEnum._200104Xmlencsha512}
|
||||
>
|
||||
${t`SHA512`}
|
||||
</option>
|
||||
</select>
|
||||
@ -185,21 +283,43 @@ export class SAMLSourceForm extends ModelForm<SAMLSource, string> {
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Signature algorithm`}
|
||||
?required=${true}
|
||||
name="signatureAlgorithm">
|
||||
name="signatureAlgorithm"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option value=${SignatureAlgorithmEnum._200009XmldsigrsaSha1} ?selected=${this.instance?.signatureAlgorithm === SignatureAlgorithmEnum._200009XmldsigrsaSha1}>
|
||||
<option
|
||||
value=${SignatureAlgorithmEnum._200009XmldsigrsaSha1}
|
||||
?selected=${this.instance?.signatureAlgorithm ===
|
||||
SignatureAlgorithmEnum._200009XmldsigrsaSha1}
|
||||
>
|
||||
${t`RSA-SHA1`}
|
||||
</option>
|
||||
<option value=${SignatureAlgorithmEnum._200104XmldsigMorersaSha256} ?selected=${this.instance?.signatureAlgorithm === SignatureAlgorithmEnum._200104XmldsigMorersaSha256 || this.instance?.signatureAlgorithm === undefined}>
|
||||
<option
|
||||
value=${SignatureAlgorithmEnum._200104XmldsigMorersaSha256}
|
||||
?selected=${this.instance?.signatureAlgorithm ===
|
||||
SignatureAlgorithmEnum._200104XmldsigMorersaSha256 ||
|
||||
this.instance?.signatureAlgorithm === undefined}
|
||||
>
|
||||
${t`RSA-SHA256`}
|
||||
</option>
|
||||
<option value=${SignatureAlgorithmEnum._200104XmldsigMorersaSha384} ?selected=${this.instance?.signatureAlgorithm === SignatureAlgorithmEnum._200104XmldsigMorersaSha384}>
|
||||
<option
|
||||
value=${SignatureAlgorithmEnum._200104XmldsigMorersaSha384}
|
||||
?selected=${this.instance?.signatureAlgorithm ===
|
||||
SignatureAlgorithmEnum._200104XmldsigMorersaSha384}
|
||||
>
|
||||
${t`RSA-SHA384`}
|
||||
</option>
|
||||
<option value=${SignatureAlgorithmEnum._200104XmldsigMorersaSha512} ?selected=${this.instance?.signatureAlgorithm === SignatureAlgorithmEnum._200104XmldsigMorersaSha512}>
|
||||
<option
|
||||
value=${SignatureAlgorithmEnum._200104XmldsigMorersaSha512}
|
||||
?selected=${this.instance?.signatureAlgorithm ===
|
||||
SignatureAlgorithmEnum._200104XmldsigMorersaSha512}
|
||||
>
|
||||
${t`RSA-SHA512`}
|
||||
</option>
|
||||
<option value=${SignatureAlgorithmEnum._200009XmldsigdsaSha1} ?selected=${this.instance?.signatureAlgorithm === SignatureAlgorithmEnum._200009XmldsigdsaSha1}>
|
||||
<option
|
||||
value=${SignatureAlgorithmEnum._200009XmldsigdsaSha1}
|
||||
?selected=${this.instance?.signatureAlgorithm ===
|
||||
SignatureAlgorithmEnum._200009XmldsigdsaSha1}
|
||||
>
|
||||
${t`DSA-SHA1`}
|
||||
</option>
|
||||
</select>
|
||||
@ -207,73 +327,124 @@ export class SAMLSourceForm extends ModelForm<SAMLSource, string> {
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group>
|
||||
<span slot="header">
|
||||
${t`Flow settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Flow settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Pre-authentication flow`}
|
||||
?required=${true}
|
||||
name="preAuthenticationFlow">
|
||||
name="preAuthenticationFlow"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.StageConfiguration,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
let selected = this.instance?.preAuthenticationFlow === flow.pk;
|
||||
if (!this.instance?.pk && !this.instance?.preAuthenticationFlow && flow.slug === "default-source-pre-authentication") {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation:
|
||||
FlowsInstancesListDesignationEnum.StageConfiguration,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
let selected =
|
||||
this.instance?.preAuthenticationFlow === flow.pk;
|
||||
if (
|
||||
!this.instance?.pk &&
|
||||
!this.instance?.preAuthenticationFlow &&
|
||||
flow.slug === "default-source-pre-authentication"
|
||||
) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow used before authentication.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Authentication flow`}
|
||||
?required=${true}
|
||||
name="authenticationFlow">
|
||||
name="authenticationFlow"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authentication,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
let selected = this.instance?.authenticationFlow === flow.pk;
|
||||
if (!this.instance?.pk && !this.instance?.authenticationFlow && flow.slug === "default-source-authentication") {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation:
|
||||
FlowsInstancesListDesignationEnum.Authentication,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
let selected =
|
||||
this.instance?.authenticationFlow === flow.pk;
|
||||
if (
|
||||
!this.instance?.pk &&
|
||||
!this.instance?.authenticationFlow &&
|
||||
flow.slug === "default-source-authentication"
|
||||
) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow to use when authenticating existing users.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow to use when authenticating existing users.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Enrollment flow`}
|
||||
?required=${true}
|
||||
name="enrollmentFlow">
|
||||
name="enrollmentFlow"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Enrollment,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
let selected = this.instance?.enrollmentFlow === flow.pk;
|
||||
if (!this.instance?.pk && !this.instance?.enrollmentFlow && flow.slug === "default-source-enrollment") {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Enrollment,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
let selected =
|
||||
this.instance?.enrollmentFlow === flow.pk;
|
||||
if (
|
||||
!this.instance?.pk &&
|
||||
!this.instance?.enrollmentFlow &&
|
||||
flow.slug === "default-source-enrollment"
|
||||
) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow to use when enrolling new users.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow to use when enrolling new users.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -28,21 +28,34 @@ import { ifDefined } from "lit-html/directives/if-defined";
|
||||
|
||||
@customElement("ak-source-saml-view")
|
||||
export class SAMLSourceViewPage extends LitElement {
|
||||
|
||||
@property({ type: String })
|
||||
set sourceSlug(slug: string) {
|
||||
new SourcesApi(DEFAULT_CONFIG).sourcesSamlRetrieve({
|
||||
slug: slug
|
||||
}).then((source) => {
|
||||
this.source = source;
|
||||
});
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesSamlRetrieve({
|
||||
slug: slug,
|
||||
})
|
||||
.then((source) => {
|
||||
this.source = source;
|
||||
});
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
source?: SAMLSource;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFPage, PFFlex, PFButton, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, AKGlobal];
|
||||
return [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFFlex,
|
||||
PFButton,
|
||||
PFDisplay,
|
||||
PFGallery,
|
||||
PFContent,
|
||||
PFCard,
|
||||
PFDescriptionList,
|
||||
PFSizing,
|
||||
AKGlobal,
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -58,111 +71,154 @@ export class SAMLSourceViewPage extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
return html`<ak-tabs>
|
||||
<section slot="page-overview" data-tab-title="${t`Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list pf-m-3-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`Name`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.source.name}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`SSO URL`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.source.ssoUrl}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`SLO URL`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.source.sloUrl}</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Issuer`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.source.issuer}</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update SAML Source`}
|
||||
</span>
|
||||
<ak-source-saml-form
|
||||
slot="form"
|
||||
.instancePk=${this.source.slug}>
|
||||
</ak-source-saml-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
<section
|
||||
slot="page-overview"
|
||||
data-tab-title="${t`Overview`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list pf-m-3-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`Name`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.source.name}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`SSO URL`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.source.ssoUrl}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`SLO URL`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.source.sloUrl}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Issuer`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.source.issuer}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section slot="page-changelog" data-tab-title="${t`Changelog`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.source.pk || ""}
|
||||
targetModelApp="authentik_sources_saml"
|
||||
targetModelName="samlsource">
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section slot="page-metadata" data-tab-title="${t`Metadata`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
${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__footer">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update SAML Source`} </span>
|
||||
<ak-source-saml-form
|
||||
slot="form"
|
||||
.instancePk=${this.source.slug}
|
||||
>
|
||||
</ak-source-saml-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div slot="page-policy-bindings" data-tab-title="${t`Policy Bindings`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`These bindings control which users can access this source.
|
||||
You can only use policies here as access is checked before the user is authenticated.`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-bound-policies-list .target=${this.source.pk} ?policyOnly=${true}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>`;
|
||||
</section>
|
||||
<section
|
||||
slot="page-changelog"
|
||||
data-tab-title="${t`Changelog`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-object-changelog
|
||||
targetModelPk=${this.source.pk || ""}
|
||||
targetModelApp="authentik_sources_saml"
|
||||
targetModelName="samlsource"
|
||||
>
|
||||
</ak-object-changelog>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-metadata"
|
||||
data-tab-title="${t`Metadata`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<div class="pf-c-card">
|
||||
${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>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div
|
||||
slot="page-policy-bindings"
|
||||
data-tab-title="${t`Policy Bindings`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">
|
||||
${t`These bindings control which users can access this source.
|
||||
You can only use policies here as access is checked before the user is authenticated.`}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-bound-policies-list .target=${this.source.pk} ?policyOnly=${true}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,11 +62,7 @@ export class StageListPage extends TablePage<Stage> {
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(t`Name`, "name"),
|
||||
new TableColumn(t`Flows`),
|
||||
new TableColumn(""),
|
||||
];
|
||||
return [new TableColumn(t`Name`, "name"), new TableColumn(t`Flows`), new TableColumn("")];
|
||||
}
|
||||
|
||||
row(item: Stage): TemplateResult[] {
|
||||
@ -80,78 +76,66 @@ export class StageListPage extends TablePage<Stage> {
|
||||
<code>${flow.slug}</code>
|
||||
</a>`;
|
||||
})}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Update`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Update ${item.verboseName}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
"instancePk": item.pk
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update ${item.verboseName}`} </span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
instancePk: item.pk,
|
||||
}}
|
||||
type=${ifDefined(item.component)}
|
||||
>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">${t`Edit`}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${item.verboseName || ""}
|
||||
.usedBy=${() => {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAllUsedByList({
|
||||
stageUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
type=${ifDefined(item.component)}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Edit`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${item.verboseName || ""}
|
||||
.usedBy=${() => {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAllUsedByList({
|
||||
stageUuid: item.pk
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAllDestroy({
|
||||
stageUuid: item.pk
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete>`,
|
||||
.delete=${() => {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAllDestroy({
|
||||
stageUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
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`Create`}</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(new StagesApi(DEFAULT_CONFIG).stagesAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create ${type.name}`}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
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`Create`}</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(
|
||||
new StagesApi(DEFAULT_CONFIG).stagesAllTypesList().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create ${type.name}`} </span>
|
||||
<ak-proxy-form slot="form" type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br />
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</li>`;
|
||||
});
|
||||
}),
|
||||
html`<ak-spinner></ak-spinner>`,
|
||||
)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { FlowsApi, AuthenticatorDuoStage, StagesApi, FlowsInstancesListDesignationEnum, AuthenticatorDuoStageRequest } from "authentik-api";
|
||||
import {
|
||||
FlowsApi,
|
||||
AuthenticatorDuoStage,
|
||||
StagesApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
AuthenticatorDuoStageRequest,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -12,7 +18,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-stage-authenticator-duo-form")
|
||||
export class AuthenticatorDuoStageForm extends ModelForm<AuthenticatorDuoStage, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<AuthenticatorDuoStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorDuoRetrieve({
|
||||
stageUuid: pk,
|
||||
@ -31,11 +36,11 @@ export class AuthenticatorDuoStageForm extends ModelForm<AuthenticatorDuoStage,
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorDuoPartialUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
patchedAuthenticatorDuoStageRequest: data
|
||||
patchedAuthenticatorDuoStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorDuoCreate({
|
||||
authenticatorDuoStageRequest: data as unknown as AuthenticatorDuoStageRequest
|
||||
authenticatorDuoStageRequest: data as unknown as AuthenticatorDuoStageRequest,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -45,53 +50,84 @@ export class AuthenticatorDuoStageForm extends ModelForm<AuthenticatorDuoStage,
|
||||
<div class="form-help-text">
|
||||
${t`Stage used to configure a duo-based authenticator. This stage should be used for configuration flows.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Stage-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Stage-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Integration key`}
|
||||
?required=${true}
|
||||
name="clientId">
|
||||
<input type="text" value="${first(this.instance?.clientId, "")}" class="pf-c-form-control" required>
|
||||
name="clientId"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.clientId, "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Secret key`}
|
||||
?required=${true}
|
||||
?writeOnly=${this.instance !== undefined}
|
||||
name="clientSecret">
|
||||
<input type="text" value="" class="pf-c-form-control" required>
|
||||
name="clientSecret"
|
||||
>
|
||||
<input type="text" value="" class="pf-c-form-control" required />
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`API Hostname`}
|
||||
?required=${true}
|
||||
name="apiHostname">
|
||||
<input type="text" value="${first(this.instance?.apiHostname, "")}" class="pf-c-form-control" required>
|
||||
name="apiHostname"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.apiHostname, "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Configuration flow`}
|
||||
name="configureFlow">
|
||||
<ak-form-element-horizontal label=${t`Configuration flow`} name="configureFlow">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.configureFlow === undefined}>---------</option>
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.StageConfiguration,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
let selected = this.instance?.configureFlow === flow.pk;
|
||||
if (!this.instance?.pk && !this.instance?.configureFlow && flow.slug === "default-otp-time-configure") {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option
|
||||
value=""
|
||||
?selected=${this.instance?.configureFlow === undefined}
|
||||
>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation:
|
||||
FlowsInstancesListDesignationEnum.StageConfiguration,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
let selected = this.instance?.configureFlow === flow.pk;
|
||||
if (
|
||||
!this.instance?.pk &&
|
||||
!this.instance?.configureFlow &&
|
||||
flow.slug === "default-otp-time-configure"
|
||||
) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage.`}
|
||||
@ -101,5 +137,4 @@ export class AuthenticatorDuoStageForm extends ModelForm<AuthenticatorDuoStage,
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
import { FlowsApi, AuthenticatorStaticStage, StagesApi, FlowsInstancesListDesignationEnum } from "authentik-api";
|
||||
import {
|
||||
FlowsApi,
|
||||
AuthenticatorStaticStage,
|
||||
StagesApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -12,7 +17,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-stage-authenticator-static-form")
|
||||
export class AuthenticatorStaticStageForm extends ModelForm<AuthenticatorStaticStage, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<AuthenticatorStaticStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorStaticRetrieve({
|
||||
stageUuid: pk,
|
||||
@ -31,11 +35,11 @@ export class AuthenticatorStaticStageForm extends ModelForm<AuthenticatorStaticS
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorStaticUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
authenticatorStaticStageRequest: data
|
||||
authenticatorStaticStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorStaticCreate({
|
||||
authenticatorStaticStageRequest: data
|
||||
authenticatorStaticStageRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -45,40 +49,64 @@ export class AuthenticatorStaticStageForm extends ModelForm<AuthenticatorStaticS
|
||||
<div class="form-help-text">
|
||||
${t`Stage used to configure a static authenticator (i.e. static tokens). This stage should be used for configuration flows.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Stage-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Stage-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Token count`}
|
||||
?required=${true}
|
||||
name="tokenCount">
|
||||
<input type="text" value="${first(this.instance?.tokenCount, 6)}" class="pf-c-form-control" required>
|
||||
name="tokenCount"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.tokenCount, 6)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Configuration flow`}
|
||||
name="configureFlow">
|
||||
<ak-form-element-horizontal label=${t`Configuration flow`} name="configureFlow">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.configureFlow === undefined}>---------</option>
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.StageConfiguration,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
let selected = this.instance?.configureFlow === flow.pk;
|
||||
if (!this.instance?.pk && !this.instance?.configureFlow && flow.slug === "default-otp-time-configure") {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option
|
||||
value=""
|
||||
?selected=${this.instance?.configureFlow === undefined}
|
||||
>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation:
|
||||
FlowsInstancesListDesignationEnum.StageConfiguration,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
let selected = this.instance?.configureFlow === flow.pk;
|
||||
if (
|
||||
!this.instance?.pk &&
|
||||
!this.instance?.configureFlow &&
|
||||
flow.slug === "default-otp-time-configure"
|
||||
) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage.`}
|
||||
@ -88,5 +116,4 @@ export class AuthenticatorStaticStageForm extends ModelForm<AuthenticatorStaticS
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
import { FlowsApi, AuthenticatorTOTPStage, StagesApi, FlowsInstancesListDesignationEnum } from "authentik-api";
|
||||
import {
|
||||
FlowsApi,
|
||||
AuthenticatorTOTPStage,
|
||||
StagesApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -11,7 +16,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-stage-authenticator-totp-form")
|
||||
export class AuthenticatorTOTPStageForm extends ModelForm<AuthenticatorTOTPStage, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<AuthenticatorTOTPStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorTotpRetrieve({
|
||||
stageUuid: pk,
|
||||
@ -30,11 +34,11 @@ export class AuthenticatorTOTPStageForm extends ModelForm<AuthenticatorTOTPStage
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorTotpUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
authenticatorTOTPStageRequest: data
|
||||
authenticatorTOTPStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorTotpCreate({
|
||||
authenticatorTOTPStageRequest: data
|
||||
authenticatorTOTPStageRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -44,21 +48,18 @@ export class AuthenticatorTOTPStageForm extends ModelForm<AuthenticatorTOTPStage
|
||||
<div class="form-help-text">
|
||||
${t`Stage used to configure a TOTP authenticator (i.e. Authy/Google Authenticator).`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Stage-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Stage-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Digits`}
|
||||
?required=${true}
|
||||
name="digits">
|
||||
<ak-form-element-horizontal label=${t`Digits`} ?required=${true} name="digits">
|
||||
<select name="users" class="pf-c-form-control">
|
||||
<option value="6" ?selected=${this.instance?.digits === 6}>
|
||||
${t`6 digits, widely compatible`}
|
||||
@ -68,29 +69,48 @@ export class AuthenticatorTOTPStageForm extends ModelForm<AuthenticatorTOTPStage
|
||||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Configuration flow`}
|
||||
name="configureFlow">
|
||||
<ak-form-element-horizontal label=${t`Configuration flow`} name="configureFlow">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.configureFlow === undefined}>---------</option>
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.StageConfiguration,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
let selected = this.instance?.configureFlow === flow.pk;
|
||||
if (!this.instance?.pk && !this.instance?.configureFlow && flow.slug === "default-otp-time-configure") {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option
|
||||
value=""
|
||||
?selected=${this.instance?.configureFlow === undefined}
|
||||
>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation:
|
||||
FlowsInstancesListDesignationEnum.StageConfiguration,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
let selected = this.instance?.configureFlow === flow.pk;
|
||||
if (
|
||||
!this.instance?.pk &&
|
||||
!this.instance?.configureFlow &&
|
||||
flow.slug === "default-otp-time-configure"
|
||||
) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
import { AuthenticatorValidateStage, NotConfiguredActionEnum, DeviceClassesEnum, StagesApi } from "authentik-api";
|
||||
import {
|
||||
AuthenticatorValidateStage,
|
||||
NotConfiguredActionEnum,
|
||||
DeviceClassesEnum,
|
||||
StagesApi,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -11,14 +16,16 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-stage-authenticator-validate-form")
|
||||
export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValidateStage, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<AuthenticatorValidateStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorValidateRetrieve({
|
||||
stageUuid: pk,
|
||||
}).then(stage => {
|
||||
this.showConfigurationStage = stage.notConfiguredAction === NotConfiguredActionEnum.Configure;
|
||||
return stage;
|
||||
});
|
||||
return new StagesApi(DEFAULT_CONFIG)
|
||||
.stagesAuthenticatorValidateRetrieve({
|
||||
stageUuid: pk,
|
||||
})
|
||||
.then((stage) => {
|
||||
this.showConfigurationStage =
|
||||
stage.notConfiguredAction === NotConfiguredActionEnum.Configure;
|
||||
return stage;
|
||||
});
|
||||
}
|
||||
|
||||
@property({ type: Boolean })
|
||||
@ -36,19 +43,21 @@ export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValid
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorValidateUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
authenticatorValidateStageRequest: data
|
||||
authenticatorValidateStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorValidateCreate({
|
||||
authenticatorValidateStageRequest: data
|
||||
authenticatorValidateStageRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
isDeviceClassSelected(field: DeviceClassesEnum): boolean {
|
||||
return (this.instance?.deviceClasses || []).filter(isField => {
|
||||
return field === isField;
|
||||
}).length > 0;
|
||||
return (
|
||||
(this.instance?.deviceClasses || []).filter((isField) => {
|
||||
return field === isField;
|
||||
}).length > 0
|
||||
);
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
@ -56,83 +65,141 @@ export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValid
|
||||
<div class="form-help-text">
|
||||
${t`Stage used to validate any authenticator. This stage should be used during authentication or authorization flows.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Stage-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Stage-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Device classes`}
|
||||
?required=${true}
|
||||
name="deviceClasses">
|
||||
name="deviceClasses"
|
||||
>
|
||||
<select name="users" class="pf-c-form-control" multiple>
|
||||
<option value=${DeviceClassesEnum.Static} ?selected=${this.isDeviceClassSelected(DeviceClassesEnum.Static)}>
|
||||
<option
|
||||
value=${DeviceClassesEnum.Static}
|
||||
?selected=${this.isDeviceClassSelected(DeviceClassesEnum.Static)}
|
||||
>
|
||||
${t`Static Tokens`}
|
||||
</option>
|
||||
<option value=${DeviceClassesEnum.Totp} ?selected=${this.isDeviceClassSelected(DeviceClassesEnum.Totp)}>
|
||||
<option
|
||||
value=${DeviceClassesEnum.Totp}
|
||||
?selected=${this.isDeviceClassSelected(DeviceClassesEnum.Totp)}
|
||||
>
|
||||
${t`TOTP Authenticators`}
|
||||
</option>
|
||||
<option value=${DeviceClassesEnum.Webauthn} ?selected=${this.isDeviceClassSelected(DeviceClassesEnum.Webauthn)}>
|
||||
<option
|
||||
value=${DeviceClassesEnum.Webauthn}
|
||||
?selected=${this.isDeviceClassSelected(DeviceClassesEnum.Webauthn)}
|
||||
>
|
||||
${t`WebAuthn Authenticators`}
|
||||
</option>
|
||||
<option value=${DeviceClassesEnum.Duo} ?selected=${this.isDeviceClassSelected(DeviceClassesEnum.Duo)}>
|
||||
<option
|
||||
value=${DeviceClassesEnum.Duo}
|
||||
?selected=${this.isDeviceClassSelected(DeviceClassesEnum.Duo)}
|
||||
>
|
||||
${t`Duo Authenticators`}
|
||||
</option>
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Device classes which can be used to authenticate.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Device classes which can be used to authenticate.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Hold control/command to select multiple items.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Not configured action`}
|
||||
?required=${true}
|
||||
name="notConfiguredAction">
|
||||
<select class="pf-c-form-control" @change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLSelectElement;
|
||||
if (target.selectedOptions[0].value === NotConfiguredActionEnum.Configure) {
|
||||
this.showConfigurationStage = true;
|
||||
} else {
|
||||
this.showConfigurationStage = false;
|
||||
}
|
||||
}}>
|
||||
<option value=${NotConfiguredActionEnum.Configure} ?selected=${this.instance?.notConfiguredAction === NotConfiguredActionEnum.Configure}>
|
||||
name="notConfiguredAction"
|
||||
>
|
||||
<select
|
||||
class="pf-c-form-control"
|
||||
@change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLSelectElement;
|
||||
if (
|
||||
target.selectedOptions[0].value ===
|
||||
NotConfiguredActionEnum.Configure
|
||||
) {
|
||||
this.showConfigurationStage = true;
|
||||
} else {
|
||||
this.showConfigurationStage = false;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<option
|
||||
value=${NotConfiguredActionEnum.Configure}
|
||||
?selected=${this.instance?.notConfiguredAction ===
|
||||
NotConfiguredActionEnum.Configure}
|
||||
>
|
||||
${t`Force the user to configure an authenticator`}
|
||||
</option>
|
||||
<option value=${NotConfiguredActionEnum.Deny} ?selected=${this.instance?.notConfiguredAction === NotConfiguredActionEnum.Deny}>
|
||||
<option
|
||||
value=${NotConfiguredActionEnum.Deny}
|
||||
?selected=${this.instance?.notConfiguredAction ===
|
||||
NotConfiguredActionEnum.Deny}
|
||||
>
|
||||
${t`Deny the user access`}
|
||||
</option>
|
||||
<option value=${NotConfiguredActionEnum.Skip} ?selected=${this.instance?.notConfiguredAction === NotConfiguredActionEnum.Skip}>
|
||||
<option
|
||||
value=${NotConfiguredActionEnum.Skip}
|
||||
?selected=${this.instance?.notConfiguredAction ===
|
||||
NotConfiguredActionEnum.Skip}
|
||||
>
|
||||
${t`Continue`}
|
||||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
${this.showConfigurationStage ? html`
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Configuration stage`}
|
||||
?required=${true}
|
||||
name="configurationStage">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.configurationStage === undefined}>---------</option>
|
||||
${until(new StagesApi(DEFAULT_CONFIG).stagesAllList({
|
||||
ordering: "pk",
|
||||
}).then(stages => {
|
||||
return stages.results.map(stage => {
|
||||
const selected = this.instance?.configurationStage === stage.pk;
|
||||
return html`<option value=${ifDefined(stage.pk)} ?selected=${selected}>${stage.name} (${stage.verboseName})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
`: html``}
|
||||
${this.showConfigurationStage
|
||||
? html`
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Configuration stage`}
|
||||
?required=${true}
|
||||
name="configurationStage"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option
|
||||
value=""
|
||||
?selected=${this.instance?.configurationStage ===
|
||||
undefined}
|
||||
>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new StagesApi(DEFAULT_CONFIG)
|
||||
.stagesAllList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((stages) => {
|
||||
return stages.results.map((stage) => {
|
||||
const selected =
|
||||
this.instance?.configurationStage ===
|
||||
stage.pk;
|
||||
return html`<option
|
||||
value=${ifDefined(stage.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${stage.name} (${stage.verboseName})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
`
|
||||
: html``}
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-stage-authenticator-webauthn-form")
|
||||
export class AuthenticateWebAuthnStageForm extends ModelForm<AuthenticateWebAuthnStage, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<AuthenticateWebAuthnStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorWebauthnRetrieve({
|
||||
stageUuid: pk,
|
||||
@ -28,11 +27,11 @@ export class AuthenticateWebAuthnStageForm extends ModelForm<AuthenticateWebAuth
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorWebauthnUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
authenticateWebAuthnStageRequest: data
|
||||
authenticateWebAuthnStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorWebauthnCreate({
|
||||
authenticateWebAuthnStageRequest: data
|
||||
authenticateWebAuthnStageRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -42,13 +41,14 @@ export class AuthenticateWebAuthnStageForm extends ModelForm<AuthenticateWebAuth
|
||||
<div class="form-help-text">
|
||||
${t`Stage used to configure a WebAutnn authenticator (i.e. Yubikey, FaceID/Windows Hello).`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-stage-captcha-form")
|
||||
export class CaptchaStageForm extends ModelForm<CaptchaStage, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<CaptchaStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesCaptchaRetrieve({
|
||||
stageUuid: pk,
|
||||
@ -29,11 +28,11 @@ export class CaptchaStageForm extends ModelForm<CaptchaStage, string> {
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesCaptchaPartialUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
patchedCaptchaStageRequest: data
|
||||
patchedCaptchaStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesCaptchaCreate({
|
||||
captchaStageRequest: data as unknown as CaptchaStageRequest
|
||||
captchaStageRequest: data as unknown as CaptchaStageRequest,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -43,35 +42,45 @@ export class CaptchaStageForm extends ModelForm<CaptchaStage, string> {
|
||||
<div class="form-help-text">
|
||||
${t`This stage checks the user's current session against the Google reCaptcha service.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Stage-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Stage-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Public Key`}
|
||||
?required=${true}
|
||||
name="publicKey">
|
||||
<input type="text" value="${ifDefined(this.instance?.publicKey || "")}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Public key, acquired from https://www.google.com/recaptcha/intro/v3.html.`}</p>
|
||||
name="publicKey"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.publicKey || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Public key, acquired from https://www.google.com/recaptcha/intro/v3.html.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Private Key`}
|
||||
?required=${true}
|
||||
?writeOnly=${this.instance !== undefined}
|
||||
name="privateKey">
|
||||
<input type="text" value="" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Private key, acquired from https://www.google.com/recaptcha/intro/v3.html.`}</p>
|
||||
name="privateKey"
|
||||
>
|
||||
<input type="text" value="" class="pf-c-form-control" required />
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Private key, acquired from https://www.google.com/recaptcha/intro/v3.html.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,17 +10,18 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-stage-consent-form")
|
||||
export class ConsentStageForm extends ModelForm<ConsentStage, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<ConsentStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesConsentRetrieve({
|
||||
stageUuid: pk,
|
||||
}).then(stage => {
|
||||
this.showExpiresIn = stage.mode === ConsentStageModeEnum.Expiring;
|
||||
return stage;
|
||||
});
|
||||
return new StagesApi(DEFAULT_CONFIG)
|
||||
.stagesConsentRetrieve({
|
||||
stageUuid: pk,
|
||||
})
|
||||
.then((stage) => {
|
||||
this.showExpiresIn = stage.mode === ConsentStageModeEnum.Expiring;
|
||||
return stage;
|
||||
});
|
||||
}
|
||||
|
||||
@property({type: Boolean})
|
||||
@property({ type: Boolean })
|
||||
showExpiresIn = false;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
@ -35,11 +36,11 @@ export class ConsentStageForm extends ModelForm<ConsentStage, string> {
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesConsentUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
consentStageRequest: data
|
||||
consentStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesConsentCreate({
|
||||
consentStageRequest: data
|
||||
consentStageRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -49,36 +50,49 @@ export class ConsentStageForm extends ModelForm<ConsentStage, string> {
|
||||
<div class="form-help-text">
|
||||
${t`Prompt for the user's consent. The consent can either be permanent or expire in a defined amount of time.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Stage-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Stage-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Mode`}
|
||||
?required=${true}
|
||||
name="mode">
|
||||
<select class="pf-c-form-control" @change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLSelectElement;
|
||||
if (target.selectedOptions[0].value === ConsentStageModeEnum.Expiring) {
|
||||
this.showExpiresIn = true;
|
||||
} else {
|
||||
this.showExpiresIn = false;
|
||||
}
|
||||
}}>
|
||||
<option value=${ConsentStageModeEnum.AlwaysRequire} ?selected=${this.instance?.mode === ConsentStageModeEnum.AlwaysRequire}>
|
||||
<ak-form-element-horizontal label=${t`Mode`} ?required=${true} name="mode">
|
||||
<select
|
||||
class="pf-c-form-control"
|
||||
@change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLSelectElement;
|
||||
if (
|
||||
target.selectedOptions[0].value ===
|
||||
ConsentStageModeEnum.Expiring
|
||||
) {
|
||||
this.showExpiresIn = true;
|
||||
} else {
|
||||
this.showExpiresIn = false;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<option
|
||||
value=${ConsentStageModeEnum.AlwaysRequire}
|
||||
?selected=${this.instance?.mode ===
|
||||
ConsentStageModeEnum.AlwaysRequire}
|
||||
>
|
||||
${t`Always require consent`}
|
||||
</option>
|
||||
<option value=${ConsentStageModeEnum.Permanent} ?selected=${this.instance?.mode === ConsentStageModeEnum.Permanent}>
|
||||
<option
|
||||
value=${ConsentStageModeEnum.Permanent}
|
||||
?selected=${this.instance?.mode === ConsentStageModeEnum.Permanent}
|
||||
>
|
||||
${t`Consent given last indefinitely`}
|
||||
</option>
|
||||
<option value=${ConsentStageModeEnum.Expiring} ?selected=${this.instance?.mode === ConsentStageModeEnum.Expiring}>
|
||||
<option
|
||||
value=${ConsentStageModeEnum.Expiring}
|
||||
?selected=${this.instance?.mode === ConsentStageModeEnum.Expiring}
|
||||
>
|
||||
${t`Consent expires.`}
|
||||
</option>
|
||||
</select>
|
||||
@ -87,13 +101,20 @@ export class ConsentStageForm extends ModelForm<ConsentStage, string> {
|
||||
?hidden=${!this.showExpiresIn}
|
||||
label=${t`Consent expires in`}
|
||||
?required=${true}
|
||||
name="consentExpireIn">
|
||||
<input type="text" value="${ifDefined(this.instance?.consentExpireIn || "weeks=4")}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3).`}</p>
|
||||
name="consentExpireIn"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.consentExpireIn || "weeks=4")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3).`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-stage-deny-form")
|
||||
export class DenyStageForm extends ModelForm<DenyStage, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<DenyStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesDenyRetrieve({
|
||||
stageUuid: pk,
|
||||
@ -28,11 +27,11 @@ export class DenyStageForm extends ModelForm<DenyStage, string> {
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesDenyUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
denyStageRequest: data
|
||||
denyStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesDenyCreate({
|
||||
denyStageRequest: data
|
||||
denyStageRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -42,13 +41,14 @@ export class DenyStageForm extends ModelForm<DenyStage, string> {
|
||||
<div class="form-help-text">
|
||||
${t`Statically deny the flow. To use this stage effectively, disable *Evaluate on plan* on the respective binding.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-stage-dummy-form")
|
||||
export class DummyStageForm extends ModelForm<DummyStage, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<DummyStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesDummyRetrieve({
|
||||
stageUuid: pk,
|
||||
@ -28,11 +27,11 @@ export class DummyStageForm extends ModelForm<DummyStage, string> {
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesDummyUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
dummyStageRequest: data
|
||||
dummyStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesDummyCreate({
|
||||
dummyStageRequest: data
|
||||
dummyStageRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -42,13 +41,14 @@ export class DummyStageForm extends ModelForm<DummyStage, string> {
|
||||
<div class="form-help-text">
|
||||
${t`Dummy stage used for testing. Shows a simple continue button and always passes.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -12,17 +12,18 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-stage-email-form")
|
||||
export class EmailStageForm extends ModelForm<EmailStage, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<EmailStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesEmailRetrieve({
|
||||
stageUuid: pk,
|
||||
}).then(stage => {
|
||||
this.showConnectionSettings = !stage.useGlobalSettings;
|
||||
return stage;
|
||||
});
|
||||
return new StagesApi(DEFAULT_CONFIG)
|
||||
.stagesEmailRetrieve({
|
||||
stageUuid: pk,
|
||||
})
|
||||
.then((stage) => {
|
||||
this.showConnectionSettings = !stage.useGlobalSettings;
|
||||
return stage;
|
||||
});
|
||||
}
|
||||
|
||||
@property({type: Boolean})
|
||||
@property({ type: Boolean })
|
||||
showConnectionSettings = false;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
@ -37,11 +38,11 @@ export class EmailStageForm extends ModelForm<EmailStage, string> {
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesEmailPartialUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
patchedEmailStageRequest: data
|
||||
patchedEmailStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesEmailCreate({
|
||||
emailStageRequest: data
|
||||
emailStageRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -51,63 +52,80 @@ export class EmailStageForm extends ModelForm<EmailStage, string> {
|
||||
return html``;
|
||||
}
|
||||
return html`<ak-form-group>
|
||||
<span slot="header">
|
||||
${t`Connection settings`}
|
||||
</span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`SMTP Host`}
|
||||
?required=${true}
|
||||
name="host">
|
||||
<input type="text" value="${ifDefined(this.instance?.host || "")}" class="pf-c-form-control" required>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`SMTP Port`}
|
||||
?required=${true}
|
||||
name="port">
|
||||
<input type="number" value="${first(this.instance?.port, 25)}" class="pf-c-form-control" required>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`SMTP Username`}
|
||||
name="username">
|
||||
<input type="text" value="${ifDefined(this.instance?.username || "")}" class="pf-c-form-control">
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`SMTP Password`}
|
||||
?writeOnly=${this.instance !== undefined}
|
||||
name="password">
|
||||
<input type="text" value="" class="pf-c-form-control">
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="useTls">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.useTls, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Use TLS`}
|
||||
</label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="useSsl">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.useSsl, false)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Use SSL`}
|
||||
</label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Timeout`}
|
||||
?required=${true}
|
||||
name="timeout">
|
||||
<input type="number" value="${first(this.instance?.timeout, 30)}" class="pf-c-form-control" required>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`From address`}
|
||||
?required=${true}
|
||||
name="fromAddress">
|
||||
<input type="text" value="${ifDefined(this.instance?.fromAddress || "system@authentik.local")}" class="pf-c-form-control" required>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>`;
|
||||
<span slot="header"> ${t`Connection settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal label=${t`SMTP Host`} ?required=${true} name="host">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.host || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${t`SMTP Port`} ?required=${true} name="port">
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.port, 25)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${t`SMTP Username`} name="username">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.username || "")}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`SMTP Password`}
|
||||
?writeOnly=${this.instance !== undefined}
|
||||
name="password"
|
||||
>
|
||||
<input type="text" value="" class="pf-c-form-control" />
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="useTls">
|
||||
<div class="pf-c-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.useTls, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Use TLS`} </label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="useSsl">
|
||||
<div class="pf-c-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.useSsl, false)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Use SSL`} </label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${t`Timeout`} ?required=${true} name="timeout">
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.timeout, 30)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`From address`}
|
||||
?required=${true}
|
||||
name="fromAddress"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.fromAddress || "system@authentik.local")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>`;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
@ -115,55 +133,84 @@ export class EmailStageForm extends ModelForm<EmailStage, string> {
|
||||
<div class="form-help-text">
|
||||
${t`Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Stage-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Stage-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal name="useGlobalSettings">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.useGlobalSettings, true)} @change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLInputElement;
|
||||
this.showConnectionSettings = !target.checked;
|
||||
}}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Use global settings`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.useGlobalSettings, true)}
|
||||
@change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLInputElement;
|
||||
this.showConnectionSettings = !target.checked;
|
||||
}}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Use global settings`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`When enabled, global Email connection settings will be used and connection settings below will be ignored.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When enabled, global Email connection settings will be used and connection settings below will be ignored.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Token expiry`}
|
||||
?required=${true}
|
||||
name="tokenExpiry">
|
||||
<input type="number" value="${first(this.instance?.tokenExpiry, 30)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Time in minutes the token sent is valid.`}</p>
|
||||
name="tokenExpiry"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.tokenExpiry, 30)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Time in minutes the token sent is valid.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Subject`}
|
||||
?required=${true}
|
||||
name="subject">
|
||||
<input type="text" value="${first(this.instance?.subject, "authentik")}" class="pf-c-form-control" required>
|
||||
name="subject"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.subject, "authentik")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Template`}
|
||||
?required=${true}
|
||||
name="template">
|
||||
name="template"
|
||||
>
|
||||
<select name="users" class="pf-c-form-control">
|
||||
${until(new StagesApi(DEFAULT_CONFIG).stagesEmailTemplatesList().then(templates => {
|
||||
return templates.map(template => {
|
||||
const selected = this.instance?.template === template.name;
|
||||
return html`<option value=${ifDefined(template.name)} ?selected=${selected}>
|
||||
${template.description}
|
||||
</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new StagesApi(DEFAULT_CONFIG)
|
||||
.stagesEmailTemplatesList()
|
||||
.then((templates) => {
|
||||
return templates.map((template) => {
|
||||
const selected =
|
||||
this.instance?.template === template.name;
|
||||
return html`<option
|
||||
value=${ifDefined(template.name)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${template.description}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
@ -171,5 +218,4 @@ export class EmailStageForm extends ModelForm<EmailStage, string> {
|
||||
${this.renderConnectionSettings()}
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
import { FlowsApi, IdentificationStage, UserFieldsEnum, StagesApi, FlowsInstancesListDesignationEnum, SourcesApi } from "authentik-api";
|
||||
import {
|
||||
FlowsApi,
|
||||
IdentificationStage,
|
||||
UserFieldsEnum,
|
||||
StagesApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
SourcesApi,
|
||||
} from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
@ -12,7 +19,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-stage-identification-form")
|
||||
export class IdentificationStageForm extends ModelForm<IdentificationStage, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<IdentificationStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesIdentificationRetrieve({
|
||||
stageUuid: pk,
|
||||
@ -31,19 +37,21 @@ export class IdentificationStageForm extends ModelForm<IdentificationStage, stri
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesIdentificationUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
identificationStageRequest: data
|
||||
identificationStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesIdentificationCreate({
|
||||
identificationStageRequest: data
|
||||
identificationStageRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
isUserFieldSelected(field: UserFieldsEnum): boolean {
|
||||
return (this.instance?.userFields || []).filter(isField => {
|
||||
return field === isField;
|
||||
}).length > 0;
|
||||
return (
|
||||
(this.instance?.userFields || []).filter((isField) => {
|
||||
return field === isField;
|
||||
}).length > 0
|
||||
);
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
@ -51,122 +59,204 @@ export class IdentificationStageForm extends ModelForm<IdentificationStage, stri
|
||||
<div class="form-help-text">
|
||||
${t`Let the user identify themselves with their username or Email address.`}
|
||||
</div>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Name`}
|
||||
?required=${true}
|
||||
name="name">
|
||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Stage-specific settings`}
|
||||
</span>
|
||||
<span slot="header"> ${t`Stage-specific settings`} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`User fields`}
|
||||
name="userFields">
|
||||
<ak-form-element-horizontal label=${t`User fields`} name="userFields">
|
||||
<select name="users" class="pf-c-form-control" multiple>
|
||||
<option value=${UserFieldsEnum.Username} ?selected=${this.isUserFieldSelected(UserFieldsEnum.Username)}>
|
||||
<option
|
||||
value=${UserFieldsEnum.Username}
|
||||
?selected=${this.isUserFieldSelected(UserFieldsEnum.Username)}
|
||||
>
|
||||
${t`Username`}
|
||||
</option>
|
||||
<option value=${UserFieldsEnum.Email} ?selected=${this.isUserFieldSelected(UserFieldsEnum.Email)}>
|
||||
<option
|
||||
value=${UserFieldsEnum.Email}
|
||||
?selected=${this.isUserFieldSelected(UserFieldsEnum.Email)}
|
||||
>
|
||||
${t`Email`}
|
||||
</option>
|
||||
<option value=${UserFieldsEnum.Upn} ?selected=${this.isUserFieldSelected(UserFieldsEnum.Upn)}>
|
||||
<option
|
||||
value=${UserFieldsEnum.Upn}
|
||||
?selected=${this.isUserFieldSelected(UserFieldsEnum.Upn)}
|
||||
>
|
||||
${t`UPN`}
|
||||
</option>
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Fields a user can identify themselves with. If no fields are selected, the user will only be able to use sources.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Fields a user can identify themselves with. If no fields are selected, the user will only be able to use sources.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Hold control/command to select multiple items.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Password stage`}
|
||||
name="passwordStage">
|
||||
<ak-form-element-horizontal label=${t`Password stage`} name="passwordStage">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.passwordStage === undefined}>---------</option>
|
||||
${until(new StagesApi(DEFAULT_CONFIG).stagesPasswordList({
|
||||
ordering: "pk",
|
||||
}).then(stages => {
|
||||
return stages.results.map(stage => {
|
||||
const selected = this.instance?.passwordStage === stage.pk;
|
||||
return html`<option value=${ifDefined(stage.pk)} ?selected=${selected}>${stage.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option
|
||||
value=""
|
||||
?selected=${this.instance?.passwordStage === undefined}
|
||||
>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new StagesApi(DEFAULT_CONFIG)
|
||||
.stagesPasswordList({
|
||||
ordering: "pk",
|
||||
})
|
||||
.then((stages) => {
|
||||
return stages.results.map((stage) => {
|
||||
const selected =
|
||||
this.instance?.passwordStage === stage.pk;
|
||||
return html`<option
|
||||
value=${ifDefined(stage.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${stage.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`When selected, a password field is shown on the same page instead of a separate page. This prevents username enumeration attacks.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When selected, a password field is shown on the same page instead of a separate page. This prevents username enumeration attacks.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="caseInsensitiveMatching">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.caseInsensitiveMatching, true)}>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.caseInsensitiveMatching, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Case insensitive matching`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`When enabled, user fields are matched regardless of their casing.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When enabled, user fields are matched regardless of their casing.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Sources`}
|
||||
?required=${true}
|
||||
name="sources">
|
||||
name="sources"
|
||||
>
|
||||
<select name="users" class="pf-c-form-control" multiple>
|
||||
${until(new SourcesApi(DEFAULT_CONFIG).sourcesAllList({}).then(sources => {
|
||||
return sources.results.map(source => {
|
||||
const selected = Array.from(this.instance?.sources || []).some(su => {
|
||||
return su == source.pk;
|
||||
});
|
||||
return html`<option value=${ifDefined(source.pk)} ?selected=${selected}>${source.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
${until(
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesAllList({})
|
||||
.then((sources) => {
|
||||
return sources.results.map((source) => {
|
||||
const selected = Array.from(
|
||||
this.instance?.sources || [],
|
||||
).some((su) => {
|
||||
return su == source.pk;
|
||||
});
|
||||
return html`<option
|
||||
value=${ifDefined(source.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${source.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Select sources should be shown for users to authenticate with. This only affects web-based sources, not LDAP.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Select sources should be shown for users to authenticate with. This only affects web-based sources, not LDAP.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Hold control/command to select multiple items.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="showMatchedUser">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.showMatchedUser, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Show matched user`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.showMatchedUser, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Show matched user`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`When a valid username/email has been entered, and this option is enabled, the user's username and avatar will be shown. Otherwise, the text that the user entered will be shown.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When a valid username/email has been entered, and this option is enabled, the user's username and avatar will be shown. Otherwise, the text that the user entered will be shown.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Enrollment flow`}
|
||||
name="enrollmentFlow">
|
||||
<ak-form-element-horizontal label=${t`Enrollment flow`} name="enrollmentFlow">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.enrollmentFlow === undefined}>---------</option>
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Enrollment,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
const selected = this.instance?.enrollmentFlow === flow.pk;
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option
|
||||
value=""
|
||||
?selected=${this.instance?.enrollmentFlow === undefined}
|
||||
>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Enrollment,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
const selected =
|
||||
this.instance?.enrollmentFlow === flow.pk;
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Optional enrollment flow, which is linked at the bottom of the page.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Optional enrollment flow, which is linked at the bottom of the page.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Recovery flow`}
|
||||
name="recoveryFlow">
|
||||
<ak-form-element-horizontal label=${t`Recovery flow`} name="recoveryFlow">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.recoveryFlow === undefined}>---------</option>
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Recovery,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
const selected = this.instance?.recoveryFlow === flow.pk;
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<option value="" ?selected=${this.instance?.recoveryFlow === undefined}>
|
||||
---------
|
||||
</option>
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Recovery,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
const selected =
|
||||
this.instance?.recoveryFlow === flow.pk;
|
||||
return html`<option
|
||||
value=${ifDefined(flow.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${flow.name} (${flow.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Optional recovery flow, which is linked at the bottom of the page.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Optional recovery flow, which is linked at the bottom of the page.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-invitation-form")
|
||||
export class InvitationForm extends ModelForm<Invitation, string> {
|
||||
|
||||
loadInstance(pk: string): Promise<Invitation> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesInvitationInvitationsRetrieve({
|
||||
inviteUuid: pk,
|
||||
@ -30,36 +29,38 @@ export class InvitationForm extends ModelForm<Invitation, string> {
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesInvitationInvitationsUpdate({
|
||||
inviteUuid: this.instance.pk || "",
|
||||
invitationRequest: data
|
||||
invitationRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesInvitationInvitationsCreate({
|
||||
invitationRequest: data
|
||||
invitationRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Expires`}
|
||||
?required=${true}
|
||||
name="expires">
|
||||
<input type="date" class="pf-c-form-control" required>
|
||||
<ak-form-element-horizontal label=${t`Expires`} ?required=${true} name="expires">
|
||||
<input type="date" class="pf-c-form-control" required />
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Attributes`}
|
||||
name="fixedData">
|
||||
<ak-codemirror mode="yaml" value="${YAML.stringify(first(this.instance?.fixedData, {}))}">
|
||||
<ak-form-element-horizontal label=${t`Attributes`} name="fixedData">
|
||||
<ak-codemirror
|
||||
mode="yaml"
|
||||
value="${YAML.stringify(first(this.instance?.fixedData, {}))}"
|
||||
>
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">${t`Optional data which is loaded into the flow's 'prompt_data' context variable. YAML or JSON.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Optional data which is loaded into the flow's 'prompt_data' context variable. YAML or JSON.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="singleUse">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.singleUse, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Single use`}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
?checked=${first(this.instance?.singleUse, true)}
|
||||
/>
|
||||
<label class="pf-c-check__label"> ${t`Single use`} </label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When enabled, the invitation will be deleted after usage.`}
|
||||
@ -67,5 +68,4 @@ export class InvitationForm extends ModelForm<Invitation, string> {
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -12,9 +12,8 @@ import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList
|
||||
|
||||
@customElement("ak-stage-invitation-list-link")
|
||||
export class InvitationListLink extends LitElement {
|
||||
|
||||
@property()
|
||||
invitation?: string
|
||||
invitation?: string;
|
||||
|
||||
@property()
|
||||
selectedFlow?: string;
|
||||
@ -35,33 +34,52 @@ export class InvitationListLink extends LitElement {
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<select class="pf-c-form-control" @change=${(ev: Event) => {
|
||||
const current = (ev.target as HTMLInputElement).value;
|
||||
this.selectedFlow = current;
|
||||
}}>
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Enrollment,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
return html`<option value=${flow.slug} ?selected=${flow.slug === this.selectedFlow}>${flow.slug}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
<select
|
||||
class="pf-c-form-control"
|
||||
@change=${(ev: Event) => {
|
||||
const current = (ev.target as HTMLInputElement).value;
|
||||
this.selectedFlow = current;
|
||||
}}
|
||||
>
|
||||
${until(
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Enrollment,
|
||||
})
|
||||
.then((flows) => {
|
||||
return flows.results.map((flow) => {
|
||||
return html`<option
|
||||
value=${flow.slug}
|
||||
?selected=${flow.slug === this.selectedFlow}
|
||||
>
|
||||
${flow.slug}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Link to use the invitation.`}</span>
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Link to use the invitation.`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<input class="pf-c-form-control" readonly type="text" value=${this.renderLink()} />
|
||||
<input
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
type="text"
|
||||
value=${this.renderLink()}
|
||||
/>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -57,55 +57,47 @@ export class InvitationListPage extends TablePage<Invitation> {
|
||||
html`${item.pk}`,
|
||||
html`${item.createdBy?.username}`,
|
||||
html`${item.expires?.toLocaleString() || "-"}`,
|
||||
html`
|
||||
<ak-forms-delete
|
||||
html` <ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${t`Invitation`}
|
||||
.usedBy=${() => {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesInvitationInvitationsUsedByList({
|
||||
inviteUuid: item.pk
|
||||
inviteUuid: item.pk,
|
||||
});
|
||||
}}
|
||||
.delete=${() => {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesInvitationInvitationsDestroy({
|
||||
inviteUuid: item.pk
|
||||
inviteUuid: item.pk,
|
||||
});
|
||||
}}>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: Invitation): TemplateResult {
|
||||
return html`
|
||||
<td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<ak-stage-invitation-list-link invitation=${item.pk}></ak-stage-invitation-list-link>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>`;
|
||||
return html` <td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<ak-stage-invitation-list-link
|
||||
invitation=${item.pk}
|
||||
></ak-stage-invitation-list-link>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>`;
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${t`Create`}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${t`Create Invitation`}
|
||||
</span>
|
||||
<ak-invitation-form slot="form">
|
||||
</ak-invitation-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${t`Create`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Invitation`} </span>
|
||||
<ak-invitation-form slot="form"> </ak-invitation-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${t`Create`}</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user