diff options
Diffstat (limited to 'src/authorization/cedar_authorizer.rs')
| -rw-r--r-- | src/authorization/cedar_authorizer.rs | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/src/authorization/cedar_authorizer.rs b/src/authorization/cedar_authorizer.rs new file mode 100644 index 00000000..a877cf87 --- /dev/null +++ b/src/authorization/cedar_authorizer.rs @@ -0,0 +1,141 @@ +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::fs; +use std::str::FromStr; + +#[derive(Debug)] +pub struct CedarAuthorizer { + policies: PolicySet, + authorizer: CedarAuth, +} + +impl CedarAuthorizer { + pub fn new(policies: cedar_policy::PolicySet) -> CedarAuthorizer { + CedarAuthorizer { + policies, + authorizer: CedarAuth::new(), + } + } + + pub fn new_from(path: &std::path::Path) -> CedarAuthorizer { + Self::new(Self::load_from(path).unwrap_or_else(|_| PolicySet::default())) + } + + fn load_from(path: &std::path::Path) -> Result<PolicySet, Box<dyn std::error::Error>> { + if !path.exists() || !path.is_dir() { + return Ok(PolicySet::default()); + } + + let mut policies = PolicySet::new(); + + for entry in fs::read_dir(path)? { + let file_path = entry?.path(); + + if let Some(extension) = file_path.extension() { + if extension == "cedar" { + let content = fs::read_to_string(&file_path)?; + let file_policies = PolicySet::from_str(&content)?; + + for policy in file_policies.policies() { + policies.add(policy.clone())?; + } + } + } + } + + Ok(policies) + } +} + +impl Default for CedarAuthorizer { + fn default() -> Self { + Self::new_from(std::path::Path::new("/etc/authzd")) + } +} + +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>) + } +} |
