From a073b7a5b13ff9de0e60d2f6dead2db2ed7cdce4 Mon Sep 17 00:00:00 2001 From: "Jens L." Date: Fri, 9 Aug 2024 22:20:01 +0200 Subject: [PATCH] enterprise: add support for license flags (#10842) Signed-off-by: Jens Langhammer --- authentik/enterprise/api.py | 4 +- authentik/enterprise/license.py | 9 ++- blueprints/schema.json | 8 --- schema.yml | 10 +++ .../enterprise/EnterpriseLicenseListPage.ts | 3 +- .../enterprise/EnterpriseStatusBanner.ts | 68 ++++++++++++------- 6 files changed, 64 insertions(+), 38 deletions(-) diff --git a/authentik/enterprise/api.py b/authentik/enterprise/api.py index 510fc378f4..ca7c18cb1e 100644 --- a/authentik/enterprise/api.py +++ b/authentik/enterprise/api.py @@ -1,6 +1,5 @@ """Enterprise API Views""" -from dataclasses import asdict from datetime import timedelta from django.utils.timezone import now @@ -104,8 +103,7 @@ class LicenseViewSet(UsedByMixin, ModelViewSet): @action(detail=False, methods=["GET"], permission_classes=[IsAuthenticated]) def summary(self, request: Request) -> Response: """Get the total license status""" - response = LicenseSummarySerializer(data=asdict(LicenseKey.cached_summary())) - response.is_valid(raise_exception=True) + response = LicenseSummarySerializer(instance=LicenseKey.cached_summary()) return Response(response.data) @permission_required(None, ["authentik_enterprise.view_license"]) diff --git a/authentik/enterprise/license.py b/authentik/enterprise/license.py index 3ce789e34c..b6c933f3d1 100644 --- a/authentik/enterprise/license.py +++ b/authentik/enterprise/license.py @@ -20,6 +20,7 @@ from rest_framework.fields import ( ChoiceField, DateTimeField, IntegerField, + ListField, ) from authentik.core.api.utils import PassiveSerializer @@ -55,6 +56,7 @@ class LicenseFlags(Enum): """License flags""" TRIAL = "trial" + NON_PRODUCTION = "non_production" @dataclass @@ -65,6 +67,7 @@ class LicenseSummary: external_users: int status: LicenseUsageStatus latest_valid: datetime + license_flags: list[LicenseFlags] class LicenseSummarySerializer(PassiveSerializer): @@ -74,6 +77,7 @@ class LicenseSummarySerializer(PassiveSerializer): external_users = IntegerField(required=True) status = ChoiceField(choices=LicenseUsageStatus.choices) latest_valid = DateTimeField() + license_flags = ListField(child=ChoiceField(choices=tuple(x.value for x in LicenseFlags))) @dataclass @@ -86,7 +90,7 @@ class LicenseKey: name: str internal_users: int = 0 external_users: int = 0 - flags: list[LicenseFlags] = field(default_factory=list) + license_flags: list[LicenseFlags] = field(default_factory=list) @staticmethod def validate(jwt: str, check_expiry=True) -> "LicenseKey": @@ -132,7 +136,7 @@ class LicenseKey: total.exp = exp_ts if exp_ts <= total.exp: total.exp = exp_ts - total.flags.extend(lic.status.flags) + total.license_flags.extend(lic.status.license_flags) return total @staticmethod @@ -216,6 +220,7 @@ class LicenseKey: internal_users=self.internal_users, external_users=self.external_users, status=status, + license_flags=self.license_flags, ) @staticmethod diff --git a/blueprints/schema.json b/blueprints/schema.json index 7278a76d01..3d8b7436ae 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -6357,13 +6357,9 @@ "authentik_sources_plex.change_plexsourcepropertymapping", "authentik_sources_plex.delete_plexsourcepropertymapping", "authentik_sources_plex.view_plexsourcepropertymapping", - "authentik_sources_plex.add_plexsourceconnection", "authentik_sources_plex.add_userplexsourceconnection", - "authentik_sources_plex.change_plexsourceconnection", "authentik_sources_plex.change_userplexsourceconnection", - "authentik_sources_plex.delete_plexsourceconnection", "authentik_sources_plex.delete_userplexsourceconnection", - "authentik_sources_plex.view_plexsourceconnection", "authentik_sources_plex.view_userplexsourceconnection", "authentik_sources_saml.add_groupsamlsourceconnection", "authentik_sources_saml.change_groupsamlsourceconnection", @@ -12016,13 +12012,9 @@ "authentik_sources_plex.change_plexsourcepropertymapping", "authentik_sources_plex.delete_plexsourcepropertymapping", "authentik_sources_plex.view_plexsourcepropertymapping", - "authentik_sources_plex.add_plexsourceconnection", "authentik_sources_plex.add_userplexsourceconnection", - "authentik_sources_plex.change_plexsourceconnection", "authentik_sources_plex.change_userplexsourceconnection", - "authentik_sources_plex.delete_plexsourceconnection", "authentik_sources_plex.delete_userplexsourceconnection", - "authentik_sources_plex.view_plexsourceconnection", "authentik_sources_plex.view_userplexsourceconnection", "authentik_sources_saml.add_groupsamlsourceconnection", "authentik_sources_saml.change_groupsamlsourceconnection", diff --git a/schema.yml b/schema.yml index efaaa462f9..8a5cf91b5f 100644 --- a/schema.yml +++ b/schema.yml @@ -41352,6 +41352,11 @@ components: - key - license_uuid - name + LicenseFlagsEnum: + enum: + - trial + - non_production + type: string LicenseForecast: type: object description: Serializer for license forecast @@ -41391,10 +41396,15 @@ components: latest_valid: type: string format: date-time + license_flags: + type: array + items: + $ref: '#/components/schemas/LicenseFlagsEnum' required: - external_users - internal_users - latest_valid + - license_flags - status LicenseSummaryStatusEnum: enum: diff --git a/web/src/admin/enterprise/EnterpriseLicenseListPage.ts b/web/src/admin/enterprise/EnterpriseLicenseListPage.ts index fec91f6eee..84dad79292 100644 --- a/web/src/admin/enterprise/EnterpriseLicenseListPage.ts +++ b/web/src/admin/enterprise/EnterpriseLicenseListPage.ts @@ -183,7 +183,8 @@ export class EnterpriseLicenseListPage extends TablePage { header=${msg("Expiry")} subtext=${msg("Cumulative license expiry")} > - ${this.summary?.status === LicenseSummaryStatusEnum.Unlicensed + ${this.summary && + this.summary?.status !== LicenseSummaryStatusEnum.Unlicensed ? html`
${getRelativeTime(this.summary.latestValid)}
${this.summary.latestValid.toLocaleString()}` : "-"} diff --git a/web/src/elements/enterprise/EnterpriseStatusBanner.ts b/web/src/elements/enterprise/EnterpriseStatusBanner.ts index 5a898b892e..cf09392245 100644 --- a/web/src/elements/enterprise/EnterpriseStatusBanner.ts +++ b/web/src/elements/enterprise/EnterpriseStatusBanner.ts @@ -2,23 +2,45 @@ import { AKElement } from "@goauthentik/elements/Base"; import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider"; import { msg } from "@lit/localize"; -import { CSSResult, TemplateResult, html } from "lit"; +import { CSSResult, TemplateResult, html, nothing } from "lit"; import { customElement, property } from "lit/decorators.js"; import PFBanner from "@patternfly/patternfly/components/Banner/banner.css"; -import { LicenseSummaryStatusEnum } from "@goauthentik/api"; +import { LicenseFlagsEnum, LicenseSummaryStatusEnum } from "@goauthentik/api"; @customElement("ak-enterprise-status") export class EnterpriseStatusBanner extends WithLicenseSummary(AKElement) { @property() - interface: "admin" | "user" | "" = ""; + interface: "admin" | "user" | "flow" | "" = ""; static get styles(): CSSResult[] { return [PFBanner]; } - renderBanner(): TemplateResult { + renderStatusBanner() { + // Check if we're in the correct interface to render a banner + switch (this.licenseSummary.status) { + // user warning is both on admin interface and user interface + case LicenseSummaryStatusEnum.LimitExceededUser: + if ( + this.interface.toLowerCase() !== "user" && + this.interface.toLowerCase() !== "admin" + ) { + return nothing; + } + break; + case LicenseSummaryStatusEnum.ExpirySoon: + case LicenseSummaryStatusEnum.Expired: + case LicenseSummaryStatusEnum.LimitExceededAdmin: + if (this.interface.toLowerCase() !== "admin") { + return nothing; + } + break; + case LicenseSummaryStatusEnum.ReadOnly: + default: + break; + } let message = ""; switch (this.licenseSummary.status) { case LicenseSummaryStatusEnum.LimitExceededAdmin: @@ -44,7 +66,8 @@ export class EnterpriseStatusBanner extends WithLicenseSummary(AKElement) { break; } return html`
@@ -53,26 +76,23 @@ export class EnterpriseStatusBanner extends WithLicenseSummary(AKElement) {
`; } + renderFlagBanner(): TemplateResult { + return html` + ${this.licenseSummary.licenseFlags.includes(LicenseFlagsEnum.Trial) + ? html`
+ ${msg("This authentik instance uses a Trial license.")} +
` + : nothing} + ${this.licenseSummary.licenseFlags.includes(LicenseFlagsEnum.NonProduction) + ? html`
+ ${msg("This authentik instance uses a Non-production license.")} +
` + : nothing} + `; + } + render(): TemplateResult { - switch (this.licenseSummary.status) { - case LicenseSummaryStatusEnum.LimitExceededUser: - if (this.interface.toLowerCase() === "user") { - return this.renderBanner(); - } - break; - case LicenseSummaryStatusEnum.ExpirySoon: - case LicenseSummaryStatusEnum.Expired: - case LicenseSummaryStatusEnum.LimitExceededAdmin: - if (this.interface.toLowerCase() === "admin") { - return this.renderBanner(); - } - break; - case LicenseSummaryStatusEnum.ReadOnly: - return this.renderBanner(); - default: - break; - } - return html``; + return html`${this.renderFlagBanner()}${this.renderStatusBanner()}`; } }