Files
Unraid-AppOps-Gateway-Public/backend/tests/test_forge_bootstrap_apply.py
T

1049 lines
43 KiB
Python

from __future__ import annotations
import base64
import hashlib
import json
import threading
import pytest
from sqlmodel import Session, SQLModel, create_engine, select
from backend.app.core.config import settings
from backend.app.core.time import utc_now
from backend.app.db.models import DeploymentJob, Policy
from backend.app.schemas.enrollment import EnrollmentTrustConfigV1
from backend.app.schemas.forge_bootstrap_apply import ForgeBootstrapApplyV1
from backend.app.services.enrollment_plane import EnrollmentPlane
from backend.app.services.forge_bootstrap_apply import (
ForgeBootstrapApplyError,
ForgeBootstrapApplyService,
canonical_preflight_receipt,
forge_oidc_attestation_matches,
)
from backend.app.services.secret_store import set_secret
_REVISION = "a" * 40
_WORKSPACE_DIGEST = "sha256:" + "b" * 64
_FRONTDOOR_DIGEST = "sha256:" + "c" * 64
_PREFLIGHT_ID = "forge-preflight-0123456789abcdef"
_SERVICE_HEALTH = ["api", "dashboard", "itworx-forge", "postgres", "worker"]
_OIDC_OPENID_MAPPING_ID = "01234567-89ab-4cde-8fab-0123456789ab"
_OIDC_EMAIL_MAPPING_ID = "21234567-89ab-4cde-8fab-0123456789ab"
_OIDC_PROFILE_MAPPING_ID = "31234567-89ab-4cde-8fab-0123456789ab"
_OIDC_PROVIDER_ID = "456"
_OIDC_SIGNING_KEY_ID = "11234567-89ab-4cde-8fab-0123456789ab"
_OIDC_FINGERPRINT = "ab" * 32
_OIDC_KEY_KID = base64.urlsafe_b64encode(bytes(range(64))).decode("ascii").rstrip("=")
_OIDC_MAPPING_IDS = {
"openid": _OIDC_OPENID_MAPPING_ID,
"email": _OIDC_EMAIL_MAPPING_ID,
"profile": _OIDC_PROFILE_MAPPING_ID,
}
_OIDC_MAPPING_EXPRESSION_SHA256 = {
"openid": "38e58da331cc5378d4f269389242aecb80adf30093c405524dc1ab5aab9d452f",
"email": "d1200eabeffc3b7f379e9c1636367b69befb0b1ac2b5d82b7be20e78667f2b94",
"profile": "648b3f6dbf2212940309a581b239c9996562ca017a89be22bcfa93f9aa2c26ae",
}
_FORGE_PRIVATE_OWNER = "itworx-forge-private"
_FORGE_SOURCE_URL = "https://gitea.example.com/Jens/itworx-forge"
@pytest.fixture(autouse=True)
def _forge_oidc_protected_pins(monkeypatch):
apis = dict(settings.apis)
apis["AUTHENTIK_FORGE_OIDC_BINDING_VALID"] = True
apis["AUTHENTIK_FORGE_OIDC_PROVIDER_ID"] = _OIDC_PROVIDER_ID
apis["AUTHENTIK_FORGE_OIDC_OPENID_CLAIM_MAPPING"] = _OIDC_OPENID_MAPPING_ID
apis["AUTHENTIK_FORGE_OIDC_EMAIL_CLAIM_MAPPING"] = _OIDC_EMAIL_MAPPING_ID
apis["AUTHENTIK_FORGE_OIDC_PROFILE_CLAIM_MAPPING"] = _OIDC_PROFILE_MAPPING_ID
apis["AUTHENTIK_FORGE_OIDC_SIGNING_KEY_ID"] = _OIDC_SIGNING_KEY_ID
apis["AUTHENTIK_OIDC_AUTHORIZATION_FLOW"] = "default-provider-authorization-implicit-consent"
apis["AUTHENTIK_OIDC_INVALIDATION_FLOW"] = "default-provider-invalidation-flow"
apis["FORGE_PRIVATE_PACKAGE_OWNER"] = _FORGE_PRIVATE_OWNER
monkeypatch.setattr(settings, "apis", apis)
def _oidc_attestation():
return {
"issuer": "https://auth.example-host.com/application/o/itworx-forge/",
"audience": "itworx-forge",
"clientId": "itworx-forge",
"clientType": "confidential",
"callback": "https://forge.example.com/auth/callback",
"applicationId": "41234567-89ab-4cde-8fab-0123456789ab",
"providerId": _OIDC_PROVIDER_ID,
"clientSecretMatched": True,
"authorizationFlowId": "default-provider-authorization-implicit-consent",
"invalidationFlowId": "default-provider-invalidation-flow",
"roleGroups": ["forge-owners", "forge-operators", "forge-viewers"],
"scopeMappingIds": _OIDC_MAPPING_IDS.copy(),
"scopeMappingExpressionSha256": _OIDC_MAPPING_EXPRESSION_SHA256.copy(),
"profileMappingName": "ITWorx Forge profile and groups claim v1",
"standardMappingsManaged": True,
"supportedScopes": ["email", "openid", "profile"],
"authorizationCodeFlowSupported": True,
"signingKeyId": _OIDC_SIGNING_KEY_ID,
"signingKeyType": "RSA",
"signingKeyPrivateAvailable": True,
"signingKeyFingerprintSha256": _OIDC_FINGERPRINT,
"signingKeyKid": _OIDC_KEY_KID,
"tokenSigningAlgorithm": "RS256",
"jwksKid": _OIDC_KEY_KID,
}
def _profile(**overrides):
value = {
"api_version": "itworx.forge-first-install/v1",
"revision": _REVISION,
"workspace_image_digest": _WORKSPACE_DIGEST,
"frontdoor_image_digest": _FRONTDOOR_DIGEST,
"preflight_job_id": _PREFLIGHT_ID,
"preflight_sha256": "d" * 64,
}
value.update(overrides)
return ForgeBootstrapApplyV1.model_validate(value)
def _session(tmp_path):
engine = create_engine(f"sqlite:///{tmp_path / 'forge-first-install.db'}")
SQLModel.metadata.create_all(engine)
return Session(engine)
def _passed_preflight(session):
profile = {
"api_version": "itworx.forge-bootstrap-preflight/v1",
"revision": _REVISION,
"workspace_image_digest": _WORKSPACE_DIGEST,
"frontdoor_image_digest": _FRONTDOOR_DIGEST,
}
check_names = {
"deployment_apply_policy",
"controller_trust",
"managed_appdata_root_selection",
"exact_main",
"source_manifest",
"managed_appdata",
"proxy_network",
"resource_collisions",
"push_validation",
"image_registry",
"oidc",
"secret_delivery",
}
job = DeploymentJob(
id=_PREFLIGHT_ID,
application_id="itworx-forge",
repository="jens/itworx-forge",
revision=_REVISION,
plan_sha256=hashlib.sha256(
json.dumps(profile, sort_keys=True, separators=(",", ":")).encode()
).hexdigest(),
idempotency_sha256="e" * 64,
status="preflight_succeeded",
stage="preflight_complete",
completed_at=utc_now(),
plan_json=profile,
preflight_json={
"apiVersion": "itworx.forge-bootstrap-preflight/v1",
"status": "passed",
"blockers": [],
"unknowns": [],
"checks": [{"name": name, "status": "passed", "detail": "passed"} for name in sorted(check_names)],
},
evidence_json={
"profile": "forge-production-bootstrap",
"policyId": "forge-apply-policy",
"workspaceImageDigest": _WORKSPACE_DIGEST,
"frontdoorImageDigest": _FRONTDOOR_DIGEST,
"controller": {
"mainRevision": _REVISION,
"attestations": {
"push_validation": {
"context": "Managed validation / full (push)",
"conclusion": "success",
"revision": _REVISION,
},
"image_registry": {
"owner": _FORGE_PRIVATE_OWNER,
"ownerVisibility": "private",
"workspaceDigest": _WORKSPACE_DIGEST,
"frontdoorDigest": _FRONTDOOR_DIGEST,
"workspacePullable": True,
"frontdoorPullable": True,
"workspaceSource": _FORGE_SOURCE_URL,
"workspaceRevision": _REVISION,
"frontdoorSource": _FORGE_SOURCE_URL,
"frontdoorRevision": _REVISION,
},
"oidc": _oidc_attestation(),
"secret_delivery": {
"requiredFilesReady": True,
"metadataSha256": "f" * 64,
},
},
},
},
)
session.add(job)
session.commit()
return job
def _trust(session, *, controller_identity="unraid-controller"):
EnrollmentPlane.set_trust_config(
session,
EnrollmentTrustConfigV1(
controller_identity=controller_identity,
bootstrap_secret_ref="CONTROLLER_BOOTSTRAP",
trusted_appdata_root="/var/lib/docker/volumes/itworx-managed-appdata/_data",
managed_appdata_backup_acknowledged=True,
approved_networks=("itworx-mcp-hub-npm",),
),
)
set_secret(session, "FORGE_DEPLOYMENT_BROKER", "f" * 40, commit=True)
def _policy(session, *, manual="allowed"):
session.add(
Policy(
id="forge-apply-policy",
action="deployment.apply",
target="itworx-forge",
voice="blocked",
manual=manual,
risk="high",
reason="explicit exact target",
)
)
session.commit()
def _success(request_id):
return {
"apiVersion": "itworx.forge-first-install/v1",
"status": "deployed",
"request": request_id,
"repository": "jens/itworx-forge",
"revision": _REVISION,
"operation": "forge_first_install",
"workspaceImageDigest": _WORKSPACE_DIGEST,
"frontdoorImageDigest": _FRONTDOOR_DIGEST,
"receipt": {
"revision": _REVISION,
"workspaceImageDigest": _WORKSPACE_DIGEST,
"frontdoorImageDigest": _FRONTDOOR_DIGEST,
"migration": {"service": "migrate", "status": "completed", "result": "succeeded"},
"health": {"status": "healthy", "services": _SERVICE_HEALTH},
},
}
def _needs_credentials(request_id):
return {
"apiVersion": "itworx.forge-first-install/v1",
"status": "needs_database_credentials",
"request": request_id,
"repository": "jens/itworx-forge",
"revision": _REVISION,
"operation": "forge_first_install",
"workspaceImageDigest": _WORKSPACE_DIGEST,
"frontdoorImageDigest": _FRONTDOOR_DIGEST,
}
class _Broker:
def __init__(self, response=None, status_response=None):
self.response = response
self.status_response = status_response
self.apply_calls = []
self.status_calls = []
self.resume_calls = []
self.session = None
def forge_bootstrap_apply(self, profile, *, idempotency_key, secret):
assert self.session is not None
request_id = ForgeBootstrapApplyService.request_id(profile, idempotency_key)
rows = self.session.exec(
select(DeploymentJob).where(DeploymentJob.broker_request_id == request_id)
).all()
assert len(rows) == 1
assert rows[0].status == "applying"
self.apply_calls.append((profile.model_dump(mode="json"), idempotency_key, secret))
if isinstance(self.response, Exception):
raise self.response
return self.response(request_id) if callable(self.response) else self.response
def forge_bootstrap_status(self, request_id, *, secret):
self.status_calls.append((request_id, secret))
if isinstance(self.status_response, Exception):
raise self.status_response
return self.status_response(request_id) if callable(self.status_response) else self.status_response
def forge_bootstrap_resume(self, request_id, *, secret):
rows = self.session.exec(
select(DeploymentJob).where(DeploymentJob.broker_request_id == request_id)
).all()
assert len(rows) == 1
assert rows[0].status == "applying"
assert rows[0].stage == "resume_request_pending"
self.resume_calls.append((request_id, secret))
if isinstance(self.response, Exception):
raise self.response
return self.response(request_id) if callable(self.response) else self.response
def _service(session, broker):
broker.session = session
return ForgeBootstrapApplyService(broker=broker)
def test_apply_profile_rejects_any_caller_selected_runtime_input():
_profile()
for field, value in (
("target", "other-host"),
("endpoint", "https://attacker.invalid"),
("secret_ref", "OTHER_SECRET"),
("command", "docker compose up"),
("host_path", "/etc"),
):
with pytest.raises(ValueError):
_profile(**{field: value})
def test_apply_rejects_preflight_hash_mismatch_before_controller_call(tmp_path):
with _session(tmp_path) as session:
job = _passed_preflight(session)
_trust(session)
_policy(session)
broker = _Broker(response=lambda request_id: _success(request_id))
profile = _profile(preflight_sha256="0" * 64)
with pytest.raises(ForgeBootstrapApplyError, match="preflight receipt"):
_service(session, broker).apply(session, profile, idempotency_key="forge-first-0001")
assert broker.apply_calls == []
assert session.get(DeploymentJob, job.id) is not None
assert len(session.exec(select(DeploymentJob).where(DeploymentJob.id.startswith("forge-install-"))).all()) == 0
def test_blocked_or_unknown_stored_preflight_cannot_submit_apply(tmp_path):
with _session(tmp_path) as session:
preflight = _passed_preflight(session)
preflight.preflight_json = {
**preflight.preflight_json,
"unknowns": ["image_registry"],
}
session.add(preflight)
session.commit()
_trust(session)
_policy(session)
broker = _Broker(response=lambda request_id: _success(request_id))
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
with pytest.raises(ForgeBootstrapApplyError, match="no unknown checks"):
_service(session, broker).apply(session, profile, idempotency_key="forge-first-0007")
assert broker.apply_calls == []
def test_preflight_policy_binding_must_still_match_at_apply(tmp_path):
with _session(tmp_path) as session:
preflight = _passed_preflight(session)
_trust(session)
_policy(session)
session.delete(session.get(Policy, "forge-apply-policy"))
session.add(
Policy(
id="changed-forge-apply-policy",
action="deployment.apply",
target="itworx-forge",
voice="blocked",
manual="allowed",
risk="high",
)
)
session.commit()
broker = _Broker(response=lambda request_id: _success(request_id))
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
with pytest.raises(ForgeBootstrapApplyError, match="policy changed after preflight"):
_service(session, broker).apply(session, profile, idempotency_key="forge-first-0008")
assert broker.apply_calls == []
def test_stored_managed_root_trust_and_fixed_secret_are_required(tmp_path):
with _session(tmp_path) as session:
preflight = _passed_preflight(session)
_policy(session)
broker = _Broker(response=lambda request_id: _success(request_id))
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
with pytest.raises(ForgeBootstrapApplyError, match="managed-root trust"):
_service(session, broker).apply(session, profile, idempotency_key="forge-first-0010")
assert broker.apply_calls == []
@pytest.mark.parametrize("controller_identity", ["tower-deployment-controller", "other-controller"])
def test_foreign_controller_identity_blocks_before_install_intent(tmp_path, controller_identity):
with _session(tmp_path) as session:
preflight = _passed_preflight(session)
_trust(session, controller_identity=controller_identity)
_policy(session)
broker = _Broker(response=lambda request_id: _success(request_id))
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
with pytest.raises(ForgeBootstrapApplyError, match="managed-root trust"):
_service(session, broker).apply(
session, profile, idempotency_key="forge-controller-identity-0001"
)
assert broker.apply_calls == []
assert session.exec(select(DeploymentJob)).all() == [preflight]
@pytest.mark.parametrize(
("owner", "visibility"),
[("another-owner", "private"), (_FORGE_PRIVATE_OWNER, "public")],
)
def test_apply_rejects_registry_attestation_without_fixed_private_owner(
tmp_path, owner, visibility
):
with _session(tmp_path) as session:
preflight = _passed_preflight(session)
evidence = json.loads(json.dumps(preflight.evidence_json))
evidence["controller"]["attestations"]["image_registry"]["owner"] = owner
evidence["controller"]["attestations"]["image_registry"]["ownerVisibility"] = visibility
preflight.evidence_json = evidence
session.add(preflight)
session.commit()
_trust(session)
_policy(session)
broker = _Broker(response=lambda request_id: _success(request_id))
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
with pytest.raises(ForgeBootstrapApplyError, match="verified controller attestations"):
_service(session, broker).apply(
session, profile, idempotency_key="forge-first-private-owner-0001"
)
assert broker.apply_calls == []
def test_apply_persists_before_post_and_only_marks_deployed_with_full_receipt(tmp_path):
with _session(tmp_path) as session:
preflight = _passed_preflight(session)
_trust(session)
_policy(session)
broker = _Broker(response=lambda request_id: _success(request_id))
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
result = _service(session, broker).apply(
session, profile, idempotency_key="forge-first-0002"
)
assert result["status"] == "deployed"
assert len(broker.apply_calls) == 1
request_id = result["request"]
assert len(request_id) == 64
row = session.get(DeploymentJob, result["id"])
assert row is not None and row.broker_request_id == request_id
assert row.evidence_json["controller"]["receipt"]["migration"]["result"] == "succeeded"
def test_wrong_digest_or_incomplete_health_receipt_never_marks_deployed(tmp_path):
with _session(tmp_path) as session:
preflight = _passed_preflight(session)
_trust(session)
_policy(session)
response = _success("will-be-replaced")
response["receipt"]["health"]["services"] = ["api", "dashboard", "itworx-forge", "postgres"]
broker = _Broker(response=lambda request_id: {**response, "request": request_id})
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
result = _service(session, broker).apply(
session, profile, idempotency_key="forge-first-0009"
)
assert result["status"] == "outcome_unknown"
assert result["evidence"]["controller"] == {}
def test_same_idempotency_key_cannot_be_rebound_to_a_different_profile(tmp_path):
with _session(tmp_path) as session:
preflight = _passed_preflight(session)
_trust(session)
_policy(session)
broker = _Broker(response=lambda request_id: _success(request_id))
service = _service(session, broker)
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
service.apply(session, profile, idempotency_key="forge-first-0011")
with pytest.raises(ForgeBootstrapApplyError, match="different Forge first-install profile"):
service.apply(
session,
_profile(preflight_sha256="e" * 64),
idempotency_key="forge-first-0011",
)
assert len(broker.apply_calls) == 1
@pytest.mark.parametrize(
("application_id", "repository"),
(
("other-application", "jens/itworx-forge"),
("itworx-forge", "other/repository"),
),
)
def test_idempotency_replay_rejects_existing_job_for_another_target(
tmp_path, application_id, repository
):
with _session(tmp_path) as session:
profile = _profile()
key = "forge-crossjob-0001"
idempotency_digest = hashlib.sha256(
f"forge-first-install:{key}".encode()
).hexdigest()
plan = profile.model_dump(mode="json")
session.add(
DeploymentJob(
id="unrelated-deployment-job",
application_id=application_id,
repository=repository,
revision=_REVISION,
plan_sha256=hashlib.sha256(
json.dumps(plan, sort_keys=True, separators=(",", ":")).encode()
).hexdigest(),
idempotency_sha256=idempotency_digest,
status="failed",
stage="complete",
plan_json=plan,
)
)
session.commit()
broker = _Broker()
with pytest.raises(ForgeBootstrapApplyError, match="different application or repository"):
_service(session, broker).apply(session, profile, idempotency_key=key)
assert broker.apply_calls == []
assert broker.status_calls == []
def test_integrity_error_replay_rejects_raced_job_for_another_target(tmp_path, monkeypatch):
from sqlalchemy.exc import IntegrityError
engine = create_engine(f"sqlite:///{tmp_path / 'forge-integrity-replay.db'}")
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
preflight = _passed_preflight(session)
_trust(session)
_policy(session)
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
key = "race-" + "replay-" + "fixture"
idempotency_digest = hashlib.sha256(
f"forge-first-install:{key}".encode()
).hexdigest()
job_id = f"forge-install-{idempotency_digest}"
plan = profile.model_dump(mode="json")
broker = _Broker()
original_commit = session.commit
def insert_foreign_race_then_conflict():
session.rollback()
with Session(engine) as competing_session:
competing_session.add(
DeploymentJob(
id=job_id,
application_id="itworx-forge",
repository="other/repository",
revision=_REVISION,
plan_sha256=hashlib.sha256(
json.dumps(plan, sort_keys=True, separators=(",", ":")).encode()
).hexdigest(),
idempotency_sha256=idempotency_digest,
status="failed",
stage="complete",
plan_json=plan,
)
)
competing_session.commit()
raise IntegrityError("simulated concurrent insert", {}, RuntimeError("duplicate"))
monkeypatch.setattr(session, "commit", insert_foreign_race_then_conflict)
with pytest.raises(ForgeBootstrapApplyError, match="different application or repository"):
_service(session, broker).apply(session, profile, idempotency_key=key)
assert broker.apply_calls == []
assert broker.status_calls == []
monkeypatch.setattr(session, "commit", original_commit)
def test_broker_uses_fixed_forge_apply_resume_and_status_routes(monkeypatch):
from backend.app.services.deployment_broker import DeploymentBrokerClient
client = DeploymentBrokerClient(base_url="https://controller.example", verify_tls=True)
calls = []
def request(method, path, **kwargs):
calls.append((method, path, kwargs))
return {"status": "accepted"}
monkeypatch.setattr(client, "_request", request)
profile = _profile()
client.forge_bootstrap_apply(
profile, idempotency_key="forge-broker-0001", secret="s" * 40
)
client.forge_bootstrap_status("a" * 64, secret="s" * 40)
client.forge_bootstrap_resume("a" * 64, secret="s" * 40)
assert [(method, path) for method, path, _kwargs in calls] == [
("POST", "/v1/appops/forge/bootstrap/apply"),
("GET", "/v1/appops/forge/bootstrap/status/" + "a" * 64),
("POST", "/v1/appops/forge/bootstrap/resume/" + "a" * 64),
]
assert calls[0][2]["payload"] == {
"profile": profile.model_dump(mode="json"),
"idempotencyKey": "forge-broker-0001",
}
assert calls[2][2]["payload"] == {}
def test_unknown_apply_outcome_reconciles_by_get_without_resubmitting(tmp_path):
with _session(tmp_path) as session:
preflight = _passed_preflight(session)
_trust(session)
_policy(session)
broker = _Broker(response={"status": "unreachable"})
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
service = _service(session, broker)
first = service.apply(session, profile, idempotency_key="forge-first-0003")
assert first["status"] == "outcome_unknown"
request_id = first["request"]
broker.status_response = lambda _request_id: _success(request_id)
reconciled = service.apply(session, profile, idempotency_key="forge-first-0003")
assert reconciled["status"] == "deployed"
assert len(broker.apply_calls) == 1
assert broker.status_calls == [(request_id, "f" * 40)]
def test_preflight_changed_is_recorded_as_no_mutation(tmp_path):
with _session(tmp_path) as session:
preflight = _passed_preflight(session)
_trust(session)
_policy(session)
broker = _Broker()
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
broker.response = lambda request_id: {
"apiVersion": "itworx.forge-first-install/v1",
"status": "preflight_changed",
"request": request_id,
"repository": "jens/itworx-forge",
"revision": _REVISION,
"operation": "forge_first_install",
"workspaceImageDigest": _WORKSPACE_DIGEST,
"frontdoorImageDigest": _FRONTDOOR_DIGEST,
"noMutation": True,
}
result = _service(session, broker).apply(
session, profile, idempotency_key="forge-first-0004"
)
assert result["status"] == "preflight_changed"
assert result["evidence"]["controller"]["noMutation"] is True
assert result["result"] == "Controller rejected changed Forge first-install preflight before mutation."
def test_database_credentials_state_requires_explicit_resume_and_never_reposts_apply(tmp_path):
with _session(tmp_path) as session:
preflight = _passed_preflight(session)
_trust(session)
_policy(session)
broker = _Broker()
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
service = _service(session, broker)
broker.response = lambda request_id: _needs_credentials(request_id)
first = service.apply(session, profile, idempotency_key="forge-first-0005")
assert first["status"] == "needs_database_credentials"
request_id = first["request"]
assert len(broker.apply_calls) == 1
# Replaying apply only asks the durable read-only status endpoint.
broker.status_response = lambda request: _needs_credentials(request)
replay = service.apply(session, profile, idempotency_key="forge-first-0005")
assert replay["status"] == "needs_database_credentials"
assert len(broker.apply_calls) == 1
# Only the explicit resume call may submit the controller's one-shot resume.
broker.response = lambda request: _success(request)
resumed = service.resume(session, first["id"])
assert resumed["status"] == "deployed"
assert len(broker.apply_calls) == 1
assert broker.resume_calls == [(request_id, "f" * 40)]
def test_concurrent_explicit_resume_claims_only_once(tmp_path):
engine = create_engine(f"sqlite:///{tmp_path / 'forge-concurrent-resume.db'}")
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
preflight = _passed_preflight(session)
_trust(session)
_policy(session)
initial_broker = _Broker(response=lambda request_id: _needs_credentials(request_id))
initial_broker.session = session
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
initial = ForgeBootstrapApplyService(broker=initial_broker).apply(
session, profile, idempotency_key="forge-concurrent-resume-0001"
)
assert initial["status"] == "needs_database_credentials"
status_barrier = threading.Barrier(2)
class ConcurrentBroker:
def __init__(self):
self.status_calls = []
self.resume_calls = []
self.lock = threading.Lock()
def forge_bootstrap_status(self, request_id, *, secret):
with self.lock:
self.status_calls.append((request_id, secret))
status_barrier.wait(timeout=10)
return _needs_credentials(request_id)
def forge_bootstrap_resume(self, request_id, *, secret):
with self.lock:
self.resume_calls.append((request_id, secret))
return {
"apiVersion": "itworx.forge-first-install/v1",
"status": "accepted",
"request": request_id,
"repository": "jens/itworx-forge",
"revision": _REVISION,
"operation": "forge_first_install",
"workspaceImageDigest": _WORKSPACE_DIGEST,
"frontdoorImageDigest": _FRONTDOOR_DIGEST,
}
broker = ConcurrentBroker()
service = ForgeBootstrapApplyService(broker=broker)
results = []
errors = []
def resume_in_own_session():
try:
with Session(engine) as session:
results.append(service.resume(session, initial["id"]))
except Exception as exc: # noqa: BLE001 - surface thread failures in the assertion below.
errors.append(exc)
threads = [threading.Thread(target=resume_in_own_session) for _ in range(2)]
for thread in threads:
thread.start()
for thread in threads:
thread.join(timeout=20)
assert all(not thread.is_alive() for thread in threads)
assert errors == []
assert len(broker.status_calls) == 2
assert len(broker.resume_calls) == 1
assert all(result["status"] == "applying" for result in results)
def test_stale_needs_credentials_get_cannot_reopen_claimed_resume(tmp_path):
engine = create_engine(f"sqlite:///{tmp_path / 'forge-stale-resume-status.db'}")
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
preflight = _passed_preflight(session)
_trust(session)
_policy(session)
initial_broker = _Broker(response=lambda request_id: _needs_credentials(request_id))
initial_broker.session = session
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
initial = ForgeBootstrapApplyService(broker=initial_broker).apply(
session, profile, idempotency_key="forge-stale-resume-0001"
)
assert initial["status"] == "needs_database_credentials"
post_started = threading.Event()
release_post = threading.Event()
class DelayedResumeBroker:
def __init__(self):
self.status_calls = []
self.resume_calls = []
def forge_bootstrap_status(self, request_id, *, secret):
self.status_calls.append((request_id, secret))
return _needs_credentials(request_id)
def forge_bootstrap_resume(self, request_id, *, secret):
self.resume_calls.append((request_id, secret))
post_started.set()
assert release_post.wait(timeout=10)
return {
"apiVersion": "itworx.forge-first-install/v1",
"status": "accepted",
"request": request_id,
"repository": "jens/itworx-forge",
"revision": _REVISION,
"operation": "forge_first_install",
"workspaceImageDigest": _WORKSPACE_DIGEST,
"frontdoorImageDigest": _FRONTDOOR_DIGEST,
}
broker = DelayedResumeBroker()
service = ForgeBootstrapApplyService(broker=broker)
first_result = []
first_errors = []
def first_resume():
try:
with Session(engine) as session:
first_result.append(service.resume(session, initial["id"]))
except Exception as exc: # noqa: BLE001 - surface thread failures in the assertion below.
first_errors.append(exc)
first_thread = threading.Thread(target=first_resume)
first_thread.start()
assert post_started.wait(timeout=10)
# The controller has not processed the first POST yet and still answers
# needs_database_credentials. That stale observation cannot reset the
# persisted local claim or authorize another resume POST.
with Session(engine) as session:
second_result = service.resume(session, initial["id"])
assert second_result["status"] == "outcome_unknown"
assert second_result["stage"] == "resume_status_unresolved"
release_post.set()
first_thread.join(timeout=15)
assert not first_thread.is_alive()
assert first_errors == []
assert first_result[0]["status"] == "outcome_unknown"
assert len(broker.status_calls) == 2
assert len(broker.resume_calls) == 1
def test_resume_refuses_without_signed_pre_migration_credentials_state(tmp_path):
with _session(tmp_path) as session:
preflight = _passed_preflight(session)
_trust(session)
_policy(session)
broker = _Broker(response=lambda request: _success(request))
profile = _profile(preflight_sha256=canonical_preflight_receipt(preflight))
service = _service(session, broker)
first = service.apply(session, profile, idempotency_key="forge-first-0006")
assert first["status"] == "deployed"
result = service.resume(session, first["id"])
assert result["status"] == "deployed"
assert broker.resume_calls == []
def test_preflight_serialization_exposes_server_computed_receipt_digest(tmp_path):
from backend.app.services.forge_bootstrap_preflight import (
ForgeBootstrapPreflightService,
)
with _session(tmp_path) as session:
job = _passed_preflight(session)
serialized = ForgeBootstrapPreflightService.serialize_job(job)
assert serialized["preflightSha256"] == canonical_preflight_receipt(job)
def test_controller_passed_checks_require_profile_bound_attestations(monkeypatch):
from backend.app.schemas.forge_bootstrap import ForgeBootstrapPreflightV1
from backend.app.services.forge_bootstrap_apply import _attestations_ready
from backend.app.services.forge_bootstrap_preflight import (
_checked_controller_result,
)
profile = ForgeBootstrapPreflightV1.model_validate(
{
"api_version": "itworx.forge-bootstrap-preflight/v1",
"revision": _REVISION,
"workspace_image_digest": _WORKSPACE_DIGEST,
"frontdoor_image_digest": _FRONTDOOR_DIGEST,
}
)
checks = {
name: {"status": "passed"}
for name in (
"exact_main", "source_manifest", "managed_appdata", "proxy_network",
"resource_collisions", "push_validation", "image_registry", "oidc", "secret_delivery",
)
}
result = {
"apiVersion": "itworx.forge-bootstrap-preflight/v1",
"status": "preflight_ok",
"revision": _REVISION,
"workspaceImageDigest": _WORKSPACE_DIGEST,
"frontdoorImageDigest": _FRONTDOOR_DIGEST,
"checks": checks,
"evidence": {},
}
assert _checked_controller_result(result, profile) is None
result["evidence"] = {
"attestations": {
"push_validation": {
"context": "Managed validation / full (push)",
"conclusion": "success",
"revision": _REVISION,
},
"image_registry": {
"owner": _FORGE_PRIVATE_OWNER,
"ownerVisibility": "private",
"workspaceDigest": _WORKSPACE_DIGEST,
"frontdoorDigest": _FRONTDOOR_DIGEST,
"workspacePullable": True,
"frontdoorPullable": True,
"workspaceSource": _FORGE_SOURCE_URL,
"workspaceRevision": _REVISION,
"frontdoorSource": _FORGE_SOURCE_URL,
"frontdoorRevision": _REVISION,
},
"oidc": _oidc_attestation(),
"secret_delivery": {"requiredFilesReady": True, "metadataSha256": "f" * 64},
}
}
parsed = _checked_controller_result(result, profile)
assert parsed is not None
assert parsed[1]["attestations"]["image_registry"]["workspaceDigest"] == _WORKSPACE_DIGEST
assert parsed[1]["attestations"]["oidc"] == _oidc_attestation()
assert _attestations_ready(parsed[1], _profile())
for protected_owner in (None, "Jens", "owner.with.dot", "a" * 40):
monkeypatch.setitem(settings.apis, "FORGE_PRIVATE_PACKAGE_OWNER", protected_owner)
assert not _attestations_ready(parsed[1], _profile())
monkeypatch.setitem(settings.apis, "FORGE_PRIVATE_PACKAGE_OWNER", _FORGE_PRIVATE_OWNER)
# A positive controller OIDC status is downgraded unless the signed proof
# matches the independently configured AppOps mapping and signing-key IDs.
result["evidence"]["attestations"]["oidc"]["scopeMappingIds"]["profile"] = "41234567-89ab-4cde-8fab-0123456789ab"
parsed = _checked_controller_result(result, profile)
assert parsed is not None
assert next(item for item in parsed[0] if item["name"] == "oidc")["status"] == "blocked"
assert "oidc" not in parsed[1].get("attestations", {})
result["evidence"]["attestations"]["oidc"] = _oidc_attestation()
monkeypatch.setitem(settings.apis, "AUTHENTIK_FORGE_OIDC_PROFILE_CLAIM_MAPPING", None)
parsed = _checked_controller_result(result, profile)
assert parsed is not None
assert next(item for item in parsed[0] if item["name"] == "oidc")["status"] == "blocked"
monkeypatch.setitem(settings.apis, "AUTHENTIK_FORGE_OIDC_PROFILE_CLAIM_MAPPING", _OIDC_PROFILE_MAPPING_ID)
monkeypatch.setitem(settings.apis, "AUTHENTIK_FORGE_OIDC_SIGNING_KEY_ID", None)
assert not _attestations_ready(
{"attestations": {"oidc": _oidc_attestation()}}, _profile()
)
monkeypatch.setitem(settings.apis, "AUTHENTIK_FORGE_OIDC_SIGNING_KEY_ID", _OIDC_SIGNING_KEY_ID)
for field, value in (
("clientSecretMatched", False),
("scopeMappingExpressionSha256", {**_OIDC_MAPPING_EXPRESSION_SHA256, "profile": "0" * 64}),
("signingKeyPrivateAvailable", False),
("jwksKid", "attacker-kid"),
):
original = _oidc_attestation()
result["evidence"]["attestations"]["oidc"] = {**original, field: value}
parsed = _checked_controller_result(result, profile)
assert parsed is not None
assert next(item for item in parsed[0] if item["name"] == "oidc")["status"] == "blocked"
result["evidence"]["attestations"]["oidc"] = _oidc_attestation()
result["evidence"]["attestations"]["image_registry"]["ownerVisibility"] = "public"
assert _checked_controller_result(result, profile) is None
result["evidence"]["attestations"]["image_registry"]["ownerVisibility"] = "private"
result["evidence"]["attestations"]["image_registry"]["owner"] = "unreviewed-owner"
assert _checked_controller_result(result, profile) is None
result["evidence"]["attestations"]["image_registry"]["owner"] = _FORGE_PRIVATE_OWNER
result["evidence"]["attestations"]["image_registry"]["workspaceSource"] = "https://attacker.invalid/repo"
assert _checked_controller_result(result, profile) is None
result["evidence"]["attestations"]["image_registry"]["workspaceSource"] = _FORGE_SOURCE_URL
result["evidence"]["attestations"]["image_registry"]["frontdoorRevision"] = "d" * 40
assert _checked_controller_result(result, profile) is None
result["evidence"]["attestations"]["image_registry"]["frontdoorRevision"] = _REVISION
result["evidence"]["attestations"]["image_registry"]["workspaceDigest"] = _FRONTDOOR_DIGEST
assert _checked_controller_result(result, profile) is None
result["evidence"]["attestations"]["image_registry"]["workspaceDigest"] = _WORKSPACE_DIGEST
result["evidence"]["attestations"]["secret_delivery"]["metadataSha256"] = "not-a-digest"
assert _checked_controller_result(result, profile) is None
def test_controller_oidc_attestation_accepts_authentik_legacy_kid():
legacy_kid = "8cf5f32a6c6d9a876fd0284980a25ce0"
attestation = {
**_oidc_attestation(),
"signingKeyKid": legacy_kid,
"jwksKid": legacy_kid,
}
assert forge_oidc_attestation_matches(attestation)
def test_controller_oidc_attestation_binds_exact_scope_mappings_and_discovery():
valid = _oidc_attestation()
assert forge_oidc_attestation_matches(valid)
for invalid in (
{**valid, "mappingId": _OIDC_PROFILE_MAPPING_ID},
{**valid, "mappingName": "legacy claim"},
{**valid, "mappingScope": "groups"},
{**valid, "mappingExpressionSha256": "0" * 64},
{**valid, "scopeMappingIds": {"openid": _OIDC_OPENID_MAPPING_ID}},
{
**valid,
"scopeMappingIds": {
"openid": _OIDC_EMAIL_MAPPING_ID,
"email": _OIDC_OPENID_MAPPING_ID,
"profile": _OIDC_PROFILE_MAPPING_ID,
},
},
{**valid, "scopeMappingIds": {**_OIDC_MAPPING_IDS, "profile": _OIDC_EMAIL_MAPPING_ID}},
{**valid, "scopeMappingIds": {**_OIDC_MAPPING_IDS, "email": _OIDC_OPENID_MAPPING_ID}},
{**valid, "supportedScopes": ["email", "groups", "openid", "profile"]},
{**valid, "supportedScopes": ["email", "openid"]},
{**valid, "authorizationCodeFlowSupported": 1},
{**valid, "standardMappingsManaged": 1},
{**valid, "profileMappingName": "stock profile"},
{**valid, "applicationId": "123"},
{**valid, "providerId": "457"},
):
assert not forge_oidc_attestation_matches(invalid)
def test_controller_oidc_attestation_requires_current_protected_scope_binding(monkeypatch):
attestation = _oidc_attestation()
assert forge_oidc_attestation_matches(attestation)
monkeypatch.setitem(settings.apis, "AUTHENTIK_FORGE_OIDC_BINDING_VALID", 1)
assert not forge_oidc_attestation_matches(attestation)
monkeypatch.setitem(settings.apis, "AUTHENTIK_FORGE_OIDC_BINDING_VALID", True)
monkeypatch.setitem(settings.apis, "AUTHENTIK_FORGE_OIDC_PROVIDER_ID", "0456")
assert not forge_oidc_attestation_matches(attestation)
monkeypatch.setitem(settings.apis, "AUTHENTIK_FORGE_OIDC_PROVIDER_ID", _OIDC_PROVIDER_ID)
monkeypatch.setitem(settings.apis, "AUTHENTIK_FORGE_OIDC_EMAIL_CLAIM_MAPPING", _OIDC_OPENID_MAPPING_ID)
assert not forge_oidc_attestation_matches(attestation)
def test_controller_oidc_attestation_requires_canonical_sha512_kid():
assert forge_oidc_attestation_matches(_oidc_attestation())
noncanonical_kid = "A" * 85 + "B"
assert not forge_oidc_attestation_matches(
{
**_oidc_attestation(),
"signingKeyKid": noncanonical_kid,
"jwksKid": noncanonical_kid,
}
)