1049 lines
43 KiB
Python
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,
|
|
}
|
|
)
|