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
|
/// Reads the manifest JSON (from ConfigMap mount or API), filters
|
||||||
/// entries by the session's ShellClass and delegation scope, and
|
/// entries by the session's ShellClass and delegation scope, and
|
||||||
/// optionally verifies on-disk binary hashes.
|
/// 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(
|
pub fn load_manifest(
|
||||||
ctx: &SessionContext,
|
ctx: &SessionContext,
|
||||||
manifest_json: &str,
|
manifest_json: &str,
|
||||||
) -> Result<ManifestLoadResult, String> {
|
) -> 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> =
|
let entries: Vec<VerifiedEntry> =
|
||||||
serde_json::from_str(manifest_json).map_err(|e| format!("manifest parse error: {e}"))?;
|
serde_json::from_str(manifest_json).map_err(|e| format!("manifest parse error: {e}"))?;
|
||||||
|
|
||||||
|
|
@ -248,6 +279,29 @@ mod tests {
|
||||||
assert!(result.is_err());
|
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]
|
#[test]
|
||||||
fn manifest_cid_is_computed() {
|
fn manifest_cid_is_computed() {
|
||||||
let result = load_manifest(&ctx(ShellClass::System, true), &manifest_json()).unwrap();
|
let result = load_manifest(&ctx(ShellClass::System, true), &manifest_json()).unwrap();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue