summaryrefslogtreecommitdiff
path: root/src/authorization/cedar_authorizer.rs
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-06-24 14:36:58 -0600
committermo khan <mo@mokhan.ca>2025-06-24 14:36:58 -0600
commit85490a4cfa7f3836d3d2f1e7cbfe48b668aa484b (patch)
tree3fb62e5566ef838187b8568f9e71d0495f24d812 /src/authorization/cedar_authorizer.rs
parenta0537b163037a92652ec92c1f47945e0572bb76e (diff)
feat: connect check service to a minimal cedar policy
Diffstat (limited to 'src/authorization/cedar_authorizer.rs')
-rw-r--r--src/authorization/cedar_authorizer.rs123
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