Files
sausage/host/tests/cred_policy.rs
T

151 lines
4.0 KiB
Rust

//! Explicit grant approvals honor their limits. Denials suppress duplicate approval prompts.
use sausage::cred::{
edits::{Operation, OperationRequest},
grants::Grant,
items::Item,
requests::{GrantRequest, Overrides},
webauthn::Decision,
*,
};
fn now() -> i64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs() as i64
}
fn fixture() -> (tempfile::TempDir, Cred, Caller, Item) {
let dir = tempfile::tempdir().unwrap();
let c = Cred::open(dir.path()).unwrap();
let caller = Caller {
program: "/bin/test.wasm".into(),
pid: 1,
conversations: vec!["root".into()],
};
let item = c.create_item(serde_json::from_value(serde_json::json!({"name":"test","rules":[],"validate_js":null,"transform_js":"","fields":[]})).unwrap(),&caller,c.unlocked().unwrap()).unwrap();
(dir, c, caller, item)
}
fn grant(item: &Item) -> Grant {
Grant {
id: String::new(),
items: vec![item.id.clone()],
subject: types::Subject::Conversation("root".into()),
exp: None,
max_uses: None,
uses: 0,
revoked: false,
validator: None,
note: None,
}
}
fn operation(c: &Cred, caller: &Caller, key: &str, op: Operation) -> String {
c.file_operation_request(
caller,
key,
OperationRequest {
item: None,
base_version: None,
change: vec![op],
},
)
.unwrap()
}
#[test]
fn grant_operations_apply_overrides_defaults_and_replay_liveness() {
let (_dir, c, caller, item) = fixture();
let op = Operation::GrantCreate {
grant: grant(&item),
};
let id = operation(&c, &caller, "grant", op.clone());
let before = now();
c.approve(
&id,
Decision::Approve,
Overrides {
duration_secs: Some(120),
max_uses: Some(1),
validator: Some("true".into()),
unlimited: false,
},
c.unlocked().unwrap(),
)
.unwrap();
let g = c.grant_list().unwrap().pop().unwrap();
assert!(
g.exp
.is_some_and(|e| (before + 120..=now() + 120).contains(&e))
);
assert_eq!(g.max_uses, Some(1));
assert_eq!(g.validator.as_deref(), Some("true"));
let revoke = operation(
&c,
&caller,
"revoke",
Operation::GrantRevoke { target: g.id },
);
assert!(
c.approve(
&revoke,
Decision::Approve,
Overrides {
duration_secs: Some(1),
..Default::default()
},
c.unlocked().unwrap()
)
.is_err()
);
c.approve(
&revoke,
Decision::Approve,
Overrides::default(),
c.unlocked().unwrap(),
)
.unwrap();
assert_ne!(operation(&c, &caller, "grant", op), id);
let id = operation(
&c,
&caller,
"default",
Operation::GrantCreate {
grant: grant(&item),
},
);
let before = now();
c.approve(
&id,
Decision::Approve,
Overrides::default(),
c.unlocked().unwrap(),
)
.unwrap();
assert!(
c.grant_list()
.unwrap()
.last()
.unwrap()
.exp
.is_some_and(|e| (before + 3600..=now() + 3600).contains(&e))
);
}
#[test]
fn denial_replays_until_expiry_and_requester_duration_is_capped() {
let (_dir, c, caller, item) = fixture();
let request = GrantRequest {
items: vec![item.id],
duration_secs: Some(86401),
max_uses: None,
validator: None,
};
assert!(
c.file_request(&caller, "too-long", request.clone())
.is_err()
);
let request = GrantRequest {
duration_secs: None,
..request
};
let id = c.file_request(&caller, "denied", request.clone()).unwrap();
c.deny(&id, Decision::Deny, c.unlocked().unwrap()).unwrap();
assert_eq!(c.file_request(&caller, "denied", request).unwrap(), id);
}