Publish curated Unraid-AppOps-Gateway source from 68878c3167dacf2b424c16780e60e0d60815e477
This commit is contained in:
1 parent
837c263cd8
commit
551de41e9f
18 files changed
+1183
-58
No files matched your search
@@ -14,6 +14,7 @@ import re
|
||||
import secrets
|
||||
import uuid
|
||||
from typing import Any, Literal
|
||||
from urllib.parse import urlsplit, urlunsplit
|
||||
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlmodel import Session, select
|
||||
@@ -30,6 +31,7 @@ from ..schemas.enrollment import (
|
||||
EnrollmentTrustConfigV1,
|
||||
)
|
||||
from .audit_logger import log_event
|
||||
from .enrollment_oidc_settings import bind_template
|
||||
from .deployment_broker import DeploymentBrokerClient
|
||||
from .enrollment_profiles import PROFILE_VERSION, profile_contract, profile_summary
|
||||
from .secret_store import (
|
||||
@@ -155,6 +157,22 @@ def _enrollment_secret_store_ready(storage: dict[str, Any]) -> bool:
|
||||
return bool(storage.get("productionReady"))
|
||||
|
||||
|
||||
def _application_oidc_issuer(issuer: str | None, application_id: str | None = None) -> str | None:
|
||||
"""Bind Authentik's per-provider issuer to this application's own client."""
|
||||
if not issuer:
|
||||
return None
|
||||
parsed = urlsplit(issuer)
|
||||
path = parsed.path.rstrip('/')
|
||||
if application_id is None:
|
||||
if path not in {'', '/application/o'} and not re.fullmatch(r'/application/o/[a-z][a-z0-9-]{1,62}', path):
|
||||
return None
|
||||
return urlunsplit((parsed.scheme, parsed.netloc, path + '/', '', ''))
|
||||
expected_path = f"/application/o/{application_id}"
|
||||
if parsed.path.rstrip("/") not in {"", "/application/o", expected_path}:
|
||||
return None
|
||||
return urlunsplit((parsed.scheme, parsed.netloc, expected_path + "/", "", ""))
|
||||
|
||||
|
||||
class EnrollmentPlane:
|
||||
def __init__(
|
||||
self,
|
||||
@@ -222,6 +240,8 @@ class EnrollmentPlane:
|
||||
|
||||
def capabilities(self, session: Session) -> dict[str, Any]:
|
||||
trust = self.get_trust_config(session)
|
||||
if isinstance(self.authentik, AuthentikConnector):
|
||||
bind_template(session, self.authentik)
|
||||
storage = secret_storage_status()
|
||||
configured = trust is not None
|
||||
bootstrap = bool(trust and has_secret(session, trust.bootstrap_secret_ref))
|
||||
@@ -335,11 +355,30 @@ class EnrollmentPlane:
|
||||
oidc_configured = bool(
|
||||
trust
|
||||
and trust.oidc_issuer
|
||||
and _application_oidc_issuer(trust.oidc_issuer)
|
||||
and trust.administrator_group
|
||||
and trust.support_agent_group
|
||||
and trust.read_only_group
|
||||
and self.authentik.is_configured()
|
||||
)
|
||||
if oidc_configured and trust is not None:
|
||||
template = self.authentik.validate_deployment_oidc_template(
|
||||
role_claim=trust.oidc_role_claim,
|
||||
role_groups=(trust.administrator_group, trust.support_agent_group, trust.read_only_group),
|
||||
)
|
||||
groups = self.authentik.groups()
|
||||
found_groups = {
|
||||
str(group.get("name") or "")
|
||||
for group in groups.get("items", [])
|
||||
if isinstance(group, dict)
|
||||
}
|
||||
oidc_configured = bool(
|
||||
template.get("status") == "ready"
|
||||
and groups.get("success") is True
|
||||
and {
|
||||
trust.administrator_group, trust.support_agent_group, trust.read_only_group,
|
||||
}.issubset(found_groups)
|
||||
)
|
||||
certificate_ready = bool(npm_ready)
|
||||
|
||||
def item(ready: bool, code: str) -> dict[str, str]:
|
||||
@@ -464,6 +503,8 @@ class EnrollmentPlane:
|
||||
def preflight(self, session: Session, plan: EnrollmentPlanV1) -> dict[str, Any]:
|
||||
checks: list[dict[str, str]] = []
|
||||
trust = self.get_trust_config(session)
|
||||
if isinstance(self.authentik, AuthentikConnector):
|
||||
bind_template(session, self.authentik)
|
||||
profile = profile_contract(plan.profile)
|
||||
if trust is None:
|
||||
checks.append(_check("enrollment_trust_not_configured", "blocked", "Root enrollment trust boundaries are not configured."))
|
||||
@@ -586,6 +627,13 @@ class EnrollmentPlane:
|
||||
checks.append(_check("oidc_profile_unavailable", "blocked", "Configured OIDC profile or Authentik role groups are unavailable."))
|
||||
else:
|
||||
assert trust is not None and plan.authentication.roles is not None
|
||||
issuer_matches = _application_oidc_issuer(trust.oidc_issuer, plan.application_id)
|
||||
checks.append(_check(
|
||||
"oidc_issuer" if issuer_matches else "oidc_issuer_mismatch",
|
||||
"passed" if issuer_matches else "blocked",
|
||||
"Authentik issuer is bound to this application's own provider."
|
||||
if issuer_matches else "Configured issuer belongs to a different application or unsupported issuer path.",
|
||||
))
|
||||
expected_roles = {
|
||||
"administrator": trust.administrator_group,
|
||||
"support_agent": trust.support_agent_group,
|
||||
@@ -603,8 +651,9 @@ class EnrollmentPlane:
|
||||
if auth_status.get("status") != "connected":
|
||||
checks.append(_check("oidc_provider", "blocked", "Authentik is unavailable."))
|
||||
else:
|
||||
template = self.authentik.deployment_oidc_template_status(
|
||||
role_claim=trust.oidc_role_claim
|
||||
template = self.authentik.validate_deployment_oidc_template(
|
||||
role_claim=trust.oidc_role_claim,
|
||||
role_groups=(trust.administrator_group, trust.support_agent_group, trust.read_only_group),
|
||||
)
|
||||
if template.get("status") != "ready":
|
||||
checks.append(_check("oidc_profile_unavailable", "blocked", "The server-owned Authentik OIDC template is incomplete."))
|
||||
@@ -978,6 +1027,8 @@ class EnrollmentPlane:
|
||||
raise EnrollmentError("application identity or repository is already enrolled")
|
||||
|
||||
trust = self.get_trust_config(session)
|
||||
if isinstance(self.authentik, AuthentikConnector):
|
||||
bind_template(session, self.authentik)
|
||||
if trust is None:
|
||||
raise EnrollmentError("enrollment trust boundaries are not configured")
|
||||
enrollment = self._create_enrollment(session, plan, plan_digest, trust)
|
||||
@@ -1145,7 +1196,7 @@ class EnrollmentPlane:
|
||||
runtime_values.update({
|
||||
"OIDC_CLIENT_ID": plan.application_id,
|
||||
"OIDC_CLIENT_SECRET": oidc_secret,
|
||||
"OIDC_ISSUER": trust.oidc_issuer or "",
|
||||
"OIDC_ISSUER": _application_oidc_issuer(trust.oidc_issuer, plan.application_id) or "",
|
||||
"OIDC_ROLE_CLAIM": trust.oidc_role_claim,
|
||||
})
|
||||
if plan.profile == "repopulse-authenticated":
|
||||
|
||||
Reference in new issue
Block a user