1use crate::{
2 entity::Entity,
3 error::ValidationError,
4 node::{ContextData, Node, NodeInner},
5 property::PropertyError,
6 proto::{self},
7 storage::StorageEngine,
8};
9use ankql::{ast::Predicate, error::ParseError};
10use ankurah_proto::Attested;
11use async_trait::async_trait;
12use thiserror::Error;
13use tracing::debug;
14#[derive(Debug, Error)]
16pub enum AccessDenied {
17 #[error("Access denied by policy: {0}")]
18 ByPolicy(&'static str),
19 #[error("Access denied by collection: {0}")]
20 CollectionDenied(proto::CollectionId),
21 #[error("Access denied by property error: {0}")]
22 PropertyError(Box<PropertyError>),
23 #[error("Access denied by parse error: {0}")]
24 ParseError(ParseError),
25 #[error("Insufficient attestation")]
26 InsufficientAttestation,
27}
28
29impl From<PropertyError> for AccessDenied {
30 fn from(error: PropertyError) -> Self { AccessDenied::PropertyError(Box::new(error)) }
31}
32impl From<ParseError> for AccessDenied {
33 fn from(error: ParseError) -> Self { AccessDenied::ParseError(error) }
34}
35
36#[cfg(feature = "wasm")]
37impl From<AccessDenied> for wasm_bindgen::JsValue {
38 fn from(error: AccessDenied) -> Self { wasm_bindgen::JsValue::from_str(&error.to_string()) }
39}
40
41impl AccessDenied {}
42
43#[async_trait]
49pub trait PolicyAgent: Clone + Send + Sync + 'static {
50 type ContextData: ContextData;
53
54 fn sign_request<SE: StorageEngine>(
57 &self,
58 node: &NodeInner<SE, Self>,
59 cdata: &Self::ContextData,
60 request: &proto::NodeRequest,
61 ) -> proto::AuthData;
62
63 async fn check_request<SE: StorageEngine>(
68 &self,
69 node: &Node<SE, Self>,
70 auth: &proto::AuthData,
71 request: &proto::NodeRequest,
72 ) -> Result<Self::ContextData, ValidationError>
73 where
74 Self: Sized;
75
76 fn check_event<SE: StorageEngine>(
80 &self,
81 node: &Node<SE, Self>,
82 cdata: &Self::ContextData,
83 entity: &Entity,
84 event: &proto::Event,
85 ) -> Result<Option<proto::Attestation>, AccessDenied>;
86
87 fn validate_received_event<SE: StorageEngine>(
90 &self,
91 node: &Node<SE, Self>,
92 received_from_node: &proto::EntityId,
93 event: &Attested<proto::Event>,
94 ) -> Result<(), AccessDenied>;
95
96 fn attest_state<SE: StorageEngine>(&self, node: &Node<SE, Self>, state: &proto::EntityState) -> Option<proto::Attestation>;
98
99 fn validate_received_state<SE: StorageEngine>(
100 &self,
101 node: &Node<SE, Self>,
102 received_from_node: &proto::EntityId,
103 state: &Attested<proto::EntityState>,
104 ) -> Result<(), AccessDenied>;
105
106 fn can_access_collection(&self, data: &Self::ContextData, collection: &proto::CollectionId) -> Result<(), AccessDenied>;
109
110 fn filter_predicate(
112 &self,
113 data: &Self::ContextData,
114 collection: &proto::CollectionId,
115 predicate: Predicate,
116 ) -> Result<Predicate, AccessDenied>;
117
118 fn check_read(
123 &self,
124 data: &Self::ContextData,
125 id: &proto::EntityId,
126 collection: &proto::CollectionId,
127 state: &proto::State,
128 ) -> Result<(), AccessDenied>;
129
130 fn check_read_event(&self, data: &Self::ContextData, event: &Attested<proto::Event>) -> Result<(), AccessDenied>;
132
133 fn check_write(&self, data: &Self::ContextData, entity: &Entity, event: Option<&proto::Event>) -> Result<(), AccessDenied>;
135
136 }
144
145#[derive(Clone)]
147pub struct PermissiveAgent {}
148
149impl Default for PermissiveAgent {
150 fn default() -> Self { Self::new() }
151}
152
153impl PermissiveAgent {
154 pub fn new() -> Self { Self {} }
155}
156
157#[async_trait]
158impl PolicyAgent for PermissiveAgent {
159 type ContextData = &'static DefaultContext;
160
161 fn sign_request<SE: StorageEngine>(
163 &self,
164 _node: &NodeInner<SE, Self>,
165 _cdata: &Self::ContextData,
166 _request: &proto::NodeRequest,
167 ) -> proto::AuthData {
168 debug!("PermissiveAgent sign_request: {:?}", _request);
169 proto::AuthData(vec![])
170 }
171
172 async fn check_request<SE: StorageEngine>(
174 &self,
175 _node: &Node<SE, Self>,
176 _auth: &proto::AuthData,
177 _request: &proto::NodeRequest,
178 ) -> Result<Self::ContextData, ValidationError> {
179 Ok(DEFAULT_CONTEXT)
180 }
181
182 fn check_event<SE: StorageEngine>(
184 &self,
185 _node: &Node<SE, Self>,
186 _cdata: &Self::ContextData,
187 _entity: &Entity,
188 _event: &proto::Event,
189 ) -> Result<Option<proto::Attestation>, AccessDenied> {
190 Ok(None)
191 }
192
193 fn validate_received_event<SE: StorageEngine>(
194 &self,
195 _node: &Node<SE, Self>,
196 _from_node: &proto::EntityId,
197 _event: &proto::Attested<proto::Event>,
198 ) -> Result<(), AccessDenied> {
199 Ok(())
200 }
201
202 fn attest_state<SE: StorageEngine>(&self, _node: &Node<SE, Self>, _state: &proto::EntityState) -> Option<proto::Attestation> {
203 None
206 }
207
208 fn validate_received_state<SE: StorageEngine>(
209 &self,
210 _node: &Node<SE, Self>,
211 _from_node: &proto::EntityId,
212 _state: &Attested<proto::EntityState>,
213 ) -> Result<(), AccessDenied> {
214 Ok(())
217 }
218
219 fn can_access_collection(&self, _context: &Self::ContextData, _collection: &proto::CollectionId) -> Result<(), AccessDenied> { Ok(()) }
220
221 fn check_read(
222 &self,
223 _context: &Self::ContextData,
224 _id: &proto::EntityId,
225 _collection: &proto::CollectionId,
226 _state: &proto::State,
227 ) -> Result<(), AccessDenied> {
228 Ok(())
230 }
231
232 fn check_read_event(&self, _context: &Self::ContextData, _event: &Attested<proto::Event>) -> Result<(), AccessDenied> {
233 Ok(())
236 }
237
238 fn check_write(&self, _context: &Self::ContextData, _entity: &Entity, _event: Option<&proto::Event>) -> Result<(), AccessDenied> {
239 Ok(())
240 }
241
242 fn filter_predicate(
243 &self,
244 _context: &Self::ContextData,
245 _collection: &proto::CollectionId,
246 predicate: Predicate,
247 ) -> Result<Predicate, AccessDenied> {
248 Ok(predicate)
249 }
250
251 }
263
264pub struct DefaultContext {}
267pub static DEFAULT_CONTEXT: &DefaultContext = &DefaultContext {};
268
269impl Default for DefaultContext {
270 fn default() -> Self { Self::new() }
271}
272
273impl DefaultContext {
274 pub fn new() -> Self { Self {} }
275}
276
277#[async_trait]
278impl ContextData for &'static DefaultContext {}