Jesús Pérez 9095ea6d8e
Some checks failed
Nickel Type Check / Nickel Type Checking (push) Has been cancelled
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (nightly) (push) Has been cancelled
Rust CI / Check + Test + Lint (stable) (push) Has been cancelled
feat: add stratum-orchestrator with graph, state, NATS, and Nickel action nodes
New crates: stratum-orchestrator (Cedar authz, Vault secrets, Nu/agent executors,
  saga runner), stratum-graph (petgraph DAG + SurrealDB repo), stratum-state
  (SurrealDB tracker), platform-nats (NKey auth client), ncl-import-resolver.

  Updates: stratum-embeddings (SurrealDB store + persistent cache), stratum-llm
  circuit breaker. Adds Nickel action-nodes, schemas, config, Nushell scripts,
  docker-compose dev stack, and ADR-003.
2026-02-22 21:33:26 +00:00

147 lines
3.9 KiB
Rust

/// 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();
}