diff options
Diffstat (limited to 'src/authorization/cedar_authorizer.rs')
| -rw-r--r-- | src/authorization/cedar_authorizer.rs | 123 |
1 files changed, 114 insertions, 9 deletions
diff --git a/src/authorization/cedar_authorizer.rs b/src/authorization/cedar_authorizer.rs index 44bc9e06..577b75ba 100644 --- a/src/authorization/cedar_authorizer.rs +++ b/src/authorization/cedar_authorizer.rs @@ -1,11 +1,28 @@ 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; -pub struct CedarAuthorizer {} +#[derive(Debug)] +pub struct CedarAuthorizer { + policies: PolicySet, + authorizer: CedarAuth, +} impl CedarAuthorizer { pub fn new() -> CedarAuthorizer { - 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, + } } } @@ -17,21 +34,83 @@ impl Default for CedarAuthorizer { impl Authorizer for CedarAuthorizer { fn authorize(&self, request: CheckRequest) -> bool { - let headers = request + 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) - .unwrap(); - - if let Some(authorization) = headers.get("authorization") { - if authorization == "Bearer valid-token" { - return true; + { + 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, } + } +} - 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>) } } @@ -96,6 +175,32 @@ mod tests { assert!(!result); } + #[test] + fn test_cedar_authorizer_allows_static_assets() { + let authorizer = CedarAuthorizer::new(); + let mut headers = HashMap::new(); + headers.insert(":path".to_string(), "/static/style.css".to_string()); + let request = create_request(|item: &mut HttpRequest| { + item.headers = headers; + }); + + let result = authorizer.authorize(request); + assert!(result); + } + + #[test] + fn test_cedar_authorizer_allows_js_assets() { + let authorizer = CedarAuthorizer::new(); + let mut headers = HashMap::new(); + headers.insert(":path".to_string(), "/app.js".to_string()); + let request = create_request(|item: &mut HttpRequest| { + item.headers = headers; + }); + + let result = authorizer.authorize(request); + assert!(result); + } + // test css passthrough // test javascript passthrough // test ico passthrough |
