diff --git a/org-ops-core/src/manifest_loader.rs b/org-ops-core/src/manifest_loader.rs index b2e61e9..f8b0d70 100644 --- a/org-ops-core/src/manifest_loader.rs +++ b/org-ops-core/src/manifest_loader.rs @@ -52,10 +52,41 @@ pub struct ManifestLoadResult { /// Reads the manifest JSON (from ConfigMap mount or API), filters /// entries by the session's ShellClass and delegation scope, and /// optionally verifies on-disk binary hashes. +/// Optional signature status from the reconciler. +/// Passed alongside manifest_json when the ConfigMap includes it. +#[derive(Debug, Clone, Default)] +pub struct ManifestMeta { + /// Whether the reconciler verified all required witness signatures. + pub signature_valid: bool, + /// Whether witness signatures are required for this manifest. + pub signatures_required: bool, +} + pub fn load_manifest( ctx: &SessionContext, manifest_json: &str, ) -> Result { + load_manifest_with_meta(ctx, manifest_json, &ManifestMeta::default()) +} + +/// Load manifest with optional signature metadata. +/// +/// If `meta.signatures_required` is true and `meta.signature_valid` is +/// false, the load is rejected (unsigned manifests don't take effect). +pub fn load_manifest_with_meta( + ctx: &SessionContext, + manifest_json: &str, + meta: &ManifestMeta, +) -> Result { + // Check signature if required + if meta.signatures_required && !meta.signature_valid { + return Err( + "manifest not signed by all required Accord witnesses — \ + contact your quorum administrator" + .into(), + ); + } + let entries: Vec = serde_json::from_str(manifest_json).map_err(|e| format!("manifest parse error: {e}"))?; @@ -248,6 +279,29 @@ mod tests { assert!(result.is_err()); } + #[test] + fn unsigned_manifest_rejected_when_required() { + let meta = ManifestMeta { + signatures_required: true, + signature_valid: false, + }; + let result = + load_manifest_with_meta(&ctx(ShellClass::System, true), &manifest_json(), &meta); + assert!(result.is_err()); + assert!(result.unwrap_err().contains("not signed")); + } + + #[test] + fn signed_manifest_accepted() { + let meta = ManifestMeta { + signatures_required: true, + signature_valid: true, + }; + let result = + load_manifest_with_meta(&ctx(ShellClass::System, true), &manifest_json(), &meta); + assert!(result.is_ok()); + } + #[test] fn manifest_cid_is_computed() { let result = load_manifest(&ctx(ShellClass::System, true), &manifest_json()).unwrap();