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:
Jens L
2023-07-17 17:57:08 +02:00
committed by GitHub
parent cf799fca03
commit 41af486006
56 changed files with 2534 additions and 128 deletions

View 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>
`;
}
}