1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
use super::authorizer::Authorizer;
use cedar_policy::{
Authorizer as CedarAuth, Context, Entities, EntityId, EntityTypeName, EntityUid, PolicySet,
Request as CedarRequest, RestrictedExpression,
};
use envoy_types::ext_authz::v3::pb::CheckRequest;
use std::collections::HashMap;
use std::str::FromStr;
#[derive(Debug)]
pub struct CedarAuthorizer {
policies: PolicySet,
authorizer: CedarAuth,
}
impl CedarAuthorizer {
pub fn new() -> CedarAuthorizer {
let policy_src = include_str!("../../policies/auth_policy.cedar");
let policies = policy_src.parse().expect("Failed to parse Cedar policies");
let authorizer = CedarAuth::new();
CedarAuthorizer {
policies,
authorizer,
}
}
}
impl Default for CedarAuthorizer {
fn default() -> Self {
Self::new()
}
}
impl Authorizer for CedarAuthorizer {
fn authorize(&self, request: CheckRequest) -> bool {
let headers = match request
.attributes
.as_ref()
.and_then(|attr| attr.request.as_ref())
.and_then(|req| req.http.as_ref())
.map(|http| &http.headers)
{
Some(headers) => headers,
None => return false,
};
// Extract authorization token
let bearer_token = headers
.get("authorization")
.and_then(|auth| auth.strip_prefix("Bearer "))
.unwrap_or("");
// Extract request path for static asset checking
let path = headers
.get(":path")
.or_else(|| headers.get("path"))
.map_or("", |v| v.as_str());
// Create Cedar entities and request
match self.create_cedar_request(bearer_token, path) {
Ok(cedar_request) => {
let entities = Entities::empty();
let response =
self.authorizer
.is_authorized(&cedar_request, &self.policies, &entities);
matches!(response.decision(), cedar_policy::Decision::Allow)
}
Err(_) => false,
}
}
}
impl CedarAuthorizer {
fn create_cedar_request(
&self,
bearer_token: &str,
path: &str,
) -> Result<CedarRequest, Box<dyn std::error::Error>> {
// Create principal entity
let principal_id = EntityId::from_str("client")?;
let principal_type = EntityTypeName::from_str("User")?;
let principal = EntityUid::from_type_name_and_id(principal_type, principal_id);
// Create action entity
let action_id = EntityId::from_str("check")?;
let action_type = EntityTypeName::from_str("Action")?;
let action = EntityUid::from_type_name_and_id(action_type, action_id);
// Create resource entity
let resource_id = EntityId::from_str("resource")?;
let resource_type = EntityTypeName::from_str("Resource")?;
let resource = EntityUid::from_type_name_and_id(resource_type, resource_id);
// Create context with bearer token and path
let mut context_map = HashMap::new();
if !bearer_token.is_empty() {
context_map.insert(
"bearer_token".to_string(),
RestrictedExpression::from_str(&format!("\"{}\"", bearer_token))?,
);
}
if !path.is_empty() {
context_map.insert(
"path".to_string(),
RestrictedExpression::from_str(&format!("\"{}\"", path))?,
);
}
let context = Context::from_pairs(context_map.into_iter().collect::<Vec<_>>())?;
CedarRequest::new(principal, action, resource, context, None)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
}
}
|