r"""Automate provisioning of Authentik SAML Provider & Application for MeshCentral. Pre-configures: 1. Authentik SAML Provider with endpoints: - ACS URL: https://mesh.snarfnet.net/auth-saml - Audience: https://mesh.snarfnet.net - SP Binding: Post 2. Authentik Application ('meshcentral') with brand icon and grouping. 3. Outputs exact SAML values for MeshCentral config.json. """ import json import ssl import sys import urllib.request ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE AK_BASE = "https://authentik.snarfnet.net" AK_TOKEN = "SnarfNetPermanentAutomationToken2026" AUTH_FLOW = "38a86ef3-f43a-4756-8f54-ca008a8eef6e" INVAL_FLOW = "7a8145b8-2476-4636-9901-825f826df805" CERT_PK = "6d48ba88-4b51-428f-903c-93334988a941" def ak_request(endpoint, method="GET", data=None): url = f"{AK_BASE.rstrip('/')}/api/v3/{endpoint.lstrip('/')}" body = json.dumps(data).encode("utf-8") if data else None headers = { "Authorization": f"Bearer {AK_TOKEN}", "Content-Type": "application/json", "Accept": "application/json", } req = urllib.request.Request(url, data=body, headers=headers, method=method) with urllib.request.urlopen(req, timeout=10, context=ctx) as r: raw = r.read().decode("utf-8") return r.getcode(), json.loads(raw) if raw else {} def main(): print("=" * 60) print("MeshCentral <-> Authentik SAML Provisioner") print("=" * 60) code, res = ak_request("providers/saml/") existing_provider = next((p for p in res.get("results", []) if p.get("name") == "MeshCentral"), None) if existing_provider: provider_id = existing_provider["pk"] print(f"Existing MeshCentral Provider found (ID {provider_id})") else: provider_payload = { "name": "MeshCentral", "authorization_flow": AUTH_FLOW, "invalidation_flow": INVAL_FLOW, "acs_url": "https://mesh.snarfnet.net/auth-saml", "audience": "https://mesh.snarfnet.net", "issuer": f"{AK_BASE}/application/saml/meshcentral/metadata/", "sp_binding": "post", "signing_kp": CERT_PK, "verification_kp": None, "name_id_mapping": None, "sign_assertion": True, "sign_response": True, "digest_algorithm": "http://www.w3.org/2001/04/xmlenc#sha256", "signature_algorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", } code, new_prov = ak_request("providers/saml/", method="POST", data=provider_payload) provider_id = new_prov["pk"] print(f"Created MeshCentral SAML Provider (ID {provider_id})") code, res = ak_request("core/applications/") existing_app = next((a for a in res.get("results", []) if a.get("slug") == "meshcentral"), None) app_payload = { "name": "MeshCentral", "slug": "meshcentral", "provider": provider_id, "group": "Infrastructure & Hypervisors", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/meshcentral.png", "meta_description": "Remote Device Management & Web KVM", "meta_launch_url": "https://mesh.snarfnet.net", "open_in_new_tab": True, } if existing_app: ak_request("core/applications/meshcentral/", method="PATCH", data=app_payload) print("Updated existing 'meshcentral' application.") else: ak_request("core/applications/", method="POST", data=app_payload) print("Created 'meshcentral' application.") print("\n" + "=" * 60) print("SAML CONFIGURATION FOR MESHCENTRAL (config.json):") print("=" * 60) print(f"IdP Metadata URL: {AK_BASE}/application/saml/meshcentral/metadata/") print(f"IdP SingleSignOn URL: {AK_BASE}/application/saml/meshcentral/sso/binding/redirect/") print(f"IdP Entity ID: {AK_BASE}/application/saml/meshcentral/metadata/") print(f"SP ACS URL: https://mesh.snarfnet.net/auth-saml") print(f"SP Entity ID: https://mesh.snarfnet.net") print("=" * 60) if __name__ == "__main__": main()