diff --git a/authentik/endpoints/api/device_access_group.py b/authentik/endpoints/api/device_access_group.py
index 72dc209c37..29d64d1fd6 100644
--- a/authentik/endpoints/api/device_access_group.py
+++ b/authentik/endpoints/api/device_access_group.py
@@ -12,6 +12,7 @@ class DeviceAccessGroupSerializer(ModelSerializer):
fields = [
"pbm_uuid",
"name",
+ "attributes",
]
diff --git a/authentik/endpoints/connectors/agent/models.py b/authentik/endpoints/connectors/agent/models.py
index 5b3cb46cc7..b036c6cd74 100644
--- a/authentik/endpoints/connectors/agent/models.py
+++ b/authentik/endpoints/connectors/agent/models.py
@@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
from uuid import uuid4
from django.db import models
+from django.templatetags.static import static
from django.utils.translation import gettext_lazy as _
from rest_framework.serializers import Serializer
@@ -51,6 +52,10 @@ class AgentConnector(Connector):
)
challenge_trigger_check_in = models.BooleanField(default=False)
+ @property
+ def icon_url(self):
+ return static("icons/icon.svg")
+
@property
def serializer(self) -> type[Serializer]:
from authentik.endpoints.connectors.agent.api.connectors import (
diff --git a/authentik/endpoints/facts.py b/authentik/endpoints/facts.py
index 7b11902104..f2905d2c03 100644
--- a/authentik/endpoints/facts.py
+++ b/authentik/endpoints/facts.py
@@ -1,4 +1,5 @@
from django.db.models import TextChoices
+from django.utils.translation import gettext_lazy as _
from drf_spectacular.extensions import OpenApiSerializerFieldExtension
from drf_spectacular.plumbing import build_basic_type
from drf_spectacular.types import OpenApiTypes
@@ -15,7 +16,6 @@ from authentik.core.api.utils import JSONDictField
class BigIntegerFieldFix(OpenApiSerializerFieldExtension):
-
target_class = "authentik.endpoints.facts.BigIntegerField"
def map_serializer_field(self, auto_schema, direction):
@@ -46,9 +46,23 @@ class DiskSerializer(Serializer):
class OperatingSystemSerializer(Serializer):
+ """For example:
+ {"family":"linux","name":"Ubuntu","version":"24.04.3 LTS (Noble Numbat)","arch":"amd64"}
+ {"family": "windows","name":"Server 2022 Datacenter","version":"10.0.20348.4405","arch":"amd64"}
+ {"family": "windows","name":"Server 2022 Datacenter","version":"10.0.20348.4405","arch":"amd64"}
+ {"family": "mac_os", "name": "", "version": "26.2", "arch": "arm64"}
+ """
+
family = ChoiceField(OSFamily.choices, required=True)
- name = CharField(required=False)
- version = CharField(required=False)
+ name = CharField(
+ required=False, help_text=_("Operating System name, such as 'Server 2022' or 'Ubuntu'")
+ )
+ version = CharField(
+ required=False,
+ help_text=_(
+ "Operating System version, must always be the version number but may contain build name"
+ ),
+ )
arch = CharField(required=True)
diff --git a/authentik/endpoints/migrations/0004_deviceaccessgroup_attributes.py b/authentik/endpoints/migrations/0004_deviceaccessgroup_attributes.py
new file mode 100644
index 0000000000..1d705ab1a0
--- /dev/null
+++ b/authentik/endpoints/migrations/0004_deviceaccessgroup_attributes.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.2.9 on 2025-12-08 23:54
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("authentik_endpoints", "0003_alter_endpointstage_options_endpointstage_mode"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="deviceaccessgroup",
+ name="attributes",
+ field=models.JSONField(blank=True, default=dict),
+ ),
+ ]
diff --git a/authentik/endpoints/models.py b/authentik/endpoints/models.py
index fe3114d520..1cc04b5fb7 100644
--- a/authentik/endpoints/models.py
+++ b/authentik/endpoints/models.py
@@ -175,7 +175,7 @@ class Connector(ScheduledModel, SerializerModel):
]
-class DeviceAccessGroup(SerializerModel, PolicyBindingModel):
+class DeviceAccessGroup(AttributesMixin, SerializerModel, PolicyBindingModel):
name = models.TextField(unique=True)
diff --git a/authentik/enterprise/endpoints/connectors/fleet/__init__.py b/authentik/enterprise/endpoints/connectors/fleet/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/authentik/enterprise/endpoints/connectors/fleet/api.py b/authentik/enterprise/endpoints/connectors/fleet/api.py
new file mode 100644
index 0000000000..90d11baa7a
--- /dev/null
+++ b/authentik/enterprise/endpoints/connectors/fleet/api.py
@@ -0,0 +1,37 @@
+"""FleetConnector API Views"""
+
+from rest_framework.viewsets import ModelViewSet
+
+from authentik.core.api.used_by import UsedByMixin
+from authentik.endpoints.api.connectors import ConnectorSerializer
+from authentik.enterprise.api import EnterpriseRequiredMixin
+from authentik.enterprise.endpoints.connectors.fleet.models import FleetConnector
+
+
+class FleetConnectorSerializer(EnterpriseRequiredMixin, ConnectorSerializer):
+ """FleetConnector Serializer"""
+
+ class Meta(ConnectorSerializer.Meta):
+ model = FleetConnector
+ fields = ConnectorSerializer.Meta.fields + [
+ "url",
+ "token",
+ "headers_mapping",
+ "map_users",
+ "map_teams_access_group",
+ ]
+ extra_kwargs = {
+ "token": {"write_only": True},
+ }
+
+
+class FleetConnectorViewSet(UsedByMixin, ModelViewSet):
+ """FleetConnector Viewset"""
+
+ queryset = FleetConnector.objects.all()
+ serializer_class = FleetConnectorSerializer
+ filterset_fields = [
+ "name",
+ ]
+ search_fields = ["name"]
+ ordering = ["name"]
diff --git a/authentik/enterprise/endpoints/connectors/fleet/apps.py b/authentik/enterprise/endpoints/connectors/fleet/apps.py
new file mode 100644
index 0000000000..f14f9cab78
--- /dev/null
+++ b/authentik/enterprise/endpoints/connectors/fleet/apps.py
@@ -0,0 +1,12 @@
+"""authentik endpoints app config"""
+
+from authentik.enterprise.apps import EnterpriseConfig
+
+
+class AuthentikEnterpriseEndpointsConnectorFleetAppConfig(EnterpriseConfig):
+ """authentik endpoints app config"""
+
+ name = "authentik.enterprise.endpoints.connectors.fleet"
+ label = "authentik_endpoints_connectors_fleet"
+ verbose_name = "authentik Enterprise.Endpoints.Connectors.Fleet"
+ default = True
diff --git a/authentik/enterprise/endpoints/connectors/fleet/controller.py b/authentik/enterprise/endpoints/connectors/fleet/controller.py
new file mode 100644
index 0000000000..386bc8c3b2
--- /dev/null
+++ b/authentik/enterprise/endpoints/connectors/fleet/controller.py
@@ -0,0 +1,206 @@
+import re
+from typing import Any
+
+from django.db import transaction
+from requests import RequestException
+from rest_framework.exceptions import ValidationError
+
+from authentik.core.models import User
+from authentik.endpoints.controller import BaseController, ConnectorSyncException, EnrollmentMethods
+from authentik.endpoints.facts import (
+ DeviceFacts,
+ OSFamily,
+)
+from authentik.endpoints.models import (
+ Device,
+ DeviceAccessGroup,
+ DeviceConnection,
+ DeviceUserBinding,
+)
+from authentik.enterprise.endpoints.connectors.fleet.models import FleetConnector as DBC
+from authentik.events.utils import sanitize_item
+from authentik.lib.utils.http import get_http_session
+from authentik.policies.utils import delete_none_values
+
+
+class FleetController(BaseController[DBC]):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._session = get_http_session()
+ self._session.headers["Authorization"] = f"Bearer {self.connector.token}"
+ if self.connector.headers_mapping:
+ self._session.headers.update(
+ sanitize_item(
+ self.connector.headers_mapping.evaluate(
+ user=None,
+ request=None,
+ connector=self.connector,
+ )
+ )
+ )
+
+ @staticmethod
+ def vendor_identifier() -> str:
+ return "fleetdm.com"
+
+ def supported_enrollment_methods(self) -> list[EnrollmentMethods]:
+ return [EnrollmentMethods.AUTOMATIC_API]
+
+ def _url(self, path: str) -> str:
+ return f"{self.connector.url}{path}"
+
+ def _paginate_hosts(self):
+ try:
+ page = 0
+ while True:
+ self.logger.info("Fetching page of hosts...", page=page)
+ res = self._session.get(
+ self._url("/api/v1/fleet/hosts"),
+ params={
+ "order_key": "hardware_serial",
+ "page": page,
+ "per_page": 50,
+ "device_mapping": "true",
+ "populate_software": "true",
+ "populate_users": "true",
+ },
+ )
+ res.raise_for_status()
+ hosts: list[dict[str, Any]] = res.json()["hosts"]
+ if len(hosts) < 1:
+ self.logger.info("No more hosts, finished")
+ break
+ self.logger.info("Got hosts", count=len(hosts))
+ yield from hosts
+ page += 1
+ except RequestException as exc:
+ raise ConnectorSyncException(exc) from exc
+
+ @transaction.atomic
+ def sync_endpoints(self) -> None:
+ for host in self._paginate_hosts():
+ serial = host["hardware_serial"]
+ device, _ = Device.objects.get_or_create(
+ identifier=serial, defaults={"name": host["hostname"], "expiring": False}
+ )
+ connection, _ = DeviceConnection.objects.update_or_create(
+ device=device,
+ connector=self.connector,
+ )
+ if self.connector.map_users:
+ self.map_users(host, device)
+ if self.connector.map_teams_access_group:
+ self.map_access_group(host, device)
+ try:
+ connection.create_snapshot(self.convert_host_data(host))
+ except ValidationError as exc:
+ self.logger.warning(
+ "failed to create snapshot for host", host=host["hostname"], exc=exc
+ )
+
+ def map_users(self, host: dict[str, Any], device: Device):
+ for raw_user in host.get("device_mapping", []) or []:
+ user = User.objects.filter(email=raw_user["email"]).first()
+ if not user:
+ continue
+ DeviceUserBinding.objects.update_or_create(
+ target=device,
+ user=user,
+ create_defaults={
+ "is_primary": True,
+ "order": 0,
+ },
+ )
+
+ def map_access_group(self, host: dict[str, Any], device: Device):
+ team_name = host.get("team_name")
+ if not team_name:
+ return
+ group, _ = DeviceAccessGroup.objects.get_or_create(name=team_name)
+ group.attributes["io.goauthentik.endpoints.connectors.fleet.team_id"] = host["team_id"]
+ if device.access_group:
+ return
+ device.access_group = group
+ device.save()
+
+ @staticmethod
+ def os_family(host: dict[str, Any]) -> OSFamily:
+ if host["platform_like"] in ["debian", "rhel"]:
+ return OSFamily.linux
+ if host["platform_like"] == "windows":
+ return OSFamily.windows
+ if host["platform_like"] == "darwin":
+ return OSFamily.macOS
+ if host["platform"] == "android":
+ return OSFamily.android
+ if host["platform"] in ["ipados", "ios"]:
+ return OSFamily.iOS
+ return OSFamily.other
+
+ def map_os(self, host: dict[str, Any]) -> dict[str, str]:
+ family = FleetController.os_family(host)
+ os = {
+ "arch": self.or_none(host["cpu_type"]),
+ "family": family,
+ "name": self.or_none(host["platform_like"]),
+ "version": self.or_none(host["os_version"]),
+ }
+ if not host["os_version"]:
+ return delete_none_values(os)
+ version = re.search(r"(\d+\.(?:\d+\.?)+)", host["os_version"])
+ if not version:
+ return delete_none_values(os)
+ os["version"] = host["os_version"][version.start() :].strip()
+ os["name"] = host["os_version"][0 : version.start()].strip()
+ return delete_none_values(os)
+
+ def or_none(self, value) -> Any | None:
+ if value == "":
+ return None
+ return value
+
+ def convert_host_data(self, host: dict[str, Any]) -> dict[str, Any]:
+ """Convert host data from fleet to authentik"""
+ fleet_version = ""
+ for pkg in host.get("software") or []:
+ if pkg["name"] in ["fleet-osquery", "fleet-desktop"]:
+ fleet_version = pkg["version"]
+ data = {
+ "os": self.map_os(host),
+ "disks": [],
+ "network": delete_none_values(
+ {"hostname": self.or_none(host["hostname"]), "interfaces": []}
+ ),
+ "hardware": delete_none_values(
+ {
+ "model": self.or_none(host["hardware_model"]),
+ "manufacturer": self.or_none(host["hardware_vendor"]),
+ "serial": self.or_none(host["hardware_serial"]),
+ "cpu_name": self.or_none(host["cpu_brand"]),
+ "cpu_count": self.or_none(host["cpu_logical_cores"]),
+ "memory_bytes": self.or_none(host["memory"]),
+ }
+ ),
+ "software": [
+ delete_none_values(
+ {
+ "name": x["name"],
+ "version": x["version"],
+ "source": x["source"],
+ }
+ )
+ for x in (host.get("software") or [])
+ ],
+ "vendor": {
+ "fleetdm.com": {
+ "policies": [
+ delete_none_values({"name": policy["name"], "status": policy["response"]})
+ for policy in host.get("policies", [])
+ ],
+ "agent_version": fleet_version,
+ },
+ },
+ }
+ facts = DeviceFacts(data=data)
+ facts.is_valid(raise_exception=True)
+ return facts.validated_data
diff --git a/authentik/enterprise/endpoints/connectors/fleet/migrations/0001_initial.py b/authentik/enterprise/endpoints/connectors/fleet/migrations/0001_initial.py
new file mode 100644
index 0000000000..8beabaa466
--- /dev/null
+++ b/authentik/enterprise/endpoints/connectors/fleet/migrations/0001_initial.py
@@ -0,0 +1,53 @@
+# Generated by Django 5.2.10 on 2026-01-15 13:27
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ("authentik_endpoints", "0004_deviceaccessgroup_attributes"),
+ ("authentik_events", "0014_notification_hyperlink_notification_hyperlink_label_and_more"),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name="FleetConnector",
+ fields=[
+ (
+ "connector_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="authentik_endpoints.connector",
+ ),
+ ),
+ ("url", models.URLField()),
+ ("token", models.TextField()),
+ ("map_users", models.BooleanField(default=True)),
+ ("map_teams_access_group", models.BooleanField(default=False)),
+ (
+ "headers_mapping",
+ models.ForeignKey(
+ default=None,
+ help_text="Configure additional headers to be sent. Mapping should return a dictionary of key-value pairs",
+ null=True,
+ on_delete=django.db.models.deletion.SET_DEFAULT,
+ related_name="+",
+ to="authentik_events.notificationwebhookmapping",
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "Fleet Connector",
+ "verbose_name_plural": "Fleet Connectors",
+ },
+ bases=("authentik_endpoints.connector",),
+ ),
+ ]
diff --git a/authentik/enterprise/endpoints/connectors/fleet/migrations/__init__.py b/authentik/enterprise/endpoints/connectors/fleet/migrations/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/authentik/enterprise/endpoints/connectors/fleet/models.py b/authentik/enterprise/endpoints/connectors/fleet/models.py
new file mode 100644
index 0000000000..56fc0390d2
--- /dev/null
+++ b/authentik/enterprise/endpoints/connectors/fleet/models.py
@@ -0,0 +1,56 @@
+from typing import TYPE_CHECKING
+
+from django.db import models
+from django.templatetags.static import static
+from django.utils.translation import gettext_lazy as _
+from rest_framework.serializers import Serializer
+
+from authentik.endpoints.models import Connector
+
+if TYPE_CHECKING:
+ from authentik.enterprise.endpoints.connectors.fleet.controller import FleetController
+
+
+class FleetConnector(Connector):
+ """Ingest device data and policy compliance from a Fleet instance."""
+
+ url = models.URLField()
+ token = models.TextField()
+ headers_mapping = models.ForeignKey(
+ "authentik_events.NotificationWebhookMapping",
+ on_delete=models.SET_DEFAULT,
+ null=True,
+ default=None,
+ related_name="+",
+ help_text=_(
+ "Configure additional headers to be sent. "
+ "Mapping should return a dictionary of key-value pairs"
+ ),
+ )
+
+ map_users = models.BooleanField(default=True)
+ map_teams_access_group = models.BooleanField(default=False)
+
+ @property
+ def icon_url(self):
+ return static("authentik/connectors/fleet.svg")
+
+ @property
+ def serializer(self) -> type[Serializer]:
+ from authentik.enterprise.endpoints.connectors.fleet.api import FleetConnectorSerializer
+
+ return FleetConnectorSerializer
+
+ @property
+ def controller(self) -> type[FleetController]:
+ from authentik.enterprise.endpoints.connectors.fleet.controller import FleetController
+
+ return FleetController
+
+ @property
+ def component(self) -> str:
+ return "ak-endpoints-connector-fleet-form"
+
+ class Meta:
+ verbose_name = _("Fleet Connector")
+ verbose_name_plural = _("Fleet Connectors")
diff --git a/authentik/enterprise/endpoints/connectors/fleet/tests/__init__.py b/authentik/enterprise/endpoints/connectors/fleet/tests/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/authentik/enterprise/endpoints/connectors/fleet/tests/fixtures/host_fedora.json b/authentik/enterprise/endpoints/connectors/fleet/tests/fixtures/host_fedora.json
new file mode 100644
index 0000000000..6ceea2e14d
--- /dev/null
+++ b/authentik/enterprise/endpoints/connectors/fleet/tests/fixtures/host_fedora.json
@@ -0,0 +1,54 @@
+{
+ "created_at": "2026-01-23T15:32:17Z",
+ "updated_at": "2026-01-23T15:32:28Z",
+ "software": null,
+ "software_updated_at": "2026-01-23T15:32:17Z",
+ "id": 16,
+ "detail_updated_at": "1970-01-02T00:00:00Z",
+ "label_updated_at": "1970-01-02T00:00:00Z",
+ "policy_updated_at": "1970-01-02T00:00:00Z",
+ "last_enrolled_at": "2026-01-23T15:32:19Z",
+ "seen_time": "2026-01-23T15:32:21Z",
+ "refetch_requested": true,
+ "hostname": "fedora-workstation",
+ "uuid": "578c4d56-aff8-0793-14ae-7947392f5fec",
+ "platform": "rhel",
+ "osquery_version": "5.21.0",
+ "orbit_version": null,
+ "fleet_desktop_version": null,
+ "scripts_enabled": null,
+ "os_version": "Fedora Linux 43.0.0",
+ "build": "",
+ "platform_like": "rhel",
+ "code_name": "",
+ "uptime": 0,
+ "memory": 4092518400,
+ "cpu_type": "x86_64",
+ "cpu_subtype": "165",
+ "cpu_brand": "Intel(R) Core(TM) i5-10500T CPU @ 2.30GHz",
+ "cpu_physical_cores": 2,
+ "cpu_logical_cores": 2,
+ "hardware_vendor": "VMware, Inc.",
+ "hardware_model": "VMware20,1",
+ "hardware_version": "None",
+ "hardware_serial": "VMware-56 4d 8c 57 f8 af 93 07-14 ae 79 47 39 2f 5f ec",
+ "computer_name": "fedora-workstation",
+ "public_ip": "",
+ "primary_ip": "",
+ "primary_mac": "",
+ "distributed_interval": 10,
+ "config_tls_refresh": 0,
+ "logger_tls_period": 10,
+ "team_id": 2,
+ "pack_stats": null,
+ "team_name": "prod",
+ "gigs_disk_space_available": 0,
+ "percent_disk_space_available": 0,
+ "gigs_total_disk_space": 0,
+ "gigs_all_disk_space": null,
+ "issues": {
+ "failing_policies_count": 0,
+ "critical_vulnerabilities_count": 0,
+ "total_issues_count": 0
+ }
+}
diff --git a/authentik/enterprise/endpoints/connectors/fleet/tests/fixtures/host_macos.json b/authentik/enterprise/endpoints/connectors/fleet/tests/fixtures/host_macos.json
new file mode 100644
index 0000000000..fad990794a
--- /dev/null
+++ b/authentik/enterprise/endpoints/connectors/fleet/tests/fixtures/host_macos.json
@@ -0,0 +1,68 @@
+{
+ "created_at": "2025-06-25T22:21:35Z",
+ "updated_at": "2025-12-20T11:42:09Z",
+ "software": null,
+ "software_updated_at": "2025-10-22T02:24:25Z",
+ "id": 1,
+ "detail_updated_at": "2025-10-23T23:30:31Z",
+ "label_updated_at": "2025-10-23T23:30:31Z",
+ "policy_updated_at": "2025-10-23T23:02:11Z",
+ "last_enrolled_at": "2025-06-25T22:21:37Z",
+ "seen_time": "2025-10-23T23:59:08Z",
+ "refetch_requested": false,
+ "hostname": "jens-mac-vm.local",
+ "uuid": "C8B98348-A0A6-5838-A321-57B59D788269",
+ "platform": "darwin",
+ "osquery_version": "5.19.0",
+ "orbit_version": null,
+ "fleet_desktop_version": null,
+ "scripts_enabled": null,
+ "os_version": "macOS 26.0.1",
+ "build": "25A362",
+ "platform_like": "darwin",
+ "code_name": "",
+ "uptime": 256356000000000,
+ "memory": 4294967296,
+ "cpu_type": "arm64e",
+ "cpu_subtype": "ARM64E",
+ "cpu_brand": "Apple M1 Pro (Virtual)",
+ "cpu_physical_cores": 8,
+ "cpu_logical_cores": 8,
+ "hardware_vendor": "Apple Inc.",
+ "hardware_model": "VirtualMac2,1",
+ "hardware_version": "",
+ "hardware_serial": "Z5DDF07GK6",
+ "computer_name": "jens-mac-vm",
+ "public_ip": "92.116.179.252",
+ "primary_ip": "192.168.85.3",
+ "primary_mac": "e6:9d:21:c2:2f:19",
+ "distributed_interval": 10,
+ "config_tls_refresh": 60,
+ "logger_tls_period": 10,
+ "team_id": 2,
+ "pack_stats": null,
+ "team_name": "prod",
+ "gigs_disk_space_available": 23.82,
+ "percent_disk_space_available": 37,
+ "gigs_total_disk_space": 62.83,
+ "gigs_all_disk_space": null,
+ "issues": {
+ "failing_policies_count": 1,
+ "critical_vulnerabilities_count": 2,
+ "total_issues_count": 3
+ },
+ "device_mapping": null,
+ "mdm": {
+ "enrollment_status": "On (manual)",
+ "dep_profile_error": false,
+ "server_url": "https://fleet.beryjuio-home.k8s.beryju.io/mdm/apple/mdm",
+ "name": "Fleet",
+ "encryption_key_available": false,
+ "connected_to_fleet": true
+ },
+ "refetch_critical_queries_until": null,
+ "last_restarted_at": "2025-10-21T00:17:55Z",
+ "status": "offline",
+ "display_text": "jens-mac-vm.local",
+ "display_name": "jens-mac-vm"
+}
diff --git a/authentik/enterprise/endpoints/connectors/fleet/tests/fixtures/host_ubuntu.json b/authentik/enterprise/endpoints/connectors/fleet/tests/fixtures/host_ubuntu.json
new file mode 100644
index 0000000000..22e90f2c0b
--- /dev/null
+++ b/authentik/enterprise/endpoints/connectors/fleet/tests/fixtures/host_ubuntu.json
@@ -0,0 +1,68 @@
+{
+ "created_at": "2025-11-01T17:25:34Z",
+ "updated_at": "2026-01-23T12:58:55Z",
+ "software": null,
+ "software_updated_at": "2026-01-23T12:58:55Z",
+ "id": 14,
+ "detail_updated_at": "2026-01-23T12:58:55Z",
+ "label_updated_at": "2026-01-23T12:58:55Z",
+ "policy_updated_at": "2026-01-23T12:29:58Z",
+ "last_enrolled_at": "2025-11-01T17:25:38Z",
+ "seen_time": "2026-01-23T13:17:27Z",
+ "refetch_requested": false,
+ "hostname": "ubuntu-desktop",
+ "uuid": "5a4a4d56-22b0-d77b-9ba5-0bdc8ff23b60",
+ "platform": "ubuntu",
+ "osquery_version": "5.21.0",
+ "orbit_version": null,
+ "fleet_desktop_version": null,
+ "scripts_enabled": null,
+ "os_version": "Ubuntu 24.04.3 LTS",
+ "build": "",
+ "platform_like": "debian",
+ "code_name": "noble",
+ "uptime": 1631433000000000,
+ "memory": 2062721024,
+ "cpu_type": "x86_64",
+ "cpu_subtype": "165",
+ "cpu_brand": "Intel(R) Core(TM) i5-10500T CPU @ 2.30GHz",
+ "cpu_physical_cores": 2,
+ "cpu_logical_cores": 2,
+ "hardware_vendor": "VMware, Inc.",
+ "hardware_model": "VMware20,1",
+ "hardware_version": "None",
+ "hardware_serial": "VMware-56 4d 4a 5a b0 22 7b d7-9b a5 0b dc 8f f2 3b 60",
+ "computer_name": "ubuntu-desktop",
+ "public_ip": "92.116.178.120",
+ "primary_ip": "10.120.20.61",
+ "primary_mac": "00:0c:29:f2:3b:60",
+ "distributed_interval": 10,
+ "config_tls_refresh": 60,
+ "logger_tls_period": 10,
+ "team_id": 2,
+ "pack_stats": null,
+ "team_name": "prod",
+ "gigs_disk_space_available": 7.37,
+ "percent_disk_space_available": 31,
+ "gigs_total_disk_space": 23.08,
+ "gigs_all_disk_space": 23.08,
+ "issues": {
+ "failing_policies_count": 0,
+ "critical_vulnerabilities_count": 0,
+ "total_issues_count": 0
+ },
+ "device_mapping": null,
+ "mdm": {
+ "enrollment_status": null,
+ "dep_profile_error": false,
+ "server_url": null,
+ "name": "",
+ "encryption_key_available": false,
+ "connected_to_fleet": false
+ },
+ "refetch_critical_queries_until": null,
+ "last_restarted_at": "2026-01-04T15:48:22.390118Z",
+ "status": "online",
+ "display_text": "ubuntu-desktop",
+ "display_name": "ubuntu-desktop"
+}
diff --git a/authentik/enterprise/endpoints/connectors/fleet/tests/fixtures/host_windows.json b/authentik/enterprise/endpoints/connectors/fleet/tests/fixtures/host_windows.json
new file mode 100644
index 0000000000..1d6e3e114f
--- /dev/null
+++ b/authentik/enterprise/endpoints/connectors/fleet/tests/fixtures/host_windows.json
@@ -0,0 +1,68 @@
+{
+ "created_at": "2025-10-19T12:44:09Z",
+ "updated_at": "2026-01-23T13:11:45Z",
+ "software": null,
+ "software_updated_at": "2026-01-22T06:57:30Z",
+ "id": 13,
+ "detail_updated_at": "2026-01-23T12:51:35Z",
+ "label_updated_at": "2026-01-23T12:51:35Z",
+ "policy_updated_at": "2026-01-23T13:11:45Z",
+ "last_enrolled_at": "2025-11-05T20:27:14Z",
+ "seen_time": "2026-01-23T13:17:33Z",
+ "refetch_requested": false,
+ "hostname": "windows-server",
+ "uuid": "CFF12F42-9F7D-A575-2C48-01BDC6A733FB",
+ "platform": "windows",
+ "osquery_version": "5.21.0",
+ "orbit_version": null,
+ "fleet_desktop_version": null,
+ "scripts_enabled": null,
+ "os_version": "Windows Server 2022 Datacenter 21H2 10.0.20348.4405",
+ "build": "20348",
+ "platform_like": "windows",
+ "code_name": "Microsoft Windows Server 2022 Datacenter",
+ "uptime": 217075000000000,
+ "memory": 4294967296,
+ "cpu_type": "x86_64",
+ "cpu_subtype": "-1",
+ "cpu_brand": "Intel(R) Core(TM) i5-10500T CPU @ 2.30GHz",
+ "cpu_physical_cores": 1,
+ "cpu_logical_cores": 2,
+ "hardware_vendor": "VMware, Inc.",
+ "hardware_model": "VMware20,1",
+ "hardware_version": "-1",
+ "hardware_serial": "VMware-42 2f f1 cf 7d 9f 75 a5-2c 48 01 bd c6 a7 33 fb",
+ "computer_name": "WINDOWS-SERVER",
+ "public_ip": "92.116.178.120",
+ "primary_ip": "10.120.20.78",
+ "primary_mac": "00:50:56:af:fb:3a",
+ "distributed_interval": 10,
+ "config_tls_refresh": 60,
+ "logger_tls_period": 10,
+ "team_id": 2,
+ "pack_stats": null,
+ "team_name": "prod",
+ "gigs_disk_space_available": 68,
+ "percent_disk_space_available": 71,
+ "gigs_total_disk_space": 96,
+ "gigs_all_disk_space": null,
+ "issues": {
+ "failing_policies_count": 0,
+ "critical_vulnerabilities_count": 5,
+ "total_issues_count": 5
+ },
+ "device_mapping": null,
+ "mdm": {
+ "enrollment_status": null,
+ "dep_profile_error": false,
+ "server_url": null,
+ "name": "",
+ "encryption_key_available": false,
+ "connected_to_fleet": false
+ },
+ "refetch_critical_queries_until": null,
+ "last_restarted_at": "2026-01-21T00:33:38.178036Z",
+ "status": "online",
+ "display_text": "windows-server",
+ "display_name": "WINDOWS-SERVER"
+}
diff --git a/authentik/enterprise/endpoints/connectors/fleet/tests/test_connector.py b/authentik/enterprise/endpoints/connectors/fleet/tests/test_connector.py
new file mode 100644
index 0000000000..8cf03c2494
--- /dev/null
+++ b/authentik/enterprise/endpoints/connectors/fleet/tests/test_connector.py
@@ -0,0 +1,133 @@
+from json import loads
+
+from requests_mock import Mocker
+from rest_framework.test import APITestCase
+
+from authentik.endpoints.facts import OSFamily
+from authentik.endpoints.models import Device
+from authentik.enterprise.endpoints.connectors.fleet.models import FleetConnector
+from authentik.events.models import NotificationWebhookMapping
+from authentik.lib.generators import generate_id
+from authentik.lib.tests.utils import load_fixture
+
+TEST_HOST_UBUNTU = loads(load_fixture("fixtures/host_ubuntu.json"))
+TEST_HOST_FEDORA = loads(load_fixture("fixtures/host_fedora.json"))
+TEST_HOST_MACOS = loads(load_fixture("fixtures/host_macos.json"))
+TEST_HOST_WINDOWS = loads(load_fixture("fixtures/host_windows.json"))
+
+TEST_HOST = {"hosts": [TEST_HOST_UBUNTU, TEST_HOST_MACOS, TEST_HOST_WINDOWS, TEST_HOST_FEDORA]}
+
+
+class TestFleetConnector(APITestCase):
+ def setUp(self):
+ self.connector = FleetConnector.objects.create(
+ name=generate_id(), url="http://localhost", token=generate_id()
+ )
+
+ def test_sync(self):
+ controller = self.connector.controller(self.connector)
+ with Mocker() as mock:
+ mock.get(
+ "http://localhost/api/v1/fleet/hosts?order_key=hardware_serial&page=0&per_page=50&device_mapping=true&populate_software=true&populate_users=true",
+ json=TEST_HOST,
+ )
+ mock.get(
+ "http://localhost/api/v1/fleet/hosts?order_key=hardware_serial&page=1&per_page=50&device_mapping=true&populate_software=true&populate_users=true",
+ json={"hosts": []},
+ )
+ controller.sync_endpoints()
+ device = Device.objects.filter(
+ identifier="VMware-56 4d 4a 5a b0 22 7b d7-9b a5 0b dc 8f f2 3b 60"
+ ).first()
+ self.assertIsNotNone(device)
+ self.assertEqual(
+ device.cached_facts.data,
+ {
+ "os": {
+ "arch": "x86_64",
+ "name": "Ubuntu",
+ "family": "linux",
+ "version": "24.04.3 LTS",
+ },
+ "disks": [],
+ "vendor": {"fleetdm.com": {"policies": [], "agent_version": ""}},
+ "network": {"hostname": "ubuntu-desktop", "interfaces": []},
+ "hardware": {
+ "model": "VMware20,1",
+ "serial": "VMware-56 4d 4a 5a b0 22 7b d7-9b a5 0b dc 8f f2 3b 60",
+ "cpu_count": 2,
+ "cpu_name": "Intel(R) Core(TM) i5-10500T CPU @ 2.30GHz",
+ "manufacturer": "VMware, Inc.",
+ "memory_bytes": 2062721024,
+ },
+ "software": [],
+ },
+ )
+
+ def test_sync_headers(self):
+ mapping = NotificationWebhookMapping.objects.create(
+ name=generate_id(), expression="""return {"foo": "bar"}"""
+ )
+ self.connector.headers_mapping = mapping
+ self.connector.save()
+ controller = self.connector.controller(self.connector)
+ with Mocker() as mock:
+ mock.get(
+ "http://localhost/api/v1/fleet/hosts?order_key=hardware_serial&page=0&per_page=50&device_mapping=true&populate_software=true&populate_users=true",
+ json=TEST_HOST,
+ )
+ mock.get(
+ "http://localhost/api/v1/fleet/hosts?order_key=hardware_serial&page=1&per_page=50&device_mapping=true&populate_software=true&populate_users=true",
+ json={"hosts": []},
+ )
+ controller.sync_endpoints()
+ self.assertEqual(mock.call_count, 2)
+ self.assertEqual(mock.request_history[0].method, "GET")
+ self.assertEqual(mock.request_history[0].headers["foo"], "bar")
+ self.assertEqual(mock.request_history[1].method, "GET")
+ self.assertEqual(mock.request_history[1].headers["foo"], "bar")
+
+ def test_map_host_linux(self):
+ controller = self.connector.controller(self.connector)
+ self.assertEqual(
+ controller.map_os(TEST_HOST_UBUNTU),
+ {
+ "arch": "x86_64",
+ "family": OSFamily.linux,
+ "name": "Ubuntu",
+ "version": "24.04.3 LTS",
+ },
+ )
+ self.assertEqual(
+ controller.map_os(TEST_HOST_FEDORA),
+ {
+ "arch": "x86_64",
+ "family": OSFamily.linux,
+ "name": "Fedora Linux",
+ "version": "43.0.0",
+ },
+ )
+
+ def test_map_host_windows(self):
+ controller = self.connector.controller(self.connector)
+ self.assertEqual(
+ controller.map_os(TEST_HOST_WINDOWS),
+ {
+ "arch": "x86_64",
+ "family": OSFamily.windows,
+ "name": "Windows Server 2022 Datacenter 21H2",
+ "version": "10.0.20348.4405",
+ },
+ )
+
+ def test_map_host_macos(self):
+ controller = self.connector.controller(self.connector)
+ self.assertEqual(
+ controller.map_os(TEST_HOST_MACOS),
+ {
+ "arch": "arm64e",
+ "family": OSFamily.macOS,
+ "name": "macOS",
+ "version": "26.0.1",
+ },
+ )
diff --git a/authentik/enterprise/endpoints/connectors/fleet/urls.py b/authentik/enterprise/endpoints/connectors/fleet/urls.py
new file mode 100644
index 0000000000..212a5bf79d
--- /dev/null
+++ b/authentik/enterprise/endpoints/connectors/fleet/urls.py
@@ -0,0 +1,3 @@
+from authentik.enterprise.endpoints.connectors.fleet.api import FleetConnectorViewSet
+
+api_urlpatterns = [("endpoints/fleet/connectors", FleetConnectorViewSet)]
diff --git a/authentik/enterprise/settings.py b/authentik/enterprise/settings.py
index 5014743ab4..e8a4164de1 100644
--- a/authentik/enterprise/settings.py
+++ b/authentik/enterprise/settings.py
@@ -3,6 +3,7 @@
TENANT_APPS = [
"authentik.enterprise.audit",
"authentik.enterprise.endpoints.connectors.agent",
+ "authentik.enterprise.endpoints.connectors.fleet",
"authentik.enterprise.policies.unique_password",
"authentik.enterprise.providers.google_workspace",
"authentik.enterprise.providers.microsoft_entra",
diff --git a/blueprints/schema.json b/blueprints/schema.json
index 4b2cbf9418..73acb24bf5 100644
--- a/blueprints/schema.json
+++ b/blueprints/schema.json
@@ -656,6 +656,46 @@
}
}
},
+ {
+ "type": "object",
+ "required": [
+ "model",
+ "identifiers"
+ ],
+ "properties": {
+ "model": {
+ "const": "authentik_endpoints_connectors_fleet.fleetconnector"
+ },
+ "id": {
+ "type": "string"
+ },
+ "state": {
+ "type": "string",
+ "enum": [
+ "absent",
+ "created",
+ "must_created",
+ "present"
+ ],
+ "default": "present"
+ },
+ "conditions": {
+ "type": "array",
+ "items": {
+ "type": "boolean"
+ }
+ },
+ "permissions": {
+ "$ref": "#/$defs/model_authentik_endpoints_connectors_fleet.fleetconnector_permissions"
+ },
+ "attrs": {
+ "$ref": "#/$defs/model_authentik_endpoints_connectors_fleet.fleetconnector"
+ },
+ "identifiers": {
+ "$ref": "#/$defs/model_authentik_endpoints_connectors_fleet.fleetconnector"
+ }
+ }
+ },
{
"type": "object",
"required": [
@@ -5432,6 +5472,10 @@
"authentik_endpoints_connectors_agent.view_devicetoken",
"authentik_endpoints_connectors_agent.view_enrollment_token_key",
"authentik_endpoints_connectors_agent.view_enrollmenttoken",
+ "authentik_endpoints_connectors_fleet.add_fleetconnector",
+ "authentik_endpoints_connectors_fleet.change_fleetconnector",
+ "authentik_endpoints_connectors_fleet.delete_fleetconnector",
+ "authentik_endpoints_connectors_fleet.view_fleetconnector",
"authentik_enterprise.add_license",
"authentik_enterprise.add_licenseusage",
"authentik_enterprise.change_license",
@@ -6319,6 +6363,11 @@
"type": "string",
"minLength": 1,
"title": "Name"
+ },
+ "attributes": {
+ "type": "object",
+ "additionalProperties": true,
+ "title": "Attributes"
}
},
"required": []
@@ -6477,6 +6526,78 @@
}
}
},
+ "model_authentik_endpoints_connectors_fleet.fleetconnector": {
+ "type": "object",
+ "properties": {
+ "connector_uuid": {
+ "type": "string",
+ "format": "uuid",
+ "title": "Connector uuid"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "title": "Name"
+ },
+ "enabled": {
+ "type": "boolean",
+ "title": "Enabled"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "maxLength": 200,
+ "minLength": 1,
+ "title": "Url"
+ },
+ "token": {
+ "type": "string",
+ "minLength": 1,
+ "title": "Token"
+ },
+ "headers_mapping": {
+ "type": "string",
+ "format": "uuid",
+ "title": "Headers mapping",
+ "description": "Configure additional headers to be sent. Mapping should return a dictionary of key-value pairs"
+ },
+ "map_users": {
+ "type": "boolean",
+ "title": "Map users"
+ },
+ "map_teams_access_group": {
+ "type": "boolean",
+ "title": "Map teams access group"
+ }
+ },
+ "required": []
+ },
+ "model_authentik_endpoints_connectors_fleet.fleetconnector_permissions": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "permission"
+ ],
+ "properties": {
+ "permission": {
+ "type": "string",
+ "enum": [
+ "add_fleetconnector",
+ "change_fleetconnector",
+ "delete_fleetconnector",
+ "view_fleetconnector"
+ ]
+ },
+ "user": {
+ "type": "integer"
+ },
+ "role": {
+ "type": "string"
+ }
+ }
+ }
+ },
"model_authentik_enterprise.license": {
"type": "object",
"properties": {
@@ -8161,6 +8282,7 @@
"authentik.blueprints",
"authentik.enterprise.audit",
"authentik.enterprise.endpoints.connectors.agent",
+ "authentik.enterprise.endpoints.connectors.fleet",
"authentik.enterprise.policies.unique_password",
"authentik.enterprise.providers.google_workspace",
"authentik.enterprise.providers.microsoft_entra",
@@ -8288,6 +8410,7 @@
"authentik_tasks_schedules.schedule",
"authentik_brands.brand",
"authentik_blueprints.blueprintinstance",
+ "authentik_endpoints_connectors_fleet.fleetconnector",
"authentik_policies_unique_password.uniquepasswordpolicy",
"authentik_providers_google_workspace.googleworkspaceprovider",
"authentik_providers_google_workspace.googleworkspaceprovidermapping",
@@ -10551,6 +10674,10 @@
"authentik_endpoints_connectors_agent.view_devicetoken",
"authentik_endpoints_connectors_agent.view_enrollment_token_key",
"authentik_endpoints_connectors_agent.view_enrollmenttoken",
+ "authentik_endpoints_connectors_fleet.add_fleetconnector",
+ "authentik_endpoints_connectors_fleet.change_fleetconnector",
+ "authentik_endpoints_connectors_fleet.delete_fleetconnector",
+ "authentik_endpoints_connectors_fleet.view_fleetconnector",
"authentik_enterprise.add_license",
"authentik_enterprise.add_licenseusage",
"authentik_enterprise.change_license",
diff --git a/schema.yml b/schema.yml
index f3a6541071..50b8a6bd00 100644
--- a/schema.yml
+++ b/schema.yml
@@ -6447,6 +6447,196 @@ paths:
$ref: '#/components/responses/ValidationErrorResponse'
'403':
$ref: '#/components/responses/GenericErrorResponse'
+ /endpoints/fleet/connectors/:
+ get:
+ operationId: endpoints_fleet_connectors_list
+ description: FleetConnector Viewset
+ parameters:
+ - $ref: '#/components/parameters/QueryName'
+ - $ref: '#/components/parameters/QueryPaginationOrdering'
+ - $ref: '#/components/parameters/QueryPaginationPage'
+ - $ref: '#/components/parameters/QueryPaginationPageSize'
+ - $ref: '#/components/parameters/QuerySearch'
+ tags:
+ - endpoints
+ security:
+ - authentik: []
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PaginatedFleetConnectorList'
+ description: ''
+ '400':
+ $ref: '#/components/responses/ValidationErrorResponse'
+ '403':
+ $ref: '#/components/responses/GenericErrorResponse'
+ post:
+ operationId: endpoints_fleet_connectors_create
+ description: FleetConnector Viewset
+ tags:
+ - endpoints
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FleetConnectorRequest'
+ required: true
+ security:
+ - authentik: []
+ responses:
+ '201':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FleetConnector'
+ description: ''
+ '400':
+ $ref: '#/components/responses/ValidationErrorResponse'
+ '403':
+ $ref: '#/components/responses/GenericErrorResponse'
+ /endpoints/fleet/connectors/{connector_uuid}/:
+ get:
+ operationId: endpoints_fleet_connectors_retrieve
+ description: FleetConnector Viewset
+ parameters:
+ - in: path
+ name: connector_uuid
+ schema:
+ type: string
+ format: uuid
+ description: A UUID string identifying this Fleet Connector.
+ required: true
+ tags:
+ - endpoints
+ security:
+ - authentik: []
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FleetConnector'
+ description: ''
+ '400':
+ $ref: '#/components/responses/ValidationErrorResponse'
+ '403':
+ $ref: '#/components/responses/GenericErrorResponse'
+ put:
+ operationId: endpoints_fleet_connectors_update
+ description: FleetConnector Viewset
+ parameters:
+ - in: path
+ name: connector_uuid
+ schema:
+ type: string
+ format: uuid
+ description: A UUID string identifying this Fleet Connector.
+ required: true
+ tags:
+ - endpoints
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FleetConnectorRequest'
+ required: true
+ security:
+ - authentik: []
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FleetConnector'
+ description: ''
+ '400':
+ $ref: '#/components/responses/ValidationErrorResponse'
+ '403':
+ $ref: '#/components/responses/GenericErrorResponse'
+ patch:
+ operationId: endpoints_fleet_connectors_partial_update
+ description: FleetConnector Viewset
+ parameters:
+ - in: path
+ name: connector_uuid
+ schema:
+ type: string
+ format: uuid
+ description: A UUID string identifying this Fleet Connector.
+ required: true
+ tags:
+ - endpoints
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PatchedFleetConnectorRequest'
+ security:
+ - authentik: []
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FleetConnector'
+ description: ''
+ '400':
+ $ref: '#/components/responses/ValidationErrorResponse'
+ '403':
+ $ref: '#/components/responses/GenericErrorResponse'
+ delete:
+ operationId: endpoints_fleet_connectors_destroy
+ description: FleetConnector Viewset
+ parameters:
+ - in: path
+ name: connector_uuid
+ schema:
+ type: string
+ format: uuid
+ description: A UUID string identifying this Fleet Connector.
+ required: true
+ tags:
+ - endpoints
+ security:
+ - authentik: []
+ responses:
+ '204':
+ description: No response body
+ '400':
+ $ref: '#/components/responses/ValidationErrorResponse'
+ '403':
+ $ref: '#/components/responses/GenericErrorResponse'
+ /endpoints/fleet/connectors/{connector_uuid}/used_by/:
+ get:
+ operationId: endpoints_fleet_connectors_used_by_list
+ description: Get a list of all objects that use this object
+ parameters:
+ - in: path
+ name: connector_uuid
+ schema:
+ type: string
+ format: uuid
+ description: A UUID string identifying this Fleet Connector.
+ required: true
+ tags:
+ - endpoints
+ security:
+ - authentik: []
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/UsedBy'
+ description: ''
+ '400':
+ $ref: '#/components/responses/ValidationErrorResponse'
+ '403':
+ $ref: '#/components/responses/GenericErrorResponse'
/enterprise/license/:
get:
operationId: enterprise_license_list
@@ -19897,6 +20087,7 @@ paths:
- authentik_endpoints_connectors_agent.agentconnector
- authentik_endpoints_connectors_agent.agentdeviceuserbinding
- authentik_endpoints_connectors_agent.enrollmenttoken
+ - authentik_endpoints_connectors_fleet.fleetconnector
- authentik_enterprise.license
- authentik_events.event
- authentik_events.notification
@@ -33121,6 +33312,7 @@ components:
- authentik.blueprints
- authentik.enterprise.audit
- authentik.enterprise.endpoints.connectors.agent
+ - authentik.enterprise.endpoints.connectors.fleet
- authentik.enterprise.policies.unique_password
- authentik.enterprise.providers.google_workspace
- authentik.enterprise.providers.microsoft_entra
@@ -36019,6 +36211,9 @@ components:
readOnly: true
name:
type: string
+ attributes:
+ type: object
+ additionalProperties: {}
required:
- name
- pbm_uuid
@@ -36028,6 +36223,9 @@ components:
name:
type: string
minLength: 1
+ attributes:
+ type: object
+ additionalProperties: {}
required:
- name
DeviceChallenge:
@@ -37801,6 +37999,89 @@ components:
default: media
required:
- file
+ FleetConnector:
+ type: object
+ description: FleetConnector Serializer
+ properties:
+ connector_uuid:
+ type: string
+ format: uuid
+ name:
+ type: string
+ enabled:
+ type: boolean
+ component:
+ type: string
+ description: Get object component so that we know how to edit the object
+ readOnly: true
+ verbose_name:
+ type: string
+ description: Return object's verbose_name
+ readOnly: true
+ verbose_name_plural:
+ type: string
+ description: Return object's plural verbose_name
+ readOnly: true
+ meta_model_name:
+ type: string
+ description: Return internal model name
+ readOnly: true
+ url:
+ type: string
+ format: uri
+ maxLength: 200
+ headers_mapping:
+ type: string
+ format: uuid
+ nullable: true
+ description: Configure additional headers to be sent. Mapping should return
+ a dictionary of key-value pairs
+ map_users:
+ type: boolean
+ map_teams_access_group:
+ type: boolean
+ required:
+ - component
+ - meta_model_name
+ - name
+ - url
+ - verbose_name
+ - verbose_name_plural
+ FleetConnectorRequest:
+ type: object
+ description: FleetConnector Serializer
+ properties:
+ connector_uuid:
+ type: string
+ format: uuid
+ name:
+ type: string
+ minLength: 1
+ enabled:
+ type: boolean
+ url:
+ type: string
+ format: uri
+ minLength: 1
+ maxLength: 200
+ token:
+ type: string
+ writeOnly: true
+ minLength: 1
+ headers_mapping:
+ type: string
+ format: uuid
+ nullable: true
+ description: Configure additional headers to be sent. Mapping should return
+ a dictionary of key-value pairs
+ map_users:
+ type: boolean
+ map_teams_access_group:
+ type: boolean
+ required:
+ - name
+ - token
+ - url
Flow:
type: object
description: Flow Serializer
@@ -41546,6 +41827,7 @@ components:
- authentik_tasks_schedules.schedule
- authentik_brands.brand
- authentik_blueprints.blueprintinstance
+ - authentik_endpoints_connectors_fleet.fleetconnector
- authentik_policies_unique_password.uniquepasswordpolicy
- authentik_providers_google_workspace.googleworkspaceprovider
- authentik_providers_google_workspace.googleworkspaceprovidermapping
@@ -42743,13 +43025,22 @@ components:
- userinfo_endpoint
OperatingSystem:
type: object
+ description: |-
+ For example:
+ {"family":"linux","name":"Ubuntu","version":"24.04.3 LTS (Noble Numbat)","arch":"amd64"}
+ {"family": "windows","name":"Server 2022 Datacenter","version":"10.0.20348.4405","arch":"amd64"}
+ {"family": "windows","name":"Server 2022 Datacenter","version":"10.0.20348.4405","arch":"amd64"}
+ {"family": "mac_os", "name": "", "version": "26.2", "arch": "arm64"}
properties:
family:
$ref: '#/components/schemas/DeviceFactsOSFamily'
name:
type: string
+ description: Operating System name, such as 'Server 2022' or 'Ubuntu'
version:
type: string
+ description: Operating System version, must always be the version number
+ but may contain build name
arch:
type: string
required:
@@ -42757,15 +43048,24 @@ components:
- family
OperatingSystemRequest:
type: object
+ description: |-
+ For example:
+ {"family":"linux","name":"Ubuntu","version":"24.04.3 LTS (Noble Numbat)","arch":"amd64"}
+ {"family": "windows","name":"Server 2022 Datacenter","version":"10.0.20348.4405","arch":"amd64"}
+ {"family": "windows","name":"Server 2022 Datacenter","version":"10.0.20348.4405","arch":"amd64"}
+ {"family": "mac_os", "name": "", "version": "26.2", "arch": "arm64"}
properties:
family:
$ref: '#/components/schemas/DeviceFactsOSFamily'
name:
type: string
minLength: 1
+ description: Operating System name, such as 'Server 2022' or 'Ubuntu'
version:
type: string
minLength: 1
+ description: Operating System version, must always be the version number
+ but may contain build name
arch:
type: string
minLength: 1
@@ -43535,6 +43835,21 @@ components:
required:
- pagination
- results
+ PaginatedFleetConnectorList:
+ type: object
+ properties:
+ pagination:
+ $ref: '#/components/schemas/Pagination'
+ results:
+ type: array
+ items:
+ $ref: '#/components/schemas/FleetConnector'
+ autocomplete:
+ $ref: '#/components/schemas/Autocomplete'
+ required:
+ - pagination
+ - results
+ - autocomplete
PaginatedFlowList:
type: object
properties:
@@ -46154,6 +46469,9 @@ components:
name:
type: string
minLength: 1
+ attributes:
+ type: object
+ additionalProperties: {}
PatchedDeviceUserBindingRequest:
type: object
description: PolicyBinding Serializer
@@ -46498,6 +46816,37 @@ components:
expression:
type: string
minLength: 1
+ PatchedFleetConnectorRequest:
+ type: object
+ description: FleetConnector Serializer
+ properties:
+ connector_uuid:
+ type: string
+ format: uuid
+ name:
+ type: string
+ minLength: 1
+ enabled:
+ type: boolean
+ url:
+ type: string
+ format: uri
+ minLength: 1
+ maxLength: 200
+ token:
+ type: string
+ writeOnly: true
+ minLength: 1
+ headers_mapping:
+ type: string
+ format: uuid
+ nullable: true
+ description: Configure additional headers to be sent. Mapping should return
+ a dictionary of key-value pairs
+ map_users:
+ type: boolean
+ map_teams_access_group:
+ type: boolean
PatchedFlowRequest:
type: object
description: Flow Serializer
@@ -55950,6 +56299,7 @@ components:
enum:
- goauthentik.io/@merged
- goauthentik.io/platform
+ - fleetdm.com
type: string
Version:
type: object
diff --git a/web/authentik/connectors/fleet.svg b/web/authentik/connectors/fleet.svg
new file mode 100644
index 0000000000..2c8e236e56
--- /dev/null
+++ b/web/authentik/connectors/fleet.svg
@@ -0,0 +1,8 @@
+
diff --git a/web/src/admin/endpoints/connectors/ConnectorViewPage.ts b/web/src/admin/endpoints/connectors/ConnectorViewPage.ts
index c98b9eb360..aa9f8b420e 100644
--- a/web/src/admin/endpoints/connectors/ConnectorViewPage.ts
+++ b/web/src/admin/endpoints/connectors/ConnectorViewPage.ts
@@ -1,4 +1,5 @@
import "#admin/endpoints/connectors/agent/AgentConnectorViewPage";
+import "#admin/endpoints/connectors/fleet/FleetConnectorViewPage";
import "#elements/EmptyState";
import "#elements/buttons/SpinnerButton/ak-spinner-button";
@@ -41,6 +42,10 @@ export class ConnectorViewPage extends AKElement {
return html`
Invalid connector type ${this.connector?.component}
`; } diff --git a/web/src/admin/endpoints/connectors/ConnectorWizard.ts b/web/src/admin/endpoints/connectors/ConnectorWizard.ts index b9cc724f62..e6248c8d1c 100644 --- a/web/src/admin/endpoints/connectors/ConnectorWizard.ts +++ b/web/src/admin/endpoints/connectors/ConnectorWizard.ts @@ -1,5 +1,6 @@ import "#admin/common/ak-license-notice"; import "#admin/endpoints/connectors/agent/AgentConnectorForm"; +import "#admin/endpoints/connectors/fleet/FleetConnectorForm"; import "#elements/wizard/FormWizardPage"; import "#elements/wizard/TypeCreateWizardPage"; import "#elements/wizard/Wizard"; @@ -8,6 +9,7 @@ import { DEFAULT_CONFIG } from "#common/api/config"; import { AKElement } from "#elements/Base"; import { StrictUnsafe } from "#elements/utils/unsafe"; +import { TypeCreateWizardPageLayouts } from "#elements/wizard/TypeCreateWizardPage"; import { Wizard } from "#elements/wizard/Wizard"; import { EndpointsApi, TypeCreate } from "@goauthentik/api"; @@ -48,6 +50,7 @@ export class EndpointConnectorWizard extends AKElement {