use std::collections::HashMap;
use cedar_policy::{Context, Entities, EntityUid};
use crate::authz::{
AuthzEntityProvider, AuthzError,
store::{AuthzDecision, PolicyEvaluator},
};
pub struct MockPolicyEvaluator {
decisions: HashMap<(String, String), AuthzDecision>,
default: AuthzDecision,
}
impl MockPolicyEvaluator {
pub fn new() -> Self {
Self {
decisions: HashMap::new(),
default: AuthzDecision::Deny,
}
}
pub fn allow_all() -> Self {
Self {
decisions: HashMap::new(),
default: AuthzDecision::Allow,
}
}
pub fn permit(mut self, action_uid: &str, resource_uid: &str) -> Self {
self.decisions.insert(
(action_uid.to_string(), resource_uid.to_string()),
AuthzDecision::Allow,
);
self
}
pub fn deny(mut self, action_uid: &str, resource_uid: &str) -> Self {
self.decisions.insert(
(action_uid.to_string(), resource_uid.to_string()),
AuthzDecision::Deny,
);
self
}
pub fn permit_ns(
mut self,
namespace: &str,
action: &str,
resource_type: &str,
resource_id: &str,
) -> Self {
self.decisions.insert(
(
format!(r#"{namespace}::Action::"{action}""#),
format!(r#"{namespace}::{resource_type}::"{resource_id}""#),
),
AuthzDecision::Allow,
);
self
}
pub fn deny_ns(
mut self,
namespace: &str,
action: &str,
resource_type: &str,
resource_id: &str,
) -> Self {
self.decisions.insert(
(
format!(r#"{namespace}::Action::"{action}""#),
format!(r#"{namespace}::{resource_type}::"{resource_id}""#),
),
AuthzDecision::Deny,
);
self
}
}
impl Default for MockPolicyEvaluator {
fn default() -> Self {
Self::new()
}
}
impl PolicyEvaluator for MockPolicyEvaluator {
fn is_authorized(
&self,
entities: &Entities,
principal: EntityUid,
action: EntityUid,
resource: EntityUid,
context: Context,
) -> AuthzDecision {
tracing::trace!(
target: "axess::testing::mock_policy",
?entities,
?principal,
?context,
"MockPolicyEvaluator: dispatching on (action, resource) only",
);
let key = (action.to_string(), resource.to_string());
self.decisions.get(&key).copied().unwrap_or(self.default)
}
}
pub struct MockEntityProvider {
namespace: String,
}
impl MockEntityProvider {
pub fn new(namespace: impl Into<String>) -> Self {
Self {
namespace: namespace.into(),
}
}
}
impl AuthzEntityProvider for MockEntityProvider {
type ResourceId = String;
type Error = AuthzError;
async fn entities_for(
&self,
principal: &EntityUid,
resource_id: &String,
action: &EntityUid,
) -> Result<Entities, AuthzError> {
tracing::trace!(
target: "axess::testing::mock_provider",
?principal,
%resource_id,
?action,
"MockEntityProvider: returning empty entity set",
);
Ok(Entities::empty())
}
fn resource_uid(&self, id: &String) -> Result<EntityUid, AuthzError> {
use std::str::FromStr;
EntityUid::from_str(&format!(r#"{}::Resource::"{id}""#, self.namespace))
.map_err(|e| AuthzError::InvalidEntityUid(format!("Resource/{id}: {e:?}")))
}
}