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> { // 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::>())?; CedarRequest::new(principal, action, resource, context, None) .map_err(|e| Box::new(e) as Box) } }