authentik.endpoints.connectors.agent.controller
1from plistlib import PlistFormat, dumps 2from uuid import uuid4 3from xml.etree.ElementTree import Element, SubElement, tostring # nosec 4 5from django.http import HttpRequest 6from django.urls import reverse 7from rest_framework.fields import CharField 8 9from authentik.core.api.utils import PassiveSerializer 10from authentik.endpoints.connectors.agent.models import AgentConnector, EnrollmentToken 11from authentik.endpoints.controller import BaseController, Capabilities 12from authentik.endpoints.facts import OSFamily 13 14 15def csp_create_replace_item(loc_uri, data_value) -> Element: 16 """Create a Replace/Item element with the specified LocURI and Data""" 17 replace = Element("Replace") 18 item = SubElement(replace, "Item") 19 20 # Meta section 21 meta = SubElement(item, "Meta") 22 format_elem = SubElement(meta, "Format") 23 format_elem.set("xmlns", "syncml:metinf") 24 format_elem.text = "chr" 25 26 # Target section 27 target = SubElement(item, "Target") 28 loc_uri_elem = SubElement(target, "LocURI") 29 loc_uri_elem.text = loc_uri 30 31 # Data section 32 data = SubElement(item, "Data") 33 data.text = data_value 34 35 return replace 36 37 38class MDMConfigResponseSerializer(PassiveSerializer): 39 40 config = CharField(required=True) 41 mime_type = CharField(required=True) 42 filename = CharField(required=True) 43 44 45class AgentConnectorController(BaseController[AgentConnector]): 46 47 @staticmethod 48 def vendor_identifier() -> str: 49 return "goauthentik.io/platform" 50 51 def capabilities(self) -> list[Capabilities]: 52 return [Capabilities.STAGE_ENDPOINTS] 53 54 def generate_mdm_config( 55 self, target_platform: OSFamily, request: HttpRequest, token: EnrollmentToken 56 ) -> MDMConfigResponseSerializer: 57 response = None 58 if target_platform == OSFamily.windows: 59 response = self._generate_mdm_config_windows(request, token) 60 if target_platform in [OSFamily.iOS, OSFamily.macOS]: 61 response = self._generate_mdm_config_macos(request, token) 62 if not response: 63 raise ValueError(f"Unsupported platform for MDM Configuration: {target_platform}") 64 response.is_valid(raise_exception=True) 65 return response 66 67 def _generate_mdm_config_windows( 68 self, request: HttpRequest, token: EnrollmentToken 69 ) -> MDMConfigResponseSerializer: 70 base_uri = ( 71 "./Vendor/MSFT/Registry/HKLM/SOFTWARE/authentik Security Inc./Platform/ManagedConfig" 72 ) 73 token_item = csp_create_replace_item( 74 base_uri + "/RegistrationToken", 75 token.key, 76 ) 77 url_item = csp_create_replace_item( 78 base_uri + "/URL", 79 request.build_absolute_uri(reverse("authentik_core:root-redirect")), 80 ) 81 82 payload = tostring(token_item, encoding="unicode") + tostring(url_item, encoding="unicode") 83 return MDMConfigResponseSerializer( 84 data={ 85 "config": payload, 86 "mime_type": "application/xml", 87 "filename": f"{self.connector.name}_config.csp.xml", 88 } 89 ) 90 91 def _generate_mdm_config_macos( 92 self, request: HttpRequest, token: EnrollmentToken 93 ) -> MDMConfigResponseSerializer: 94 token_uuid = str(token.pk).upper() 95 payload = dumps( 96 { 97 "PayloadContent": [ 98 # Config for authentik Platform Agent (sysd) 99 { 100 "PayloadDisplayName": "authentik Platform", 101 "PayloadIdentifier": f"io.goauthentik.platform.{token_uuid}", 102 "PayloadType": "io.goauthentik.platform", 103 "PayloadUUID": str(uuid4()), 104 "PayloadVersion": 1, 105 "RegistrationToken": token.key, 106 "URL": request.build_absolute_uri(reverse("authentik_core:root-redirect")), 107 }, 108 # Config for MDM-associated domains (required for PSSO) 109 { 110 "PayloadDisplayName": "Associated Domains", 111 "PayloadIdentifier": f"com.apple.associated-domains.{token_uuid}", 112 "PayloadType": "com.apple.associated-domains", 113 "PayloadUUID": str(uuid4()), 114 "PayloadVersion": 1, 115 "Configuration": [ 116 { 117 "ApplicationIdentifier": "232G855Y8N.io.goauthentik.platform.agent", 118 "AssociatedDomains": [f"authsrv:{request.get_host()}"], 119 "EnableDirectDownloads": False, 120 } 121 ], 122 }, 123 # Config for Platform SSO 124 { 125 "PayloadDisplayName": "Platform Single Sign-On", 126 "PayloadIdentifier": f"com.apple.extensiblesso.{token_uuid}", 127 "PayloadType": "com.apple.extensiblesso", 128 "PayloadUUID": str(uuid4()), 129 "PayloadVersion": 1, 130 "ExtensionIdentifier": "io.goauthentik.platform.psso", 131 "TeamIdentifier": "232G855Y8N", 132 "Type": "Redirect", 133 "URLs": [ 134 request.build_absolute_uri(reverse("authentik_core:root-redirect")), 135 ], 136 "PlatformSSO": { 137 "AccountDisplayName": "authentik", 138 "AllowDeviceIdentifiersInAttestation": True, 139 "AuthenticationMethod": "UserSecureEnclaveKey", 140 "EnableAuthorization": True, 141 "EnableCreateUserAtLogin": True, 142 "FileVaultPolicy": ["RequireAuthentication"], 143 "LoginPolicy": ["RequireAuthentication"], 144 "NewUserAuthorizationMode": "Standard", 145 "UnlockPolicy": ["RequireAuthentication"], 146 "UseSharedDeviceKeys": True, 147 "UserAuthorizationMode": "Standard", 148 }, 149 }, 150 ], 151 "PayloadDisplayName": "authentik Platform", 152 "PayloadIdentifier": str(self.connector.pk).upper(), 153 "PayloadScope": "System", 154 "PayloadType": "Configuration", 155 "PayloadUUID": str(self.connector.pk).upper(), 156 "PayloadVersion": 1, 157 }, 158 fmt=PlistFormat.FMT_XML, 159 ).decode() 160 return MDMConfigResponseSerializer( 161 data={ 162 "config": payload, 163 "mime_type": "application/xml", 164 "filename": f"{self.connector.name}_config.mobileconfig", 165 } 166 )
def
csp_create_replace_item(loc_uri, data_value) -> xml.etree.ElementTree.Element:
16def csp_create_replace_item(loc_uri, data_value) -> Element: 17 """Create a Replace/Item element with the specified LocURI and Data""" 18 replace = Element("Replace") 19 item = SubElement(replace, "Item") 20 21 # Meta section 22 meta = SubElement(item, "Meta") 23 format_elem = SubElement(meta, "Format") 24 format_elem.set("xmlns", "syncml:metinf") 25 format_elem.text = "chr" 26 27 # Target section 28 target = SubElement(item, "Target") 29 loc_uri_elem = SubElement(target, "LocURI") 30 loc_uri_elem.text = loc_uri 31 32 # Data section 33 data = SubElement(item, "Data") 34 data.text = data_value 35 36 return replace
Create a Replace/Item element with the specified LocURI and Data
39class MDMConfigResponseSerializer(PassiveSerializer): 40 41 config = CharField(required=True) 42 mime_type = CharField(required=True) 43 filename = CharField(required=True)
Base serializer class which doesn't implement create/update methods
Inherited Members
class
AgentConnectorController(authentik.endpoints.controller.BaseController[authentik.endpoints.connectors.agent.models.AgentConnector]):
46class AgentConnectorController(BaseController[AgentConnector]): 47 48 @staticmethod 49 def vendor_identifier() -> str: 50 return "goauthentik.io/platform" 51 52 def capabilities(self) -> list[Capabilities]: 53 return [Capabilities.STAGE_ENDPOINTS] 54 55 def generate_mdm_config( 56 self, target_platform: OSFamily, request: HttpRequest, token: EnrollmentToken 57 ) -> MDMConfigResponseSerializer: 58 response = None 59 if target_platform == OSFamily.windows: 60 response = self._generate_mdm_config_windows(request, token) 61 if target_platform in [OSFamily.iOS, OSFamily.macOS]: 62 response = self._generate_mdm_config_macos(request, token) 63 if not response: 64 raise ValueError(f"Unsupported platform for MDM Configuration: {target_platform}") 65 response.is_valid(raise_exception=True) 66 return response 67 68 def _generate_mdm_config_windows( 69 self, request: HttpRequest, token: EnrollmentToken 70 ) -> MDMConfigResponseSerializer: 71 base_uri = ( 72 "./Vendor/MSFT/Registry/HKLM/SOFTWARE/authentik Security Inc./Platform/ManagedConfig" 73 ) 74 token_item = csp_create_replace_item( 75 base_uri + "/RegistrationToken", 76 token.key, 77 ) 78 url_item = csp_create_replace_item( 79 base_uri + "/URL", 80 request.build_absolute_uri(reverse("authentik_core:root-redirect")), 81 ) 82 83 payload = tostring(token_item, encoding="unicode") + tostring(url_item, encoding="unicode") 84 return MDMConfigResponseSerializer( 85 data={ 86 "config": payload, 87 "mime_type": "application/xml", 88 "filename": f"{self.connector.name}_config.csp.xml", 89 } 90 ) 91 92 def _generate_mdm_config_macos( 93 self, request: HttpRequest, token: EnrollmentToken 94 ) -> MDMConfigResponseSerializer: 95 token_uuid = str(token.pk).upper() 96 payload = dumps( 97 { 98 "PayloadContent": [ 99 # Config for authentik Platform Agent (sysd) 100 { 101 "PayloadDisplayName": "authentik Platform", 102 "PayloadIdentifier": f"io.goauthentik.platform.{token_uuid}", 103 "PayloadType": "io.goauthentik.platform", 104 "PayloadUUID": str(uuid4()), 105 "PayloadVersion": 1, 106 "RegistrationToken": token.key, 107 "URL": request.build_absolute_uri(reverse("authentik_core:root-redirect")), 108 }, 109 # Config for MDM-associated domains (required for PSSO) 110 { 111 "PayloadDisplayName": "Associated Domains", 112 "PayloadIdentifier": f"com.apple.associated-domains.{token_uuid}", 113 "PayloadType": "com.apple.associated-domains", 114 "PayloadUUID": str(uuid4()), 115 "PayloadVersion": 1, 116 "Configuration": [ 117 { 118 "ApplicationIdentifier": "232G855Y8N.io.goauthentik.platform.agent", 119 "AssociatedDomains": [f"authsrv:{request.get_host()}"], 120 "EnableDirectDownloads": False, 121 } 122 ], 123 }, 124 # Config for Platform SSO 125 { 126 "PayloadDisplayName": "Platform Single Sign-On", 127 "PayloadIdentifier": f"com.apple.extensiblesso.{token_uuid}", 128 "PayloadType": "com.apple.extensiblesso", 129 "PayloadUUID": str(uuid4()), 130 "PayloadVersion": 1, 131 "ExtensionIdentifier": "io.goauthentik.platform.psso", 132 "TeamIdentifier": "232G855Y8N", 133 "Type": "Redirect", 134 "URLs": [ 135 request.build_absolute_uri(reverse("authentik_core:root-redirect")), 136 ], 137 "PlatformSSO": { 138 "AccountDisplayName": "authentik", 139 "AllowDeviceIdentifiersInAttestation": True, 140 "AuthenticationMethod": "UserSecureEnclaveKey", 141 "EnableAuthorization": True, 142 "EnableCreateUserAtLogin": True, 143 "FileVaultPolicy": ["RequireAuthentication"], 144 "LoginPolicy": ["RequireAuthentication"], 145 "NewUserAuthorizationMode": "Standard", 146 "UnlockPolicy": ["RequireAuthentication"], 147 "UseSharedDeviceKeys": True, 148 "UserAuthorizationMode": "Standard", 149 }, 150 }, 151 ], 152 "PayloadDisplayName": "authentik Platform", 153 "PayloadIdentifier": str(self.connector.pk).upper(), 154 "PayloadScope": "System", 155 "PayloadType": "Configuration", 156 "PayloadUUID": str(self.connector.pk).upper(), 157 "PayloadVersion": 1, 158 }, 159 fmt=PlistFormat.FMT_XML, 160 ).decode() 161 return MDMConfigResponseSerializer( 162 data={ 163 "config": payload, 164 "mime_type": "application/xml", 165 "filename": f"{self.connector.name}_config.mobileconfig", 166 } 167 )
Abstract base class for generic types.
On Python 3.12 and newer, generic classes implicitly inherit from Generic when they declare a parameter list after the class's name::
class Mapping[KT, VT]:
def __getitem__(self, key: KT) -> VT:
...
# Etc.
On older versions of Python, however, generic classes have to explicitly inherit from Generic.
After a class has been declared to be generic, it can then be used as follows::
def lookup_name[KT, VT](mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
try:
return mapping[key]
except KeyError:
return default
def
generate_mdm_config( self, target_platform: authentik.endpoints.facts.OSFamily, request: django.http.request.HttpRequest, token: authentik.endpoints.connectors.agent.models.EnrollmentToken) -> MDMConfigResponseSerializer:
55 def generate_mdm_config( 56 self, target_platform: OSFamily, request: HttpRequest, token: EnrollmentToken 57 ) -> MDMConfigResponseSerializer: 58 response = None 59 if target_platform == OSFamily.windows: 60 response = self._generate_mdm_config_windows(request, token) 61 if target_platform in [OSFamily.iOS, OSFamily.macOS]: 62 response = self._generate_mdm_config_macos(request, token) 63 if not response: 64 raise ValueError(f"Unsupported platform for MDM Configuration: {target_platform}") 65 response.is_valid(raise_exception=True) 66 return response