Fix formatting

Signed-off-by: Till Wegmueller <toasterson@gmail.com>
This commit is contained in:
Till Wegmueller 2026-02-08 20:30:52 +01:00
parent df57dda960
commit 4f0dac7645
No known key found for this signature in database
6 changed files with 95 additions and 83 deletions

View file

@ -476,7 +476,10 @@ fn eval_value(expr: &Expr, context: &Value) -> Result<EvalResult, AuthzError> {
)), )),
} }
} }
Expr::In { element, collection } => { Expr::In {
element,
collection,
} => {
let elem = eval_value(element, context)?; let elem = eval_value(element, context)?;
let coll = eval_value(collection, context)?; let coll = eval_value(collection, context)?;
match coll { match coll {
@ -491,17 +494,13 @@ fn eval_value(expr: &Expr, context: &Value) -> Result<EvalResult, AuthzError> {
let r = eval_value(right, context)?; let r = eval_value(right, context)?;
match op { match op {
BinOp::And => match (&l, &r) { BinOp::And => match (&l, &r) {
(EvalResult::Bool(a), EvalResult::Bool(b)) => { (EvalResult::Bool(a), EvalResult::Bool(b)) => Ok(EvalResult::Bool(*a && *b)),
Ok(EvalResult::Bool(*a && *b))
}
_ => Err(AuthzError::InvalidCondition( _ => Err(AuthzError::InvalidCondition(
"`&&` requires boolean operands".into(), "`&&` requires boolean operands".into(),
)), )),
}, },
BinOp::Or => match (&l, &r) { BinOp::Or => match (&l, &r) {
(EvalResult::Bool(a), EvalResult::Bool(b)) => { (EvalResult::Bool(a), EvalResult::Bool(b)) => Ok(EvalResult::Bool(*a || *b)),
Ok(EvalResult::Bool(*a || *b))
}
_ => Err(AuthzError::InvalidCondition( _ => Err(AuthzError::InvalidCondition(
"`||` requires boolean operands".into(), "`||` requires boolean operands".into(),
)), )),
@ -591,9 +590,7 @@ mod tests {
fn test_parse_boolean_and() { fn test_parse_boolean_and() {
let expr = parse_condition("a > 1 && b < 2").unwrap(); let expr = parse_condition("a > 1 && b < 2").unwrap();
match expr { match expr {
Expr::BinOp { Expr::BinOp { op: BinOp::And, .. } => {}
op: BinOp::And, ..
} => {}
_ => panic!("expected And"), _ => panic!("expected And"),
} }
} }
@ -657,8 +654,7 @@ mod tests {
#[test] #[test]
fn test_evaluate_boolean_and() { fn test_evaluate_boolean_and() {
let expr = let expr = parse_condition("request.time.hour >= 9 && request.time.hour < 17").unwrap();
parse_condition("request.time.hour >= 9 && request.time.hour < 17").unwrap();
let ctx = json!({ "request": { "time": { "hour": 14 } } }); let ctx = json!({ "request": { "time": { "hour": 14 } } });
assert!(evaluate(&expr, &ctx).unwrap()); assert!(evaluate(&expr, &ctx).unwrap());

View file

@ -92,7 +92,10 @@ fn check_abac_rules(
// Check if the principal matches any of the rule's principal patterns // Check if the principal matches any of the rule's principal patterns
let principal_match = rule.principals.is_empty() let principal_match = rule.principals.is_empty()
|| rule.principals.iter().any(|p| matches_principal(principal, p, state)); || rule
.principals
.iter()
.any(|p| matches_principal(principal, p, state));
if !principal_match { if !principal_match {
continue; continue;
@ -323,9 +326,14 @@ mod tests {
fn test_check_inherited_permission() { fn test_check_inherited_permission() {
let state = make_vm_state(); let state = make_vm_state();
// alice has vm_admin which includes vm_viewer -> vm:view_console // alice has vm_admin which includes vm_viewer -> vm:view_console
assert!( assert!(check(
check(&state, "user/alice", "vm:view_console", "vm/vm-123", &json!({})).unwrap() &state,
); "user/alice",
"vm:view_console",
"vm/vm-123",
&json!({})
)
.unwrap());
} }
#[test] #[test]
@ -341,9 +349,14 @@ mod tests {
fn test_check_userset_membership() { fn test_check_userset_membership() {
let state = make_vm_state(); let state = make_vm_state();
// bob is member of group/engineers, which has vm_viewer on vm/vm-456 // bob is member of group/engineers, which has vm_viewer on vm/vm-456
assert!( assert!(check(
check(&state, "user/bob", "vm:view_console", "vm/vm-456", &json!({})).unwrap() &state,
); "user/bob",
"vm:view_console",
"vm/vm-456",
&json!({})
)
.unwrap());
// but bob can't start vm-456 (only viewer) // but bob can't start vm-456 (only viewer)
assert!(!check(&state, "user/bob", "vm:start", "vm/vm-456", &json!({})).unwrap()); assert!(!check(&state, "user/bob", "vm:start", "vm/vm-456", &json!({})).unwrap());
} }
@ -351,9 +364,7 @@ mod tests {
#[test] #[test]
fn test_check_unknown_permission() { fn test_check_unknown_permission() {
let state = make_vm_state(); let state = make_vm_state();
assert!( assert!(!check(&state, "user/alice", "vm:delete", "vm/vm-123", &json!({})).unwrap());
!check(&state, "user/alice", "vm:delete", "vm/vm-123", &json!({})).unwrap()
);
} }
#[test] #[test]
@ -384,20 +395,16 @@ mod tests {
effect: "allow".into(), effect: "allow".into(),
permissions: vec!["invoice:view".into()], permissions: vec!["invoice:view".into()],
principals: vec!["group:finance".into()], principals: vec!["group:finance".into()],
condition: Some( condition: Some("request.time.hour >= 9 && request.time.hour < 17".into()),
"request.time.hour >= 9 && request.time.hour < 17".into(),
),
}], }],
grants: vec![ grants: vec![GrantTuple {
GrantTuple {
relation: "member".into(), relation: "member".into(),
object_type: "group".into(), object_type: "group".into(),
object_id: "finance".into(), object_id: "finance".into(),
subject_type: "user".into(), subject_type: "user".into(),
subject_id: "carol".into(), subject_id: "carol".into(),
subject_relation: None, subject_relation: None,
}, }],
],
}; };
let state = compile_policies(vec![parsed]).unwrap(); let state = compile_policies(vec![parsed]).unwrap();
@ -407,15 +414,25 @@ mod tests {
// Carol is in finance, outside business hours -> denied // Carol is in finance, outside business hours -> denied
let ctx_late = json!({ "request": { "time": { "hour": 22 } } }); let ctx_late = json!({ "request": { "time": { "hour": 22 } } });
assert!( assert!(!check(
!check(&state, "user/carol", "invoice:view", "invoice/inv-1", &ctx_late).unwrap() &state,
); "user/carol",
"invoice:view",
"invoice/inv-1",
&ctx_late
)
.unwrap());
// Dave is NOT in finance -> denied even during business hours // Dave is NOT in finance -> denied even during business hours
let ctx_hours = json!({ "request": { "time": { "hour": 14 } } }); let ctx_hours = json!({ "request": { "time": { "hour": 14 } } });
assert!( assert!(!check(
!check(&state, "user/dave", "invoice:view", "invoice/inv-1", &ctx_hours).unwrap() &state,
); "user/dave",
"invoice:view",
"invoice/inv-1",
&ctx_hours
)
.unwrap());
} }
#[test] #[test]

View file

@ -21,7 +21,9 @@ pub enum AuthzError {
#[error("Invalid policy: {0}")] #[error("Invalid policy: {0}")]
#[diagnostic( #[diagnostic(
code(barycenter::authz::invalid_policy), code(barycenter::authz::invalid_policy),
help("Each policy file must contain valid `resource`, `role`, `rule`, or `grant` KDL nodes") help(
"Each policy file must contain valid `resource`, `role`, `rule`, or `grant` KDL nodes"
)
)] )]
InvalidPolicy(String), InvalidPolicy(String),

View file

@ -32,11 +32,10 @@ pub fn load_policies(dir: &Path) -> Result<AuthzState, AuthzError> {
for entry in entries { for entry in entries {
let path = entry.path(); let path = entry.path();
let contents = std::fs::read_to_string(&path).map_err(|source| { let contents =
AuthzError::PolicyLoadError { std::fs::read_to_string(&path).map_err(|source| AuthzError::PolicyLoadError {
path: path.display().to_string(), path: path.display().to_string(),
source, source,
}
})?; })?;
let parsed = parse_kdl_document(&contents)?; let parsed = parse_kdl_document(&contents)?;
all_parsed.push(parsed); all_parsed.push(parsed);
@ -214,16 +213,14 @@ mod tests {
}, },
], ],
rules: vec![], rules: vec![],
grants: vec![ grants: vec![GrantTuple {
GrantTuple {
relation: "vm_admin".into(), relation: "vm_admin".into(),
object_type: "vm".into(), object_type: "vm".into(),
object_id: "vm-123".into(), object_id: "vm-123".into(),
subject_type: "user".into(), subject_type: "user".into(),
subject_id: "alice".into(), subject_id: "alice".into(),
subject_relation: None, subject_relation: None,
}, }],
],
} }
} }

View file

@ -135,19 +135,13 @@ pub fn parse_kdl_document(source: &str) -> Result<ParsedPolicy, AuthzError> {
) )
})?; })?;
let on = node let on = node.get("on").and_then(|v| v.as_string()).ok_or_else(|| {
.get("on")
.and_then(|v| v.as_string())
.ok_or_else(|| {
AuthzError::InvalidGrant(format!( AuthzError::InvalidGrant(format!(
"grant `{relation}` missing `on` property (e.g. on=\"vm/vm-123\")" "grant `{relation}` missing `on` property (e.g. on=\"vm/vm-123\")"
)) ))
})?; })?;
let to = node let to = node.get("to").and_then(|v| v.as_string()).ok_or_else(|| {
.get("to")
.and_then(|v| v.as_string())
.ok_or_else(|| {
AuthzError::InvalidGrant(format!( AuthzError::InvalidGrant(format!(
"grant `{relation}` missing `to` property (e.g. to=\"user/alice\")" "grant `{relation}` missing `to` property (e.g. to=\"user/alice\")"
)) ))

View file

@ -22,7 +22,13 @@ async fn handle_check(
State(state): State<Arc<AuthzState>>, State(state): State<Arc<AuthzState>>,
Json(req): Json<CheckRequest>, Json(req): Json<CheckRequest>,
) -> impl IntoResponse { ) -> impl IntoResponse {
match engine::check(&state, &req.principal, &req.permission, &req.resource, &req.context) { match engine::check(
&state,
&req.principal,
&req.permission,
&req.resource,
&req.context,
) {
Ok(allowed) => Json(CheckResponse { allowed }).into_response(), Ok(allowed) => Json(CheckResponse { allowed }).into_response(),
Err(e) => e.into_response(), Err(e) => e.into_response(),
} }