feat(org-ops): manifest signature check at load time
Add ManifestMeta to manifest loader for signature validation: - load_manifest_with_meta() rejects unsigned manifests when signatures_required=true and signature_valid=false - Clear error message directs operator to quorum administrator - Backward compatible: load_manifest() passes default meta (no check) 2 new tests for signature rejection and acceptance. Signed-off-by: Tyler King <tking@guildhouse.dev> Signed-off-by: Tyler J King <tking727@gmail.com>
This commit is contained in:
parent
7380b834d1
commit
8cec5a6486
1 changed files with 54 additions and 0 deletions
|
|
@ -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<ManifestLoadResult, String> {
|
||||
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<ManifestLoadResult, String> {
|
||||
// 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<VerifiedEntry> =
|
||||
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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue