mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-17 19:09:11 +03:00
endpoints: show agent version (#19239)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@@ -3,7 +3,7 @@ from rest_framework.fields import SerializerMethodField
|
||||
from authentik.core.api.utils import ModelSerializer
|
||||
from authentik.endpoints.api.connectors import ConnectorSerializer
|
||||
from authentik.endpoints.api.device_fact_snapshots import DeviceFactSnapshotSerializer
|
||||
from authentik.endpoints.models import DeviceConnection
|
||||
from authentik.endpoints.models import Connector, DeviceConnection, DeviceFactSnapshot
|
||||
|
||||
|
||||
class DeviceConnectionSerializer(ModelSerializer):
|
||||
@@ -12,10 +12,19 @@ class DeviceConnectionSerializer(ModelSerializer):
|
||||
latest_snapshot = SerializerMethodField(allow_null=True)
|
||||
|
||||
def get_latest_snapshot(self, instance: DeviceConnection) -> DeviceFactSnapshotSerializer:
|
||||
snapshot = instance.devicefactsnapshot_set.order_by("-created").first()
|
||||
snapshot: DeviceFactSnapshot | None = instance.devicefactsnapshot_set.order_by(
|
||||
"-created"
|
||||
).first()
|
||||
if not snapshot:
|
||||
return None
|
||||
return DeviceFactSnapshotSerializer(snapshot).data
|
||||
connector: Connector = Connector.objects.get_subclass(pk=snapshot.connection.connector_id)
|
||||
vendor = connector.controller.vendor_identifier()
|
||||
return DeviceFactSnapshotSerializer(
|
||||
snapshot,
|
||||
context={
|
||||
"vendor": vendor,
|
||||
},
|
||||
).data
|
||||
|
||||
class Meta:
|
||||
model = DeviceConnection
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
from enum import StrEnum
|
||||
|
||||
from rest_framework.fields import SerializerMethodField
|
||||
|
||||
from authentik.core.api.utils import ModelSerializer
|
||||
from authentik.endpoints.controller import MERGED_VENDOR
|
||||
from authentik.endpoints.facts import DeviceFacts
|
||||
from authentik.endpoints.models import DeviceFactSnapshot
|
||||
from authentik.endpoints.models import Connector, DeviceFactSnapshot
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
|
||||
|
||||
def get_vendor_choices():
|
||||
choices = [(MERGED_VENDOR, MERGED_VENDOR)]
|
||||
for connector_type in all_subclasses(Connector):
|
||||
ident = connector_type().controller.vendor_identifier()
|
||||
choices.append((ident, ident))
|
||||
return choices
|
||||
|
||||
|
||||
vendors = StrEnum("DeviceConnectorVendors", get_vendor_choices())
|
||||
|
||||
|
||||
class DeviceFactSnapshotSerializer(ModelSerializer):
|
||||
|
||||
data = DeviceFacts()
|
||||
vendor = SerializerMethodField()
|
||||
|
||||
def get_vendor(self, instance: DeviceFactSnapshot) -> vendors:
|
||||
return self.context.get("vendor", MERGED_VENDOR)
|
||||
|
||||
class Meta:
|
||||
model = DeviceFactSnapshot
|
||||
@@ -14,6 +35,7 @@ class DeviceFactSnapshotSerializer(ModelSerializer):
|
||||
"connection",
|
||||
"created",
|
||||
"expires",
|
||||
"vendor",
|
||||
]
|
||||
extra_kwargs = {
|
||||
"created": {"read_only": True},
|
||||
|
||||
@@ -44,6 +44,10 @@ class MDMConfigResponseSerializer(PassiveSerializer):
|
||||
|
||||
class AgentConnectorController(BaseController[AgentConnector]):
|
||||
|
||||
@staticmethod
|
||||
def vendor_identifier() -> str:
|
||||
return "goauthentik.io/platform"
|
||||
|
||||
def supported_enrollment_methods(self):
|
||||
return []
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ from authentik.endpoints.models import Connector
|
||||
from authentik.flows.stage import StageView
|
||||
from authentik.lib.sentry import SentryIgnoredException
|
||||
|
||||
MERGED_VENDOR = "goauthentik.io/@merged"
|
||||
|
||||
|
||||
class EnrollmentMethods(models.TextChoices):
|
||||
# Automatically enrolled through user action
|
||||
@@ -28,6 +30,10 @@ class BaseController[T: "Connector"]:
|
||||
self.connector = connector
|
||||
self.logger = get_logger().bind(connector=connector.name)
|
||||
|
||||
@staticmethod
|
||||
def vendor_identifier() -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
def supported_enrollment_methods(self) -> list[EnrollmentMethods]:
|
||||
return []
|
||||
|
||||
|
||||
+10
@@ -36086,11 +36086,16 @@ components:
|
||||
format: date-time
|
||||
readOnly: true
|
||||
nullable: true
|
||||
vendor:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/VendorEnum'
|
||||
readOnly: true
|
||||
required:
|
||||
- connection
|
||||
- created
|
||||
- data
|
||||
- expires
|
||||
- vendor
|
||||
DeviceFacts:
|
||||
type: object
|
||||
properties:
|
||||
@@ -55908,6 +55913,11 @@ components:
|
||||
code:
|
||||
type: string
|
||||
additionalProperties: {}
|
||||
VendorEnum:
|
||||
enum:
|
||||
- goauthentik.io/@merged
|
||||
- goauthentik.io/platform
|
||||
type: string
|
||||
Version:
|
||||
type: object
|
||||
description: Get running and latest version.
|
||||
|
||||
@@ -14,11 +14,11 @@ import { AKElement } from "#elements/Base";
|
||||
import { Timestamp } from "#elements/table/shared";
|
||||
|
||||
import { setPageDetails } from "#components/ak-page-navbar";
|
||||
import renderDescriptionList from "#components/DescriptionList";
|
||||
import renderDescriptionList, { DescriptionPair } from "#components/DescriptionList";
|
||||
|
||||
import { getSize } from "#admin/endpoints/devices/utils";
|
||||
|
||||
import { Disk, EndpointDeviceDetails, EndpointsApi } from "@goauthentik/api";
|
||||
import { DeviceConnection, Disk, EndpointDeviceDetails, EndpointsApi } from "@goauthentik/api";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { CSSResult, html, nothing, PropertyValues } from "lit";
|
||||
@@ -188,24 +188,26 @@ export class DeviceViewPage extends AKElement {
|
||||
<div class="pf-l-grid__item pf-m-4-col pf-c-card">
|
||||
<div class="pf-c-card__title">${msg("Connections")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list pf-m-horizontal">
|
||||
${this.device.connectionsObj.map((conn) => {
|
||||
return html`<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${conn.connectorObj.name}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
${renderDescriptionList(
|
||||
this.device.connectionsObj.map((conn) => {
|
||||
return [
|
||||
html`${conn.connectorObj.name}`,
|
||||
html`<div class="pf-c-description-list__text">
|
||||
${msg(
|
||||
str`Agent version: ${this.agentVersion(conn) ?? "-"}`,
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-description-list__text">
|
||||
${conn.latestSnapshot?.created
|
||||
? Timestamp(conn.latestSnapshot.created)
|
||||
: html`-`}
|
||||
</div>
|
||||
</dd>
|
||||
</div>`;
|
||||
})}
|
||||
</dl>
|
||||
: nothing}
|
||||
</div>`,
|
||||
];
|
||||
}) as DescriptionPair[],
|
||||
{
|
||||
horizontal: true,
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-12-col pf-c-card">
|
||||
@@ -219,6 +221,15 @@ export class DeviceViewPage extends AKElement {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
agentVersion(conn: DeviceConnection): string | undefined {
|
||||
const vendorContainer = conn.latestSnapshot?.data.vendor;
|
||||
if (!vendorContainer) return;
|
||||
const vendorData = vendorContainer[conn.latestSnapshot.vendor];
|
||||
if (!vendorData) return;
|
||||
if (!("agent_version" in vendorData)) return;
|
||||
return vendorData.agent_version;
|
||||
}
|
||||
|
||||
renderProcesses() {
|
||||
if (!this.device) {
|
||||
return nothing;
|
||||
|
||||
Reference in New Issue
Block a user