diff --git a/authentik/endpoints/connectors/agent/api/agent.py b/authentik/endpoints/connectors/agent/api/agent.py index 0ba9b43a58..c2e6e4690f 100644 --- a/authentik/endpoints/connectors/agent/api/agent.py +++ b/authentik/endpoints/connectors/agent/api/agent.py @@ -1,3 +1,4 @@ +from django.db import models from rest_framework.fields import ( BooleanField, CharField, @@ -14,6 +15,12 @@ from authentik.endpoints.models import Device from authentik.lib.utils.time import timedelta_from_string from authentik.providers.oauth2.views.jwks import JWKSView +try: + from authentik.enterprise.models import LicenseUsageStatus +except ImportError: + + class LicenseUsageStatus(models.TextChoices): ... + class AgentConfigSerializer(PassiveSerializer): @@ -29,6 +36,7 @@ class AgentConfigSerializer(PassiveSerializer): auth_terminate_session_on_expiry = BooleanField() system_config = SerializerMethodField() + license_status = SerializerMethodField(required=False, allow_null=True) def get_device_id(self, instance: AgentConnector) -> str: device: Device = self.context["device"] @@ -54,6 +62,14 @@ class AgentConfigSerializer(PassiveSerializer): def get_system_config(self, instance: AgentConnector) -> ConfigSerializer: return ConfigView.get_config(self.context["request"]).data + def get_license_status(self, instance: AgentConnector) -> "LicenseUsageStatus": + try: + from authentik.enterprise.license import LicenseKey + + return LicenseKey.cached_summary().status + except ModuleNotFoundError: + return None + class EnrollSerializer(PassiveSerializer): diff --git a/authentik/root/settings.py b/authentik/root/settings.py index a544d02af1..ac511e95b1 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -190,6 +190,7 @@ SPECTACULAR_SETTINGS = { "PKCEMethodEnum": "authentik.sources.oauth.models.PKCEMethod", "DeviceFactsOSFamily": "authentik.endpoints.facts.OSFamily", "StageModeEnum": "authentik.endpoints.models.StageMode", + "LicenseSummaryStatusEnum": "authentik.enterprise.models.LicenseUsageStatus", }, "ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE": False, "ENUM_GENERATE_CHOICE_DESCRIPTION": False, diff --git a/schema.yml b/schema.yml index d808cf80b7..549f71e031 100644 --- a/schema.yml +++ b/schema.yml @@ -32811,12 +32811,18 @@ components: allOf: - $ref: '#/components/schemas/Config' readOnly: true + license_status: + allOf: + - $ref: '#/components/schemas/LicenseStatusEnum' + readOnly: true + nullable: true required: - auth_terminate_session_on_expiry - authorization_flow - device_id - jwks_auth - jwks_challenge + - license_status - nss_gid_offset - nss_uid_offset - refresh_interval @@ -40941,6 +40947,16 @@ components: minLength: 1 required: - key + LicenseStatusEnum: + enum: + - unlicensed + - valid + - expired + - expiry_soon + - limit_exceeded_admin + - limit_exceeded_user + - read_only + type: string LicenseSummary: type: object description: Serializer for license status diff --git a/web/src/admin/endpoints/devices/DeviceListPage.ts b/web/src/admin/endpoints/devices/DeviceListPage.ts index 126e24208c..4712c16862 100644 --- a/web/src/admin/endpoints/devices/DeviceListPage.ts +++ b/web/src/admin/endpoints/devices/DeviceListPage.ts @@ -10,8 +10,6 @@ import { PaginatedResponse, TableColumn, Timestamp } from "#elements/table/Table import { TablePage } from "#elements/table/TablePage"; import { SlottedTemplateResult } from "#elements/types"; -import { osFamilyToLabel } from "#admin/endpoints/devices/utils"; - import { DeviceSummary, EndpointDevice, EndpointsApi } from "@goauthentik/api"; import { msg } from "@lit/localize"; @@ -130,7 +128,7 @@ export class DeviceListPage extends TablePage { html`
${item.facts.data.network?.hostname || item.name}
`, - html`${osFamilyToLabel(item.facts.data.os?.family)} ${item.facts.data.os?.version}`, + html`${item.facts.data.os?.name} ${item.facts.data.os?.version}`, html`${item.accessGroupObj?.name || "-"}`, item.facts.created ? Timestamp(item.facts.created) : html`-`, html`