mirror of
https://github.com/CloudNebulaProject/barycenter.git
synced 2026-04-10 13:10:42 +00:00
Fix formatting
Signed-off-by: Till Wegmueller <toasterson@gmail.com>
This commit is contained in:
parent
df57dda960
commit
4f0dac7645
6 changed files with 95 additions and 83 deletions
|
|
@ -67,16 +67,16 @@ enum Token {
|
||||||
Dot,
|
Dot,
|
||||||
LParen,
|
LParen,
|
||||||
RParen,
|
RParen,
|
||||||
Eq, // ==
|
Eq, // ==
|
||||||
Ne, // !=
|
Ne, // !=
|
||||||
Gt, // >
|
Gt, // >
|
||||||
Lt, // <
|
Lt, // <
|
||||||
Ge, // >=
|
Ge, // >=
|
||||||
Le, // <=
|
Le, // <=
|
||||||
And, // &&
|
And, // &&
|
||||||
Or, // ||
|
Or, // ||
|
||||||
Not, // !
|
Not, // !
|
||||||
In, // in
|
In, // in
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tokenize(input: &str) -> Result<Vec<Token>, AuthzError> {
|
fn tokenize(input: &str) -> Result<Vec<Token>, AuthzError> {
|
||||||
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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![GrantTuple {
|
||||||
|
relation: "member".into(),
|
||||||
|
object_type: "group".into(),
|
||||||
|
object_id: "finance".into(),
|
||||||
|
subject_type: "user".into(),
|
||||||
|
subject_id: "carol".into(),
|
||||||
|
subject_relation: None,
|
||||||
}],
|
}],
|
||||||
grants: vec![
|
|
||||||
GrantTuple {
|
|
||||||
relation: "member".into(),
|
|
||||||
object_type: "group".into(),
|
|
||||||
object_id: "finance".into(),
|
|
||||||
subject_type: "user".into(),
|
|
||||||
subject_id: "carol".into(),
|
|
||||||
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]
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,11 @@ 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);
|
||||||
file_count += 1;
|
file_count += 1;
|
||||||
|
|
@ -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,
|
}],
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,23 +135,17 @@ 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")
|
AuthzError::InvalidGrant(format!(
|
||||||
.and_then(|v| v.as_string())
|
"grant `{relation}` missing `on` property (e.g. on=\"vm/vm-123\")"
|
||||||
.ok_or_else(|| {
|
))
|
||||||
AuthzError::InvalidGrant(format!(
|
})?;
|
||||||
"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")
|
AuthzError::InvalidGrant(format!(
|
||||||
.and_then(|v| v.as_string())
|
"grant `{relation}` missing `to` property (e.g. to=\"user/alice\")"
|
||||||
.ok_or_else(|| {
|
))
|
||||||
AuthzError::InvalidGrant(format!(
|
})?;
|
||||||
"grant `{relation}` missing `to` property (e.g. to=\"user/alice\")"
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let obj = ObjectRef::parse(on).ok_or_else(|| {
|
let obj = ObjectRef::parse(on).ok_or_else(|| {
|
||||||
AuthzError::InvalidGrant(format!(
|
AuthzError::InvalidGrant(format!(
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue