summaryrefslogtreecommitdiff
path: root/src/authorization/cedar_authorizer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/authorization/cedar_authorizer.rs')
-rw-r--r--src/authorization/cedar_authorizer.rs141
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>)
+ }
+}