use cedar_policy::*;
use std::{error::Error, str::FromStr};
#[test]
fn authorize_custom_request() -> Result<(), Box<dyn Error>> {
let auth = Authorizer::new();
let mut policies = PolicySet::from_str(
r#"
forbid(principal,action,resource)
when{ context has suspicion };
permit(
principal == Account::"jane",
action,
resource in Album::"jane_vacation"
);
"#,
)?;
let alice_view = Policy::parse(
Some("added policy".to_string()),
r#"
permit(
principal == User::"alice",
action == Action::"view",
resource == Photo::"VacationPhoto94.jpg"
);"#,
)?;
let alice_view_id = alice_view.id().clone();
policies.add(alice_view)?;
let entity_json = r#"
[
{
"uid": {
"__entity": {
"type": "User",
"id": "alice"
}
},
"attrs": {},
"parents": [
{
"__entity": {
"type": "UserGroup",
"id": "jane_friends"
}
}
]
},
{
"uid": {
"__entity": {
"type": "UserGroup",
"id": "jane_friends"
}
},
"attrs": {},
"parents": []
},
{
"uid": {
"__entity": {
"type": "Action",
"id": "view"
}
},
"attrs": {},
"parents": []
},
{
"uid": {
"__entity": {
"type": "Photo",
"id": "VacationPhoto94.jpg"
}
},
"attrs": {},
"parents": [
{
"__entity": {
"type": "Album",
"id": "jane_vacation"
}
}
]
},
{
"uid": {
"__entity": {
"type": "Album",
"id": "jane_vacation"
}
},
"attrs": {},
"parents": [
{
"__entity": {
"type": "Account",
"id": "jane"
}
}
]
},
{
"uid": {
"__entity": {
"type": "Account",
"id": "jane"
}
},
"attrs": {},
"parents": []
}
]
"#;
let entities = Entities::from_json_str(entity_json, None)?;
let principal = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("User").unwrap(),
EntityId::from_str("alice").unwrap(),
);
let action = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("Action").unwrap(),
EntityId::from_str("view").unwrap(),
);
let resource = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("Photo").unwrap(),
EntityId::from_str("VacationPhoto94.jpg").unwrap(),
);
let context = Context::from_pairs([
(
"host_os".to_string(),
RestrictedExpression::from_str(r#""Windows 10""#)?,
),
(
"suspicion".to_string(),
RestrictedExpression::from_str("4")?,
),
])
.unwrap();
let request = Request::new(
Some(principal.clone()),
Some(action.clone()),
Some(resource.clone()),
context,
None,
)
.unwrap();
assert_eq!(
auth.is_authorized(&request, &policies, &entities)
.decision(),
Decision::Deny
);
let request2 = Request::new(
Some(principal),
Some(action.clone()),
Some(resource.clone()),
Context::empty(),
None,
)
.unwrap();
assert_eq!(
auth.is_authorized(&request2, &policies, &entities),
Response::new(Decision::Allow, [alice_view_id].into(), Vec::new())
);
let principal = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("Account").unwrap(),
EntityId::from_str("jane").unwrap(),
);
let request3 = Request::new(
Some(principal.clone()),
None,
Some(resource.clone()),
Context::empty(),
None,
)
.unwrap();
assert_eq!(
auth.is_authorized(&request3, &policies, &entities)
.decision(),
Decision::Allow
);
let request4 = Request::new(
None,
Some(action.clone()),
Some(resource),
Context::empty(),
None,
)
.unwrap();
assert_eq!(
auth.is_authorized(&request4, &policies, &entities)
.decision(),
Decision::Deny
);
let request5 =
Request::new(Some(principal), Some(action), None, Context::empty(), None).unwrap();
assert_eq!(
auth.is_authorized(&request5, &policies, &entities)
.decision(),
Decision::Deny
);
let result = eval_expression(&request2, &entities, &Expression::from_str(r#"10 < 100"#)?)?;
assert_eq!(result, EvalResult::Bool(true));
Ok(())
}
#[test]
fn expression_eval_1() -> Result<(), Box<dyn Error>> {
let entity_json = r#"[ ]"#;
let entities = Entities::from_json_str(entity_json, None)?;
let principal = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("User").unwrap(),
EntityId::from_str("alice").unwrap(),
);
let action = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("Action").unwrap(),
EntityId::from_str("view").unwrap(),
);
let resource = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("Photo").unwrap(),
EntityId::from_str("trip.jpg").unwrap(),
);
let request = Request::new(
Some(principal),
Some(action),
Some(resource),
Context::empty(),
None,
)
.unwrap();
let result = eval_expression(
&request,
&entities,
&Expression::from_str("if 301 > 10 then 100 else 200")?,
)?;
assert_eq!(result, EvalResult::Long(100));
Ok(())
}
#[test]
fn expression_eval_attr() -> Result<(), Box<dyn Error>> {
let entity_json = r#"[
{
"uid": { "__entity" : { "type" : "User", "id" : "alice" } },
"attrs": {"age":19},
"parents": []
}
]"#;
let entities = Entities::from_json_str(entity_json, None)?;
let principal = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("User").unwrap(),
EntityId::from_str("alice").unwrap(),
);
let action = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("Action").unwrap(),
EntityId::from_str("view").unwrap(),
);
let resource = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("Photo").unwrap(),
EntityId::from_str("trip.jpg").unwrap(),
);
let request = Request::new(
Some(principal),
Some(action),
Some(resource),
Context::empty(),
None,
)
.unwrap();
let result = eval_expression(
&request,
&entities,
&Expression::from_str("if principal.age > 18 then 100 else 200")?,
)?;
assert_eq!(result, EvalResult::Long(100));
Ok(())
}
#[test]
fn expression_eval_context() -> Result<(), Box<dyn Error>> {
let entity_json = r#"[
{
"uid": { "__entity" : { "type" : "User", "id" : "alice" }},
"attrs": {"age":19},
"parents": []
}
]"#;
let entities = Entities::from_json_str(entity_json, None)?;
let principal = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("User").unwrap(),
EntityId::from_str("alice").unwrap(),
);
let action = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("Action").unwrap(),
EntityId::from_str("view").unwrap(),
);
let resource = EntityUid::from_type_name_and_id(
EntityTypeName::from_str("Photo").unwrap(),
EntityId::from_str("trip.jpg").unwrap(),
);
let context = Context::from_pairs([
(
"location".to_string(),
RestrictedExpression::from_str(r#""VA""#)?,
),
(
"suspicion".to_string(),
RestrictedExpression::from_str("4")?,
),
])
.unwrap();
let request =
Request::new(Some(principal), Some(action), Some(resource), context, None).unwrap();
let result = eval_expression(
&request,
&entities,
&Expression::from_str(
"if principal.age > 18 && context.location == \"VA\" then 100 else 200",
)?,
)?;
assert_eq!(result, EvalResult::Long(100));
Ok(())
}
#[test]
fn policy_annotations() {
let p: Policy = r#"@anno("good annotation")permit(principal, action, resource);"#
.parse()
.unwrap();
assert_eq!(p.annotation("anno"), Some("good annotation"));
assert_eq!(p.annotations().next(), Some(("anno", "good annotation")));
let t: Template =
r#"@tanno("good annotation")permit(principal == ?principal, action, resource);"#
.parse()
.unwrap();
let t = t.new_id(PolicyId::from_str("new_template_id").unwrap());
assert_eq!(t.annotation("tanno"), Some("good annotation"));
assert_eq!(t.annotations().next(), Some(("tanno", "good annotation")));
let pid = p.id().clone();
let tid = t.id().clone();
let mut s = PolicySet::new();
s.add(p).unwrap();
s.add_template(t).unwrap();
assert_eq!(s.annotation(&pid, "anno"), Some("good annotation"));
assert_eq!(
s.template_annotation(&tid, "tanno"),
Some("good annotation".to_string())
);
}
#[test]
fn change_ids() {
let ps: PolicySet = r#"
@id("first")
permit(principal, action, resource);
@id("second")
permit(principal, action, resource);
"#
.parse()
.unwrap();
let mut new_ps = PolicySet::new();
for p in ps
.policies()
.map(|p| p.new_id(p.annotation("id").unwrap().parse().unwrap()))
{
new_ps.add(p).expect("valid policy choice");
}
assert!(new_ps.policy(&"first".parse().unwrap()).is_some());
}