pmcp_code_mode/policy/
mod.rs1pub mod types;
12
13#[cfg(feature = "cedar")]
14pub mod cedar;
15
16pub use types::*;
17
18#[derive(Debug, thiserror::Error)]
20pub enum PolicyEvaluationError {
21 #[error("Policy configuration error: {0}")]
22 ConfigError(String),
23
24 #[error("Policy evaluation error: {0}")]
25 EvaluationError(String),
26
27 #[error("Authorization denied: {0}")]
28 Denied(String),
29}
30
31#[async_trait::async_trait]
38pub trait PolicyEvaluator: Send + Sync {
39 async fn evaluate_operation(
41 &self,
42 operation: &OperationEntity,
43 server_config: &ServerConfigEntity,
44 ) -> Result<AuthorizationDecision, PolicyEvaluationError>;
45
46 #[cfg(feature = "openapi-code-mode")]
49 async fn evaluate_script(
50 &self,
51 _script: &ScriptEntity,
52 _server: &OpenAPIServerEntity,
53 ) -> Result<AuthorizationDecision, PolicyEvaluationError> {
54 Ok(AuthorizationDecision {
55 allowed: false,
56 determining_policies: vec!["default_deny_scripts".to_string()],
57 errors: vec!["Script evaluation not supported by this policy evaluator".to_string()],
58 })
59 }
60
61 async fn batch_evaluate(
63 &self,
64 requests: Vec<(OperationEntity, ServerConfigEntity)>,
65 ) -> Result<Vec<AuthorizationDecision>, PolicyEvaluationError> {
66 let mut results = Vec::with_capacity(requests.len());
67 for (op, config) in &requests {
68 results.push(self.evaluate_operation(op, config).await?);
69 }
70 Ok(results)
71 }
72
73 fn is_configured(&self) -> bool {
75 true
76 }
77
78 fn name(&self) -> &str;
80}
81
82pub struct NoopPolicyEvaluator;
103
104impl NoopPolicyEvaluator {
105 pub fn new() -> Self {
110 Self
111 }
112}
113
114impl Default for NoopPolicyEvaluator {
115 fn default() -> Self {
116 Self::new()
117 }
118}
119
120#[async_trait::async_trait]
121impl PolicyEvaluator for NoopPolicyEvaluator {
122 async fn evaluate_operation(
123 &self,
124 _operation: &OperationEntity,
125 _server_config: &ServerConfigEntity,
126 ) -> Result<AuthorizationDecision, PolicyEvaluationError> {
127 Ok(AuthorizationDecision {
128 allowed: true,
129 determining_policies: vec!["noop_allow_all".to_string()],
130 errors: vec![],
131 })
132 }
133
134 #[cfg(feature = "openapi-code-mode")]
135 async fn evaluate_script(
136 &self,
137 _script: &ScriptEntity,
138 _server: &OpenAPIServerEntity,
139 ) -> Result<AuthorizationDecision, PolicyEvaluationError> {
140 Ok(AuthorizationDecision {
141 allowed: true,
142 determining_policies: vec!["noop_allow_all_scripts".to_string()],
143 errors: vec![],
144 })
145 }
146
147 fn name(&self) -> &str {
148 "noop"
149 }
150}
151
152#[cfg(test)]
153mod noop_tests {
154 use super::*;
155
156 #[tokio::test]
157 async fn noop_evaluator_allows_all_operations() {
158 let evaluator = NoopPolicyEvaluator::new();
159 let operation = OperationEntity {
160 id: "test-op".to_string(),
161 operation_type: "query".to_string(),
162 operation_name: "GetUsers".to_string(),
163 root_fields: ["users"].iter().map(|s| s.to_string()).collect(),
164 accessed_types: ["User"].iter().map(|s| s.to_string()).collect(),
165 accessed_fields: ["User.id", "User.name"]
166 .iter()
167 .map(|s| s.to_string())
168 .collect(),
169 depth: 2,
170 field_count: 2,
171 estimated_cost: 2,
172 has_introspection: false,
173 accesses_sensitive_data: false,
174 sensitive_categories: std::collections::HashSet::new(),
175 };
176 let config = ServerConfigEntity::default();
177 let result = evaluator
178 .evaluate_operation(&operation, &config)
179 .await
180 .unwrap();
181 assert!(result.allowed);
182 assert_eq!(result.determining_policies, vec!["noop_allow_all"]);
183 }
184
185 #[test]
186 fn noop_evaluator_name() {
187 let evaluator = NoopPolicyEvaluator::new();
188 assert_eq!(evaluator.name(), "noop");
189 }
190
191 #[test]
192 fn noop_evaluator_default() {
193 let evaluator = NoopPolicyEvaluator::default();
194 assert_eq!(evaluator.name(), "noop");
195 }
196}