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
@@ -0,0 +1,103 @@
|
||||
"""Root-owned generic enrollment template, separate from Forge and user SSO."""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Literal
|
||||
from uuid import UUID
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, field_validator, model_validator
|
||||
from sqlmodel import Session
|
||||
|
||||
from ..connectors.authentik import AuthentikConnector
|
||||
from ..db.models import AppSetting
|
||||
from ..schemas.enrollment import EnrollmentTrustConfigV1
|
||||
from ..core.config import settings
|
||||
|
||||
OIDC_TEMPLATE_SETTING_KEY = 'deployment_enrollment:oidc_template'
|
||||
TEMPLATE_FIELDS = {
|
||||
'authorization_flow': 'AUTHENTIK_OIDC_AUTHORIZATION_FLOW',
|
||||
'invalidation_flow': 'AUTHENTIK_OIDC_INVALIDATION_FLOW',
|
||||
'openid_mapping': 'AUTHENTIK_OIDC_OPENID_CLAIM_MAPPING',
|
||||
'email_mapping': 'AUTHENTIK_OIDC_EMAIL_CLAIM_MAPPING',
|
||||
'profile_mapping': 'AUTHENTIK_OIDC_PROFILE_CLAIM_MAPPING',
|
||||
'roles_mapping': 'AUTHENTIK_OIDC_GROUP_CLAIM_MAPPING',
|
||||
'signing_key': 'AUTHENTIK_OIDC_SIGNING_KEY_ID',
|
||||
}
|
||||
|
||||
|
||||
def trusted_issuer_origin() -> str | None:
|
||||
"""Use AppOps' established native Authentik SSO trust; never caller URLs."""
|
||||
try:
|
||||
value = urlsplit(settings.authentik_issuer or '')
|
||||
except ValueError:
|
||||
return None
|
||||
if value.scheme != 'https' or not value.netloc or value.username or value.password:
|
||||
return None
|
||||
return f'https://{value.netloc.lower()}'
|
||||
|
||||
|
||||
class EnrollmentOidcProfileV1(BaseModel):
|
||||
model_config = ConfigDict(extra='forbid', frozen=True)
|
||||
|
||||
issuer: str
|
||||
authorization_flow: UUID
|
||||
invalidation_flow: UUID
|
||||
openid_mapping: UUID
|
||||
email_mapping: UUID
|
||||
profile_mapping: UUID
|
||||
roles_mapping: UUID
|
||||
signing_key: UUID
|
||||
administrator_group: Literal['Administrator'] = 'Administrator'
|
||||
support_agent_group: Literal['SupportAgent'] = 'SupportAgent'
|
||||
read_only_group: Literal['ReadOnly'] = 'ReadOnly'
|
||||
|
||||
@field_validator('issuer')
|
||||
@classmethod
|
||||
def issuer_origin(cls, value: str) -> str:
|
||||
if not re.fullmatch(r'https://[a-z0-9]+(?:[.-][a-z0-9]+)*(?:/)?', value):
|
||||
raise ValueError('An HTTPS Authentik origin without credentials or application path is required')
|
||||
return value.rstrip('/')
|
||||
|
||||
@model_validator(mode='after')
|
||||
def separate_mappings(self):
|
||||
if len({self.openid_mapping, self.email_mapping, self.profile_mapping, self.roles_mapping}) != 4:
|
||||
raise ValueError('Four distinct scope mappings are required')
|
||||
return self
|
||||
|
||||
def template_config(self) -> dict[str, str]:
|
||||
return {**{env: str(getattr(self, field)) for field, env in TEMPLATE_FIELDS.items()}, 'AUTHENTIK_OIDC_ROLE_CLAIM': 'roles'}
|
||||
|
||||
|
||||
def load_profile(session: Session) -> EnrollmentOidcProfileV1 | None:
|
||||
row = session.get(AppSetting, OIDC_TEMPLATE_SETTING_KEY)
|
||||
if row is None or row.is_secret:
|
||||
return None
|
||||
try:
|
||||
return EnrollmentOidcProfileV1.model_validate(row.value_json)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def bind_template(session: Session, connector: AuthentikConnector) -> None:
|
||||
"""A corrupt stored binding blocks fallback; never mutate global/Forge IDs."""
|
||||
row = session.get(AppSetting, OIDC_TEMPLATE_SETTING_KEY)
|
||||
if row is None:
|
||||
connector.configure_deployment_oidc_template(None)
|
||||
return
|
||||
profile = load_profile(session)
|
||||
trust_row = session.get(AppSetting, 'deployment_enrollment:trust')
|
||||
try:
|
||||
trust = EnrollmentTrustConfigV1.model_validate(trust_row.value_json) if trust_row and not trust_row.is_secret else None
|
||||
except ValueError:
|
||||
trust = None
|
||||
if not trust or not profile or profile.issuer != trusted_issuer_origin() or (
|
||||
trust.oidc_issuer != profile.issuer
|
||||
or trust.administrator_group != profile.administrator_group
|
||||
or trust.support_agent_group != profile.support_agent_group
|
||||
or trust.read_only_group != profile.read_only_group
|
||||
):
|
||||
profile = None
|
||||
connector.configure_deployment_oidc_template(
|
||||
profile.template_config() if profile else {}, issuer_origin=profile.issuer if profile else None,
|
||||
)
|
||||
Reference in new issue
Block a user