Publish curated Unraid-AppOps-Gateway source from 451646fee806a47e3256731766980a05a262dcdd
This commit is contained in:
1 parent
532f3d9f26
commit
0a82153d95
43 files changed
+17722
-93
No files matched your search
@@ -1,13 +1,23 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
from typing import Annotated, Any
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from sqlmodel import Session
|
||||
|
||||
from ..connectors.authentik import AuthentikConnector
|
||||
from ..core.security import require_operator_request
|
||||
from ..db.session import get_session
|
||||
from ..schemas.forge_oidc import ForgeOidcProvisionRequestV1, ForgeOidcProvisionResultV1
|
||||
from ..schemas.forge_secret_delivery import (
|
||||
ForgeControllerReaderProvisionRequestV1,
|
||||
ForgeControllerReaderProvisionResultV1,
|
||||
)
|
||||
from ..services.forge_controller_reader import provision_forge_controller_reader
|
||||
from ..services.forge_oidc_provisioning import provision_forge_oidc
|
||||
from .connector_runtime import load_connector_runtime
|
||||
|
||||
router = APIRouter(prefix='/api/apps/authentik', dependencies=[Depends(load_connector_runtime)])
|
||||
router = APIRouter(prefix='/api/apps/authentik')
|
||||
|
||||
|
||||
def _shape(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
@@ -27,66 +37,103 @@ def _connector() -> AuthentikConnector:
|
||||
return AuthentikConnector()
|
||||
|
||||
|
||||
@router.get('/health')
|
||||
def _require_forge_oidc_operator(request: Request) -> str:
|
||||
return require_operator_request(request)
|
||||
|
||||
|
||||
@router.get('/health', dependencies=[Depends(load_connector_runtime)])
|
||||
def health():
|
||||
return _shape(_connector().health())
|
||||
|
||||
|
||||
@router.get('/system')
|
||||
@router.get('/system', dependencies=[Depends(load_connector_runtime)])
|
||||
def system_status():
|
||||
return _shape(_connector().system_status())
|
||||
|
||||
|
||||
@router.get('/applications')
|
||||
@router.get('/applications', dependencies=[Depends(load_connector_runtime)])
|
||||
def applications():
|
||||
return _shape(_connector().applications())
|
||||
|
||||
|
||||
@router.get('/providers')
|
||||
@router.get('/providers', dependencies=[Depends(load_connector_runtime)])
|
||||
def providers():
|
||||
return _shape(_connector().providers())
|
||||
|
||||
|
||||
@router.get('/providers/proxy')
|
||||
@router.get('/providers/proxy', dependencies=[Depends(load_connector_runtime)])
|
||||
def proxy_providers():
|
||||
return _shape(_connector().proxy_providers())
|
||||
|
||||
|
||||
@router.get('/providers/oauth2')
|
||||
@router.get('/providers/oauth2', dependencies=[Depends(load_connector_runtime)])
|
||||
def oauth2_providers():
|
||||
return _shape(_connector().oauth2_providers())
|
||||
|
||||
|
||||
@router.get('/outposts')
|
||||
@router.get('/outposts', dependencies=[Depends(load_connector_runtime)])
|
||||
def outposts():
|
||||
return _shape(_connector().outposts())
|
||||
|
||||
|
||||
@router.get('/outposts/health')
|
||||
@router.get('/outposts/health', dependencies=[Depends(load_connector_runtime)])
|
||||
def outpost_health():
|
||||
return _shape(_connector().outpost_health())
|
||||
|
||||
|
||||
@router.get('/flows')
|
||||
@router.get('/flows', dependencies=[Depends(load_connector_runtime)])
|
||||
def flows_summary():
|
||||
return _shape(_connector().flows_summary())
|
||||
|
||||
|
||||
@router.get('/policies')
|
||||
@router.get('/policies', dependencies=[Depends(load_connector_runtime)])
|
||||
def policies_summary():
|
||||
return _shape(_connector().policies_summary())
|
||||
|
||||
|
||||
@router.get('/events')
|
||||
@router.get('/events', dependencies=[Depends(load_connector_runtime)])
|
||||
def events(limit: int = 25):
|
||||
return _shape(_connector().events(limit))
|
||||
|
||||
|
||||
@router.get('/auth-coverage')
|
||||
@router.get('/auth-coverage', dependencies=[Depends(load_connector_runtime)])
|
||||
def auth_coverage():
|
||||
return _shape(_connector().auth_coverage())
|
||||
|
||||
|
||||
@router.get('/security-warnings')
|
||||
@router.get('/security-warnings', dependencies=[Depends(load_connector_runtime)])
|
||||
def security_warnings():
|
||||
return _shape(_connector().security_warnings())
|
||||
|
||||
|
||||
@router.post(
|
||||
'/forge/oidc/provision',
|
||||
response_model=ForgeOidcProvisionResultV1,
|
||||
)
|
||||
def provision_forge_oidc_application(
|
||||
payload: ForgeOidcProvisionRequestV1,
|
||||
request: Request,
|
||||
session: Annotated[Session, Depends(get_session)],
|
||||
actor_identity: Annotated[str, Depends(_require_forge_oidc_operator)],
|
||||
):
|
||||
"""Run the fixed, manually authorized Forge OIDC provisioning action."""
|
||||
# The payload is intentionally empty: callers cannot select a provider,
|
||||
# redirect URI, mapping, groups, or secret. The service repeats the exact
|
||||
# policy check before making any connector request.
|
||||
_ = payload
|
||||
return provision_forge_oidc(session, actor_identity=actor_identity)
|
||||
|
||||
|
||||
@router.post(
|
||||
'/forge/controller-reader/provision',
|
||||
response_model=ForgeControllerReaderProvisionResultV1,
|
||||
)
|
||||
def provision_forge_controller_reader_account(
|
||||
payload: ForgeControllerReaderProvisionRequestV1,
|
||||
request: Request,
|
||||
session: Annotated[Session, Depends(get_session)],
|
||||
actor_identity: Annotated[str, Depends(_require_forge_oidc_operator)],
|
||||
):
|
||||
"""Provision only the fixed read-only Forge controller service account."""
|
||||
_ = payload, request
|
||||
return provision_forge_controller_reader(session, actor_identity=actor_identity)
|
||||
@@ -16,6 +16,11 @@ from ..core.security import require_operator_request
|
||||
from ..db.session import get_session
|
||||
from ..schemas.deployment import DeploymentPlanV1
|
||||
from ..schemas.forge_bootstrap import ForgeBootstrapPreflightV1
|
||||
from ..schemas.forge_bootstrap_apply import ForgeBootstrapApplyV1
|
||||
from ..schemas.forge_secret_delivery import (
|
||||
ForgeSecretDeliveryRequestV1,
|
||||
ForgeSecretDeliveryResultV1,
|
||||
)
|
||||
from ..services.deployment_broker import DeploymentBrokerClient
|
||||
from ..services.deployment_control_plane import (
|
||||
DeploymentControlError,
|
||||
@@ -117,6 +122,62 @@ def preflight_forge_bootstrap(
|
||||
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.post(
|
||||
"/forge-bootstrap/secrets",
|
||||
response_model=ForgeSecretDeliveryResultV1,
|
||||
)
|
||||
def prepare_forge_bootstrap_secrets(
|
||||
payload: ForgeSecretDeliveryRequestV1,
|
||||
actor_identity: Annotated[str, Depends(_require_deployment_operator)],
|
||||
session: DeploymentSession,
|
||||
idempotency_key: DeploymentIdempotencyKey = None,
|
||||
):
|
||||
"""Deliver only the verified protected Forge inputs to the fixed controller."""
|
||||
from ..services.forge_secret_delivery import prepare_forge_secret_delivery
|
||||
|
||||
try:
|
||||
return prepare_forge_secret_delivery(
|
||||
session,
|
||||
profile=payload.profile,
|
||||
idempotency_key=_idempotency_header(idempotency_key),
|
||||
actor_identity=actor_identity,
|
||||
)
|
||||
except ValueError as exc:
|
||||
# Service messages are fixed safe codes; never surface upstream text.
|
||||
raise HTTPException(status_code=409, detail="forge_secret_delivery_failed") from exc
|
||||
|
||||
|
||||
@router.post("/forge-bootstrap/first-install")
|
||||
def apply_forge_first_install(
|
||||
payload: ForgeBootstrapApplyV1,
|
||||
session: DeploymentSession,
|
||||
idempotency_key: DeploymentIdempotencyKey = None,
|
||||
):
|
||||
from ..services.forge_bootstrap_apply import (
|
||||
ForgeBootstrapApplyError,
|
||||
ForgeBootstrapApplyService,
|
||||
)
|
||||
|
||||
try:
|
||||
return ForgeBootstrapApplyService().apply(
|
||||
session,
|
||||
payload,
|
||||
idempotency_key=_idempotency_header(idempotency_key),
|
||||
)
|
||||
except ForgeBootstrapApplyError as exc:
|
||||
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.post("/forge-bootstrap/first-install/{job_id}/resume")
|
||||
def resume_forge_first_install(job_id: str, session: DeploymentSession):
|
||||
from ..services.forge_bootstrap_apply import ForgeBootstrapApplyService
|
||||
|
||||
result = ForgeBootstrapApplyService().resume(session, job_id)
|
||||
if result is None:
|
||||
raise HTTPException(status_code=404, detail="Forge first-install request not found.")
|
||||
return result
|
||||
|
||||
|
||||
@router.get("/workmesh/promotions/{promotion_id}")
|
||||
def get_workmesh_promotion(promotion_id: str, session: DeploymentSession):
|
||||
from ..services.workmesh_promotion import WorkMeshPromotionService
|
||||
@@ -137,6 +198,16 @@ def list_workmesh_promotions(
|
||||
return {"items": WorkMeshPromotionService.list_promotions(session, limit=limit)}
|
||||
|
||||
|
||||
@router.get("/forge-bootstrap/first-install/{job_id}")
|
||||
def get_forge_first_install(job_id: str, session: DeploymentSession):
|
||||
from ..services.forge_bootstrap_apply import ForgeBootstrapApplyService
|
||||
|
||||
result = ForgeBootstrapApplyService().status(session, job_id)
|
||||
if result is None:
|
||||
raise HTTPException(status_code=404, detail="Forge first-install request not found.")
|
||||
return result
|
||||
|
||||
|
||||
@router.get("")
|
||||
def list_deployments(session: DeploymentSession, limit: DeploymentLimit = 100):
|
||||
return {"items": DeploymentControlPlane().list_jobs(session, limit=limit)}
|
||||
@@ -150,6 +221,11 @@ def get_delivery_status():
|
||||
|
||||
@router.get("/{job_id}")
|
||||
def get_deployment(job_id: str, session: DeploymentSession):
|
||||
if job_id.startswith("forge-install-"):
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Use the fixed Forge first-install status route for this job.",
|
||||
)
|
||||
try:
|
||||
return DeploymentControlPlane().refresh(session, job_id)
|
||||
except DeploymentControlError as exc:
|
||||
|
||||
Reference in new issue
Block a user