/// Integration test: Cedar authorization — multi-policy permit/deny scenarios. use std::io::Write; use stratum_orchestrator::auth::CedarAuthorizer; use tempfile::TempDir; fn write_policy(dir: &TempDir, name: &str, content: &str) { let path = dir.path().join(name); let mut f = std::fs::File::create(path).unwrap(); f.write_all(content.as_bytes()).unwrap(); } #[test] fn test_permit_orchestrator_executes_allowed_node() { let dir = TempDir::new().unwrap(); write_policy( &dir, "allow.cedar", r#"permit( principal == User::"orchestrator", action == Action::"execute", resource == Node::"lint" );"#, ); let authz = CedarAuthorizer::load_from_dir(dir.path()).unwrap(); authz .authorize("orchestrator", "execute", "Node::\"lint\"") .unwrap(); } #[test] fn test_deny_unknown_principal() { let dir = TempDir::new().unwrap(); write_policy( &dir, "allow.cedar", r#"permit( principal == User::"orchestrator", action == Action::"execute", resource == Node::"lint" );"#, ); let authz = CedarAuthorizer::load_from_dir(dir.path()).unwrap(); // Unknown principal — no matching permit → implicit deny let err = authz .authorize("rogue-agent", "execute", "Node::\"lint\"") .unwrap_err(); assert!(err.to_string().contains("Cedar denied")); } #[test] fn test_deny_wrong_resource() { let dir = TempDir::new().unwrap(); write_policy( &dir, "allow.cedar", r#"permit( principal == User::"orchestrator", action == Action::"execute", resource == Node::"lint" );"#, ); let authz = CedarAuthorizer::load_from_dir(dir.path()).unwrap(); let err = authz .authorize("orchestrator", "execute", "Node::\"build\"") .unwrap_err(); assert!(err.to_string().contains("Cedar denied")); } #[test] fn test_explicit_forbid_overrides_permit() { let dir = TempDir::new().unwrap(); write_policy( &dir, "00_permit.cedar", r#"permit( principal == User::"orchestrator", action == Action::"execute", resource == Node::"lint" );"#, ); write_policy( &dir, "01_forbid.cedar", r#"forbid(principal, action, resource);"#, ); let authz = CedarAuthorizer::load_from_dir(dir.path()).unwrap(); // Cedar: forbid overrides permit let err = authz .authorize("orchestrator", "execute", "Node::\"lint\"") .unwrap_err(); assert!(err.to_string().contains("Cedar denied")); } #[test] fn test_empty_policy_dir_returns_error() { let dir = TempDir::new().unwrap(); let err = match CedarAuthorizer::load_from_dir(dir.path()) { Err(e) => e, Ok(_) => panic!("expected load_from_dir to fail on empty directory"), }; assert!( err.to_string().contains("no .cedar policy files"), "expected 'no .cedar policy files' error, got: {err}" ); } #[test] fn test_multiple_permits_any_matching_allows() { let dir = TempDir::new().unwrap(); write_policy( &dir, "nodes.cedar", r#"permit( principal == User::"orchestrator", action == Action::"execute", resource == Node::"lint" ); permit( principal == User::"orchestrator", action == Action::"execute", resource == Node::"build" ); permit( principal == User::"orchestrator", action == Action::"execute", resource == Node::"install" );"#, ); let authz = CedarAuthorizer::load_from_dir(dir.path()).unwrap(); for node in ["lint", "build", "install"] { authz .authorize("orchestrator", "execute", &format!("Node::\"{node}\"")) .unwrap_or_else(|e| panic!("node '{node}' should be permitted: {e}")); } // Node not in the permit list → deny authz .authorize("orchestrator", "execute", "Node::\"notify\"") .unwrap_err(); }