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.rs207
1 files changed, 118 insertions, 89 deletions
diff --git a/src/authorization/cedar_authorizer.rs b/src/authorization/cedar_authorizer.rs
index 432102ef..64287414 100644
--- a/src/authorization/cedar_authorizer.rs
+++ b/src/authorization/cedar_authorizer.rs
@@ -1,45 +1,48 @@
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,
+ authorizer: cedar_policy::Authorizer,
+ entities: cedar_policy::Entities,
+ policies: cedar_policy::PolicySet,
}
impl CedarAuthorizer {
- pub fn new(policies: cedar_policy::PolicySet) -> CedarAuthorizer {
+ pub fn new(
+ policies: cedar_policy::PolicySet,
+ entities: cedar_policy::Entities,
+ ) -> CedarAuthorizer {
CedarAuthorizer {
policies,
- authorizer: CedarAuth::new(),
+ entities,
+ authorizer: cedar_policy::Authorizer::new(),
}
}
- pub fn new_from(path: &std::path::Path) -> CedarAuthorizer {
- Self::new(Self::load_from(path).unwrap_or_else(|_| PolicySet::default()))
+ pub fn new_from(path: &std::path::Path, entities: cedar_policy::Entities) -> CedarAuthorizer {
+ Self::new(
+ Self::load_from(path).unwrap_or_else(|_| cedar_policy::PolicySet::default()),
+ entities,
+ )
}
- fn load_from(path: &std::path::Path) -> Result<PolicySet, Box<dyn std::error::Error>> {
+ fn load_from(
+ path: &std::path::Path,
+ ) -> Result<cedar_policy::PolicySet, Box<dyn std::error::Error>> {
if !path.exists() || !path.is_dir() {
- return Ok(PolicySet::default());
+ return Ok(cedar_policy::PolicySet::default());
}
- let mut policies = PolicySet::new();
-
+ let mut policies = cedar_policy::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)?;
+ let file_policies = cedar_policy::PolicySet::from_str(&content)?;
for policy in file_policies.policies() {
policies.add(policy.clone())?;
@@ -50,16 +53,96 @@ impl CedarAuthorizer {
Ok(policies)
}
+
+ fn map_from(
+ &self,
+ http_request: envoy_types::pb::envoy::service::auth::v3::attribute_context::HttpRequest,
+ ) -> Result<cedar_policy::Request, Box<dyn std::error::Error>> {
+ let principal = self.principal_from(&http_request)?;
+ let permission = self.permission_from(&http_request)?;
+ let resource = self.resource_from(&http_request)?;
+ let context = self.context_from(http_request)?;
+
+ Ok(cedar_policy::Request::new(
+ principal, permission, resource, context, None,
+ )?)
+ }
+
+ fn principal_from(
+ &self,
+ _http_request: &envoy_types::pb::envoy::service::auth::v3::attribute_context::HttpRequest,
+ ) -> Result<cedar_policy::EntityUid, Box<dyn std::error::Error>> {
+ Ok(cedar_policy::EntityUid::from_type_name_and_id(
+ cedar_policy::EntityTypeName::from_str("User")?,
+ cedar_policy::EntityId::from_str("client")?,
+ ))
+ }
+
+ fn permission_from(
+ &self,
+ _http_request: &envoy_types::pb::envoy::service::auth::v3::attribute_context::HttpRequest,
+ ) -> Result<cedar_policy::EntityUid, Box<dyn std::error::Error>> {
+ Ok(cedar_policy::EntityUid::from_type_name_and_id(
+ cedar_policy::EntityTypeName::from_str("Action")?,
+ cedar_policy::EntityId::from_str("check")?,
+ ))
+ }
+
+ fn resource_from(
+ &self,
+ _http_request: &envoy_types::pb::envoy::service::auth::v3::attribute_context::HttpRequest,
+ ) -> Result<cedar_policy::EntityUid, Box<dyn std::error::Error>> {
+ Ok(cedar_policy::EntityUid::from_type_name_and_id(
+ cedar_policy::EntityTypeName::from_str("Resource")?,
+ cedar_policy::EntityId::from_str("resource")?,
+ ))
+ }
+
+ fn context_from(
+ &self,
+ http_request: envoy_types::pb::envoy::service::auth::v3::attribute_context::HttpRequest,
+ ) -> Result<cedar_policy::Context, Box<dyn std::error::Error>> {
+ let mut items = std::collections::HashMap::new();
+
+ items.insert("bearer_token".to_string(), self.token_from(&http_request));
+ items.insert("host".to_string(), self.safe_string(&http_request.host));
+ items.insert("method".to_string(), self.safe_string(&http_request.method));
+ items.insert("path".to_string(), self.safe_string(&http_request.path));
+
+ Ok(cedar_policy::Context::from_pairs(
+ items.into_iter().collect::<Vec<_>>(),
+ )?)
+ }
+
+ fn token_from(
+ &self,
+ http_request: &envoy_types::pb::envoy::service::auth::v3::attribute_context::HttpRequest,
+ ) -> cedar_policy::RestrictedExpression {
+ let bearer_token = &http_request
+ .headers
+ .get("authorization")
+ .and_then(|auth| auth.strip_prefix("Bearer "))
+ .unwrap_or("");
+
+ self.safe_string(bearer_token)
+ }
+
+ fn safe_string(&self, item: &str) -> cedar_policy::RestrictedExpression {
+ cedar_policy::RestrictedExpression::new_string(item.to_string())
+ }
}
impl Default for CedarAuthorizer {
fn default() -> Self {
- Self::new_from(std::path::Path::new("/etc/authzd"))
+ Self::new_from(
+ std::path::Path::new("/etc/authzd"),
+ cedar_policy::Entities::empty(),
+ )
}
}
impl Authorizer for CedarAuthorizer {
- fn authorize(&self, request: CheckRequest) -> bool {
+ fn authorize(&self, request: envoy_types::ext_authz::v3::pb::CheckRequest) -> bool {
let http_request = match request
.attributes
.as_ref()
@@ -70,37 +153,26 @@ impl Authorizer for CedarAuthorizer {
None => return false,
};
- tracing::info!(
- method = %http_request.method,
- host = %http_request.host,
- path = %http_request.path,
- scheme = %http_request.scheme,
- protocol = %http_request.protocol,
- "Processing HTTP request"
- );
-
- if http_request.host == "sparkle.staging.runway.gitlab.net"
- && http_request.method == "GET"
- && http_request.path == "/"
- {
- return true;
- }
-
- let headers = &http_request.headers;
-
- let bearer_token = headers
- .get("authorization")
- .and_then(|auth| auth.strip_prefix("Bearer "))
- .unwrap_or("");
-
- match self.create_cedar_request(bearer_token, &http_request.path.to_string()) {
+ match self.map_from(http_request.clone()) {
Ok(cedar_request) => {
- let entities = Entities::empty();
let response =
self.authorizer
- .is_authorized(&cedar_request, &self.policies, &entities);
+ .is_authorized(&cedar_request, &self.policies, &self.entities);
+
+ let decision = response.decision();
- matches!(response.decision(), cedar_policy::Decision::Allow)
+ tracing::info!(
+ method = %http_request.method,
+ host = %http_request.host,
+ path = %http_request.path,
+ scheme = %http_request.scheme,
+ protocol = %http_request.protocol,
+ decision = ?decision,
+ diagnostics = ?response.diagnostics(),
+ "Processing HTTP request"
+ );
+
+ matches!(decision, cedar_policy::Decision::Allow)
}
Err(e) => {
tracing::error!(
@@ -113,46 +185,3 @@ impl Authorizer for CedarAuthorizer {
}
}
}
-
-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>)
- }
-}