enterprise: initial enterprise (#5721)
* initial Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add user type Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add external users Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add ui, add more logic, add public JWT validation key Signed-off-by: Jens Langhammer <jens@goauthentik.io> * revert to not use install_id as session jwt signing key Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix more Signed-off-by: Jens Langhammer <jens@goauthentik.io> * switch to PKI Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add more licensing stuff Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add install ID to form Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix bugs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start adding tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fixes Signed-off-by: Jens Langhammer <jens@goauthentik.io> * use x5c correctly Signed-off-by: Jens Langhammer <jens@goauthentik.io> * license checks Signed-off-by: Jens Langhammer <jens@goauthentik.io> * use production CA Signed-off-by: Jens Langhammer <jens@goauthentik.io> * more Signed-off-by: Jens Langhammer <jens@goauthentik.io> * more UI stuff Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rename to summary Signed-off-by: Jens Langhammer <jens@goauthentik.io> * update locale, improve ui Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add direct button Signed-off-by: Jens Langhammer <jens@goauthentik.io> * update link Signed-off-by: Jens Langhammer <jens@goauthentik.io> * format and such Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove old attributes from ldap Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove is_enterprise_licensed Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix admin interface styling issue Signed-off-by: Jens Langhammer <jens@goauthentik.io> * Update authentik/core/models.py Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com> Signed-off-by: Jens L. <jens@beryju.org> * fix default case Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Signed-off-by: Jens L. <jens@beryju.org> Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
This commit is contained in:
@ -11,6 +11,7 @@ import { me } from "@goauthentik/common/users";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
import { Interface } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import "@goauthentik/elements/enterprise/EnterpriseStatusBanner";
|
||||
import "@goauthentik/elements/messages/MessageContainer";
|
||||
import "@goauthentik/elements/messages/MessageContainer";
|
||||
import "@goauthentik/elements/notifications/APIDrawer";
|
||||
@ -30,7 +31,14 @@ import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { AdminApi, CoreApi, SessionUser, UiThemeEnum, Version } from "@goauthentik/api";
|
||||
import {
|
||||
AdminApi,
|
||||
CapabilitiesEnum,
|
||||
CoreApi,
|
||||
SessionUser,
|
||||
UiThemeEnum,
|
||||
Version,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-interface-admin")
|
||||
export class AdminInterface extends Interface {
|
||||
@ -67,7 +75,17 @@ export class AdminInterface extends Interface {
|
||||
.display-none {
|
||||
display: none;
|
||||
}
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
ak-locale-context {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.pf-c-page {
|
||||
flex-grow: 1;
|
||||
background-color: var(--pf-c-page--BackgroundColor) !important;
|
||||
}
|
||||
/* Global page background colour */
|
||||
@ -113,7 +131,8 @@ export class AdminInterface extends Interface {
|
||||
|
||||
render(): TemplateResult {
|
||||
return html` <ak-locale-context
|
||||
><div class="pf-c-page">
|
||||
><ak-enterprise-status interface="admin"></ak-enterprise-status>
|
||||
<div class="pf-c-page">
|
||||
<ak-sidebar
|
||||
class="pf-c-page__sidebar ${this.sidebarOpen
|
||||
? "pf-m-expanded"
|
||||
@ -308,6 +327,16 @@ export class AdminInterface extends Interface {
|
||||
<span slot="label">${msg("Outpost Integrations")}</span>
|
||||
</ak-sidebar-item>
|
||||
</ak-sidebar-item>
|
||||
${this.config?.capabilities.includes(CapabilitiesEnum.IsEnterprise)
|
||||
? html`
|
||||
<ak-sidebar-item>
|
||||
<span slot="label">${msg("Enterprise")}</span>
|
||||
<ak-sidebar-item path="/enterprise/licenses">
|
||||
<span slot="label">${msg("Licenses")}</span>
|
||||
</ak-sidebar-item>
|
||||
</ak-sidebar-item>
|
||||
`
|
||||
: html``}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -136,4 +136,8 @@ export const ROUTES: Route[] = [
|
||||
await import("@goauthentik/admin/DebugPage");
|
||||
return html`<ak-admin-debug-page></ak-admin-debug-page>`;
|
||||
}),
|
||||
new Route(new RegExp("^/enterprise/licenses$"), async () => {
|
||||
await import("@goauthentik/admin/enterprise/EnterpriseLicenseListPage");
|
||||
return html`<ak-enterprise-license-list></ak-enterprise-license-list>`;
|
||||
}),
|
||||
];
|
||||
|
@ -26,6 +26,7 @@ export class SystemStatusCard extends AdminStatusCard<System> {
|
||||
// First install, ensure the embedded outpost host is set
|
||||
// also run when outpost host does not contain http
|
||||
// (yes it's called host and requires a URL, i know)
|
||||
// TODO: Improve this in OOB flow
|
||||
await this.setOutpostHost();
|
||||
status = await new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
|
||||
}
|
||||
|
64
web/src/admin/enterprise/EnterpriseLicenseForm.ts
Normal file
64
web/src/admin/enterprise/EnterpriseLicenseForm.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
import { EnterpriseApi, License } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-enterprise-license-form")
|
||||
export class EnterpriseLicenseForm extends ModelForm<License, string> {
|
||||
@state()
|
||||
installID?: string;
|
||||
|
||||
loadInstance(pk: string): Promise<License> {
|
||||
return new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseRetrieve({
|
||||
licenseUuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance) {
|
||||
return msg("Successfully updated license.");
|
||||
} else {
|
||||
return msg("Successfully created license.");
|
||||
}
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
this.installID = (
|
||||
await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseGetInstallIdRetrieve()
|
||||
).installId;
|
||||
}
|
||||
|
||||
async send(data: License): Promise<License> {
|
||||
if (this.instance) {
|
||||
return new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicensePartialUpdate({
|
||||
licenseUuid: this.instance.licenseUuid || "",
|
||||
patchedLicenseRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseCreate({
|
||||
licenseRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal label=${msg("Install ID")}>
|
||||
<input class="pf-c-form-control" readonly type="text" value="${this.installID}" />
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
name="key"
|
||||
?writeOnly=${this.instance !== undefined}
|
||||
label=${msg("License key")}
|
||||
>
|
||||
<textarea class="pf-c-form-control"></textarea>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
}
|
222
web/src/admin/enterprise/EnterpriseLicenseListPage.ts
Normal file
222
web/src/admin/enterprise/EnterpriseLicenseListPage.ts
Normal file
@ -0,0 +1,222 @@
|
||||
import "@goauthentik/admin/enterprise/EnterpriseLicenseForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import { PFColor } from "@goauthentik/elements/Label";
|
||||
import "@goauthentik/elements/Spinner";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import "@goauthentik/elements/cards/AggregateCard";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import "@goauthentik/elements/forms/ModalForm";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import { TableColumn } from "@goauthentik/elements/table/Table";
|
||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
|
||||
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||
|
||||
import { EnterpriseApi, License, LicenseForecast, LicenseSummary } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-enterprise-license-list")
|
||||
export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
checkbox = true;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
return msg("Licenses");
|
||||
}
|
||||
pageDescription(): string {
|
||||
return msg("Manage enterprise licenses");
|
||||
}
|
||||
pageIcon(): string {
|
||||
return "pf-icon pf-icon-key";
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
|
||||
@state()
|
||||
forecast?: LicenseForecast;
|
||||
|
||||
@state()
|
||||
summary?: LicenseSummary;
|
||||
|
||||
@state()
|
||||
installID?: string;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(
|
||||
PFDescriptionList,
|
||||
PFGrid,
|
||||
PFBanner,
|
||||
PFFormControl,
|
||||
PFButton,
|
||||
PFCard,
|
||||
css`
|
||||
.pf-m-no-padding-bottom {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
async apiEndpoint(page: number): Promise<PaginatedResponse<License>> {
|
||||
this.forecast = await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseForecastRetrieve();
|
||||
this.summary = await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve();
|
||||
this.installID = (
|
||||
await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseGetInstallIdRetrieve()
|
||||
).installId;
|
||||
return new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseList({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
pageSize: (await uiConfig()).pagination.perPage,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
new TableColumn(msg("Users")),
|
||||
new TableColumn(msg("Expiry date")),
|
||||
new TableColumn(msg("Actions")),
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("License(s)")}
|
||||
.objects=${this.selectedElements}
|
||||
.metadata=${(item: License) => {
|
||||
return [
|
||||
{ key: msg("Name"), value: item.name },
|
||||
{ key: msg("Expiry"), value: item.expiry?.toLocaleString() },
|
||||
];
|
||||
}}
|
||||
.usedBy=${(item: License) => {
|
||||
return new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseUsedByList({
|
||||
licenseUuid: item.licenseUuid,
|
||||
});
|
||||
}}
|
||||
.delete=${(item: License) => {
|
||||
return new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseDestroy({
|
||||
licenseUuid: item.licenseUuid,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${msg("Delete")}
|
||||
</button>
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
renderSectionBefore(): TemplateResult {
|
||||
return html`
|
||||
<div class="pf-c-banner pf-m-info">
|
||||
${msg("Enterprise is in preview.")}
|
||||
<a href="mailto:hello@goauthentik.io">${msg("Send us feedback!")}</a>
|
||||
</div>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-bottom">
|
||||
<div
|
||||
class="pf-l-grid pf-m-gutter pf-m-all-6-col-on-sm pf-m-all-4-col-on-md pf-m-all-3-col-on-lg pf-m-all-3-col-on-xl"
|
||||
>
|
||||
<div class="pf-l-grid__item pf-c-card">
|
||||
<div class="pf-c-card__title">${msg("How to get a license")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${this.installID
|
||||
? html` <a
|
||||
target="_blank"
|
||||
href=${`https://customers.goauthentik.io/from_authentik/purchase/?install_id=${this.installID}`}
|
||||
class="pf-c-button pf-m-primary pf-m-block"
|
||||
>${msg("Go to the customer portal")}</a
|
||||
>`
|
||||
: html`<ak-spinner></ak-spinner>`}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-c-card">
|
||||
<ak-aggregate-card
|
||||
icon="pf-icon pf-icon-user"
|
||||
header=${msg("Forecasted default users")}
|
||||
subtext=${msg("Estimated user count one year from now")}
|
||||
>
|
||||
${this.forecast?.users}
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-c-card">
|
||||
<ak-aggregate-card
|
||||
icon="pf-icon pf-icon-user"
|
||||
header=${msg("Forecasted external users")}
|
||||
subtext=${msg("Estimated external user count one year from now")}
|
||||
>
|
||||
${this.forecast?.externalUsers}
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-c-card">
|
||||
<ak-aggregate-card
|
||||
icon="pf-icon pf-icon-user"
|
||||
header=${msg("Expiry")}
|
||||
subtext=${msg("Cumulative license expiry")}
|
||||
>
|
||||
${this.summary?.hasLicense
|
||||
? this.summary.latestValid.toLocaleString()
|
||||
: "-"}
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
||||
row(item: License): TemplateResult[] {
|
||||
let color = PFColor.Green;
|
||||
if (item.expiry) {
|
||||
const now = new Date();
|
||||
const inAMonth = new Date();
|
||||
inAMonth.setDate(inAMonth.getDate() + 30);
|
||||
if (item.expiry <= inAMonth) {
|
||||
color = PFColor.Orange;
|
||||
}
|
||||
if (item.expiry <= now) {
|
||||
color = PFColor.Red;
|
||||
}
|
||||
}
|
||||
return [
|
||||
html`<div>${item.name}</div>`,
|
||||
html`<div>
|
||||
<small>0 / ${item.users}</small>
|
||||
<small>0 / ${item.externalUsers}</small>
|
||||
</div>`,
|
||||
html`<ak-label color=${color}> ${item.expiry?.toLocaleString()} </ak-label>`,
|
||||
html`<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Update")} </span>
|
||||
<span slot="header"> ${msg("Update License")} </span>
|
||||
<ak-enterprise-license-form slot="form" .instancePk=${item.licenseUuid}>
|
||||
</ak-enterprise-license-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-plain">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
</ak-forms-modal>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
<span slot="header"> ${msg("Create License")} </span>
|
||||
<ak-enterprise-license-form slot="form"> </ak-enterprise-license-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${msg("Create")}</button>
|
||||
</ak-forms-modal>
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
import "@goauthentik/admin/users/GroupSelectModal";
|
||||
import { UserTypeEnum } from "@goauthentik/api/dist/models/UserTypeEnum";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
import "@goauthentik/elements/forms/Radio";
|
||||
import YAML from "yaml";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
@ -75,6 +77,31 @@ export class UserForm extends ModelForm<User, number> {
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${msg("User's display name.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("User type")} ?required=${true} name="type">
|
||||
<ak-radio
|
||||
.options=${[
|
||||
// TODO: Add better copy
|
||||
{
|
||||
label: "Default",
|
||||
value: UserTypeEnum.Default,
|
||||
default: true,
|
||||
description: html`${msg("Default user")}`,
|
||||
},
|
||||
{
|
||||
label: "External",
|
||||
value: UserTypeEnum.External,
|
||||
description: html`${msg("External user")}`,
|
||||
},
|
||||
{
|
||||
label: "Service account",
|
||||
value: UserTypeEnum.ServiceAccount,
|
||||
description: html`${msg("Service account")}`,
|
||||
},
|
||||
]}
|
||||
.value=${this.instance?.type}
|
||||
>
|
||||
</ak-radio>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Email")} name="email">
|
||||
<input
|
||||
type="email"
|
||||
|
@ -19,6 +19,9 @@ export class AggregateCard extends AKElement {
|
||||
@property()
|
||||
headerLink?: string;
|
||||
|
||||
@property()
|
||||
subtext?: string;
|
||||
|
||||
@property({ type: Boolean })
|
||||
isCenter = true;
|
||||
|
||||
@ -79,6 +82,7 @@ export class AggregateCard extends AKElement {
|
||||
</div>
|
||||
<div class="pf-c-card__body ${this.isCenter ? "center-value" : ""}">
|
||||
${this.renderInner()}
|
||||
${this.subtext ? html`<p class="subtext">${this.subtext}</p>` : html``}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
52
web/src/elements/enterprise/EnterpriseStatusBanner.ts
Normal file
52
web/src/elements/enterprise/EnterpriseStatusBanner.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
|
||||
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||
|
||||
import { EnterpriseApi, LicenseSummary } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-enterprise-status")
|
||||
export class EnterpriseStatusBanner extends AKElement {
|
||||
@state()
|
||||
summary?: LicenseSummary;
|
||||
|
||||
@property()
|
||||
interface: "admin" | "user" | "" = "";
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBanner];
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve().then((b) => {
|
||||
this.summary = b;
|
||||
});
|
||||
}
|
||||
|
||||
renderBanner(): TemplateResult {
|
||||
return html`<div class="pf-c-banner ${this.summary?.readOnly ? "pf-m-red" : "pf-m-orange"}">
|
||||
${msg("Warning: The current user count has exceeded the configured licenses.")}
|
||||
<a href="/if/admin/#/enterprise/licenses"> ${msg("Click here for more info.")} </a>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
switch (this.interface.toLowerCase()) {
|
||||
case "admin":
|
||||
if (this.summary?.showAdminWarning || this.summary?.readOnly) {
|
||||
return this.renderBanner();
|
||||
}
|
||||
break;
|
||||
case "user":
|
||||
if (this.summary?.showUserWarning || this.summary?.readOnly) {
|
||||
return this.renderBanner();
|
||||
}
|
||||
break;
|
||||
}
|
||||
return html``;
|
||||
}
|
||||
}
|
@ -28,6 +28,16 @@ export abstract class TablePage<T> extends Table<T> {
|
||||
return html``;
|
||||
}
|
||||
|
||||
// Optionally render section above the table
|
||||
renderSectionBefore(): TemplateResult {
|
||||
return html``;
|
||||
}
|
||||
|
||||
// Optionally render section below the table
|
||||
renderSectionAfter(): TemplateResult {
|
||||
return html``;
|
||||
}
|
||||
|
||||
renderEmpty(inner?: TemplateResult): TemplateResult {
|
||||
return super.renderEmpty(html`
|
||||
${inner
|
||||
@ -75,6 +85,7 @@ export abstract class TablePage<T> extends Table<T> {
|
||||
description=${ifDefined(this.pageDescription())}
|
||||
>
|
||||
</ak-page-header>
|
||||
${this.renderSectionBefore()}
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-sidebar pf-m-gutter">
|
||||
<div class="pf-c-sidebar__main">
|
||||
@ -85,6 +96,7 @@ export abstract class TablePage<T> extends Table<T> {
|
||||
${this.renderSidebarAfter()}
|
||||
</div>
|
||||
</div>
|
||||
</section>`;
|
||||
</section>
|
||||
${this.renderSectionAfter()}`;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import { first } from "@goauthentik/common/utils";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
import { Interface } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
import "@goauthentik/elements/enterprise/EnterpriseStatusBanner";
|
||||
import "@goauthentik/elements/messages/MessageContainer";
|
||||
import "@goauthentik/elements/notifications/APIDrawer";
|
||||
import "@goauthentik/elements/notifications/NotificationDrawer";
|
||||
@ -35,7 +37,7 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
|
||||
|
||||
import { EventsApi, SessionUser } from "@goauthentik/api";
|
||||
import { CoreApi, EventsApi, SessionUser } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-interface-user")
|
||||
export class UserInterface extends Interface {
|
||||
@ -148,6 +150,7 @@ export class UserInterface extends Interface {
|
||||
userDisplay = this.me.user.username;
|
||||
}
|
||||
return html` <ak-locale-context>
|
||||
<ak-enterprise-status interface="user"></ak-enterprise-status>
|
||||
<div class="pf-c-page">
|
||||
<div class="background-wrapper" style="${this.uiConfig.theme.background}"></div>
|
||||
<header class="pf-c-page__header">
|
||||
@ -243,18 +246,23 @@ export class UserInterface extends Interface {
|
||||
: html``}
|
||||
</div>
|
||||
${this.me.original
|
||||
? html`<div class="pf-c-page__header-tools">
|
||||
<div class="pf-c-page__header-tools-group">
|
||||
<a
|
||||
class="pf-c-button pf-m-warning pf-m-small"
|
||||
href=${`/-/impersonation/end/?back=${encodeURIComponent(
|
||||
`${window.location.pathname}#${window.location.hash}`,
|
||||
)}`}
|
||||
>
|
||||
${msg("Stop impersonation")}
|
||||
</a>
|
||||
</div>
|
||||
</div>`
|
||||
? html`
|
||||
<div class="pf-c-page__header-tools">
|
||||
<div class="pf-c-page__header-tools-group">
|
||||
<ak-action-button
|
||||
class="pf-m-warning pf-m-small"
|
||||
.apiRequest=${() => {
|
||||
return new CoreApi(DEFAULT_CONFIG)
|
||||
.coreUsersImpersonateEndRetrieve()
|
||||
.then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
}}
|
||||
>
|
||||
${msg("Stop impersonation")}
|
||||
</ak-action-button>
|
||||
</div>
|
||||
</div>`
|
||||
: html``}
|
||||
<div class="pf-c-page__header-tools-group">
|
||||
<div
|
||||
|
Reference in New Issue
Block a user