1use fcdb_core::{Cap, Cid};
8use std::sync::Arc;
9use tokio::sync::{RwLock, Mutex};
10use async_trait::async_trait;
11use thiserror::Error;
12use serde::{Serialize, Deserialize};
13
14#[derive(Clone, PartialEq, Eq)]
16pub struct CapCid {
17 pub cap: Cap,
18 pub cid: Cid,
19}
20
21impl CapCid {
22 pub fn new(cid: Cid, cap: Cap) -> Self {
23 Self { cap, cid }
24 }
25}
26
27#[derive(Error, Debug)]
29pub enum ConcurError {
30 #[error("Capability check failed")]
31 CapCheckFailed,
32 #[error("Ownership violation")]
33 OwnershipViolation,
34 #[error("Transaction conflict")]
35 TransactionConflict,
36 #[error("Lease expired")]
37 LeaseExpired,
38 #[error("Permission denied")]
39 PermissionDenied,
40}
41
42pub mod perms {
44 pub const READ: u32 = 1 << 0;
45 pub const WRITE: u32 = 1 << 1;
46 pub const EXECUTE: u32 = 1 << 2;
47 pub const DERIVE: u32 = 1 << 3;
48 pub const DELEGATE: u32 = 1 << 4;
49}
50
51pub struct OwnedCapCid<T> {
54 cap_cid: CapCid,
55 data: T,
56}
57
58impl<T> OwnedCapCid<T> {
59 pub fn new(data: T, cap: Cap, cid: Cid) -> Self {
61 Self {
62 cap_cid: CapCid::new(cid, cap),
63 data,
64 }
65 }
66
67 pub fn as_ref(&self) -> (&Cap, &T) {
69 (&self.cap_cid.cap, &self.data)
70 }
71
72 pub fn as_mut(&mut self) -> (&mut Cap, &mut T) {
74 (&mut self.cap_cid.cap, &mut self.data)
75 }
76
77 pub fn into_parts(self) -> (CapCid, T) {
79 (self.cap_cid, self.data)
80 }
81}
82
83pub struct BorrowCapCid<'a, T> {
86 cap_cid: &'a CapCid,
87 data: &'a T,
88}
89
90impl<'a, T> BorrowCapCid<'a, T> {
91 pub fn new(cap_cid: &'a CapCid, data: &'a T) -> Self {
92 Self { cap_cid, data }
93 }
94
95 pub fn cap(&self) -> &Cap {
96 &self.cap_cid.cap
97 }
98
99 pub fn cid(&self) -> &Cid {
100 &self.cap_cid.cid
101 }
102
103 pub fn data(&self) -> &T {
104 self.data
105 }
106}
107
108pub struct BorrowMutCapCid<'a, T> {
111 cap_cid: &'a mut CapCid,
112 data: &'a mut T,
113}
114
115impl<'a, T> BorrowMutCapCid<'a, T> {
116 pub fn new(cap_cid: &'a mut CapCid, data: &'a mut T) -> Self {
117 Self { cap_cid, data }
118 }
119
120 pub fn cap(&self) -> &Cap {
121 &self.cap_cid.cap
122 }
123
124 pub fn cap_mut(&mut self) -> &mut Cap {
125 &mut self.cap_cid.cap
126 }
127
128 pub fn cid(&self) -> &Cid {
129 &self.cap_cid.cid
130 }
131
132 pub fn data(&self) -> &T {
133 self.data
134 }
135
136 pub fn data_mut(&mut self) -> &mut T {
137 self.data
138 }
139}
140
141pub trait CapFunctor {
144 type Target<U>;
145 type Data;
146
147 fn cap_map<U, F>(self, f: F) -> Self::Target<U>
149 where
150 F: FnOnce(Self::Data) -> U;
151
152 fn cap_flat_map<U, F>(self, f: F) -> Self::Target<U>
154 where
155 F: FnOnce(Self::Data) -> Self::Target<U>;
156}
157
158impl<T> CapFunctor for OwnedCapCid<T> {
159 type Target<U> = OwnedCapCid<U>;
160 type Data = T;
161
162 fn cap_map<U, F>(self, f: F) -> Self::Target<U>
163 where
164 F: FnOnce(Self::Data) -> U,
165 {
166 let (cap_cid, data) = self.into_parts();
167 OwnedCapCid::new(f(data), cap_cid.cap, cap_cid.cid)
168 }
169
170 fn cap_flat_map<U, F>(self, f: F) -> Self::Target<U>
171 where
172 F: FnOnce(Self::Data) -> Self::Target<U>,
173 {
174 let (cap_cid, data) = self.into_parts();
175 let OwnedCapCid { cap_cid: new_cap_cid, data: new_data } = f(data);
176
177 let composed_cap = Cap {
179 base: new_cap_cid.cap.base.max(cap_cid.cap.base),
180 len: new_cap_cid.cap.len.min(cap_cid.cap.len),
181 perms: new_cap_cid.cap.perms & cap_cid.cap.perms,
182 proof: new_cap_cid.cap.proof, };
184
185 OwnedCapCid::new(new_data, composed_cap, new_cap_cid.cid)
186 }
187}
188
189pub struct Transaction {
191 id: u64,
192 owned_resources: Vec<OwnedCapCid<Box<dyn std::any::Any + Send + Sync>>>,
193 borrowed_resources: Vec<Arc<RwLock<CapCid>>>,
194 start_time: std::time::Instant,
195 timeout_ms: u64,
196}
197
198impl Transaction {
199 pub fn new(id: u64) -> Self {
200 Self {
201 id,
202 owned_resources: Vec::new(),
203 borrowed_resources: Vec::new(),
204 start_time: std::time::Instant::now(),
205 timeout_ms: 5000, }
207 }
208
209 pub fn is_expired(&self) -> bool {
211 self.start_time.elapsed().as_millis() as u64 > self.timeout_ms
212 }
213
214 pub fn add_owned<T: Send + Sync + 'static>(&mut self, owned: OwnedCapCid<T>) {
216 let boxed = OwnedCapCid::new(
217 Box::new(owned.data) as Box<dyn std::any::Any + Send + Sync>,
218 owned.cap_cid.cap,
219 owned.cap_cid.cid
220 );
221 self.owned_resources.push(boxed);
222 }
223
224 pub fn add_borrowed(&mut self, borrowed: Arc<RwLock<CapCid>>) {
226 self.borrowed_resources.push(borrowed);
227 }
228
229 pub async fn check_write_perm(&self, target_cid: &Cid) -> Result<(), ConcurError> {
231 for owned in &self.owned_resources {
233 if owned.cap_cid.cid == *target_cid {
234 if owned.cap_cid.cap.has_perm(perms::WRITE) {
235 return Ok(());
236 } else {
237 return Err(ConcurError::PermissionDenied);
238 }
239 }
240 }
241
242 for borrowed in &self.borrowed_resources {
244 let cap_cid = borrowed.read().await;
245 if cap_cid.cid == *target_cid {
246 if cap_cid.cap.has_perm(perms::WRITE) {
247 return Ok(());
248 } else {
249 return Err(ConcurError::PermissionDenied);
250 }
251 }
252 }
253
254 Err(ConcurError::PermissionDenied)
255 }
256}
257
258pub struct LeaseManager {
260 active_leases: Arc<RwLock<std::collections::HashMap<u64, LeaseInfo>>>,
261}
262
263#[derive(Clone)]
264pub struct LeaseInfo {
265 pub resource_id: u64,
266 pub holder: String,
267 pub permissions: u32,
268 pub expires_at: u64,
269 pub auto_renew: bool,
270}
271
272impl LeaseManager {
273 pub fn new() -> Self {
274 Self {
275 active_leases: Arc::new(RwLock::new(std::collections::HashMap::new())),
276 }
277 }
278
279 pub async fn grant_lease(&self, lease_id: u64, info: LeaseInfo) -> Result<(), ConcurError> {
281 let mut leases = self.active_leases.write().await;
282 leases.insert(lease_id, info);
283 Ok(())
284 }
285
286 pub async fn check_lease(&self, lease_id: u64) -> Result<LeaseInfo, ConcurError> {
288 let leases = self.active_leases.read().await;
289 match leases.get(&lease_id) {
290 Some(info) => {
291 let now = std::time::SystemTime::now()
292 .duration_since(std::time::UNIX_EPOCH)
293 .unwrap()
294 .as_secs();
295
296 if now > info.expires_at {
297 return Err(ConcurError::LeaseExpired);
298 }
299
300 Ok(info.clone())
301 }
302 None => Err(ConcurError::LeaseExpired),
303 }
304 }
305
306 pub async fn revoke_lease(&self, lease_id: u64) -> Result<(), ConcurError> {
308 let mut leases = self.active_leases.write().await;
309 leases.remove(&lease_id);
310 Ok(())
311 }
312
313 pub async fn renew_lease(&self, lease_id: u64, new_expiry: u64) -> Result<(), ConcurError> {
315 let mut leases = self.active_leases.write().await;
316 if let Some(info) = leases.get_mut(&lease_id) {
317 if info.auto_renew {
318 info.expires_at = new_expiry;
319 Ok(())
320 } else {
321 Err(ConcurError::PermissionDenied)
322 }
323 } else {
324 Err(ConcurError::LeaseExpired)
325 }
326 }
327}
328
329pub struct ResourceManager {
331 resources: Arc<RwLock<std::collections::HashMap<Cid, Arc<RwLock<CapCid>>>>>,
332 lease_manager: LeaseManager,
333 next_txn_id: Arc<Mutex<u64>>,
334}
335
336impl ResourceManager {
337 pub fn new() -> Self {
338 Self {
339 resources: Arc::new(RwLock::new(std::collections::HashMap::new())),
340 lease_manager: LeaseManager::new(),
341 next_txn_id: Arc::new(Mutex::new(1)),
342 }
343 }
344
345 pub async fn begin_transaction(&self) -> Result<Transaction, ConcurError> {
347 let mut next_id = self.next_txn_id.lock().await;
348 let txn_id = *next_id;
349 *next_id += 1;
350
351 Ok(Transaction::new(txn_id))
352 }
353
354 pub async fn register_resource(&self, cid: Cid, cap: Cap) -> Result<(), ConcurError> {
356 let cap_cid = CapCid::new(cid, cap);
357 let mut resources = self.resources.write().await;
358 resources.insert(cid, Arc::new(RwLock::new(cap_cid)));
359 Ok(())
360 }
361
362 pub async fn acquire_exclusive(&self, cid: &Cid, txn: &mut Transaction) -> Result<(), ConcurError> {
364 let resources = self.resources.read().await;
365 if let Some(resource) = resources.get(cid) {
366 txn.check_write_perm(cid).await?;
367 txn.add_borrowed(resource.clone());
368 Ok(())
369 } else {
370 Err(ConcurError::OwnershipViolation)
371 }
372 }
373
374 pub async fn acquire_shared(&self, cid: &Cid, txn: &mut Transaction) -> Result<(), ConcurError> {
376 let resources = self.resources.read().await;
377 if let Some(resource) = resources.get(cid) {
378 txn.add_borrowed(resource.clone());
379 Ok(())
380 } else {
381 Err(ConcurError::OwnershipViolation)
382 }
383 }
384
385 pub async fn commit_transaction(&self, txn: Transaction) -> Result<(), ConcurError> {
387 if txn.is_expired() {
388 return Err(ConcurError::TransactionConflict);
389 }
390
391 for borrowed in &txn.borrowed_resources {
393 let cap_cid = borrowed.read().await;
394 }
396
397 Ok(())
399 }
400
401 pub async fn abort_transaction(&self, txn: Transaction) -> Result<(), ConcurError> {
403 Ok(())
406 }
407}
408
409pub struct CapTracer {
411 trace_log: Arc<RwLock<Vec<CapTraceEntry>>>,
412}
413
414#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
415pub struct CapTraceEntry {
416 pub timestamp: u64,
417 pub operation: String,
418 pub actor: String,
419 pub resource: Cid,
420 pub capability: Cap,
421 pub success: bool,
422 pub details: String,
423}
424
425impl CapTracer {
426 pub fn new() -> Self {
427 Self {
428 trace_log: Arc::new(RwLock::new(Vec::new())),
429 }
430 }
431
432 pub async fn record_operation(
434 &self,
435 operation: &str,
436 actor: &str,
437 resource: &Cid,
438 capability: &Cap,
439 success: bool,
440 details: &str,
441 ) {
442 let entry = CapTraceEntry {
443 timestamp: std::time::SystemTime::now()
444 .duration_since(std::time::UNIX_EPOCH)
445 .unwrap()
446 .as_secs(),
447 operation: operation.to_string(),
448 actor: actor.to_string(),
449 resource: *resource,
450 capability: *capability,
451 success,
452 details: details.to_string(),
453 };
454
455 let mut log = self.trace_log.write().await;
456 log.push(entry);
457
458 if log.len() > 1000 {
460 log.remove(0);
461 }
462 }
463
464 pub async fn get_audit_trail(&self, resource: &Cid) -> Vec<CapTraceEntry> {
466 let log = self.trace_log.read().await;
467 log.iter()
468 .filter(|entry| entry.resource == *resource)
469 .cloned()
470 .collect()
471 }
472
473 pub async fn get_actor_operations(&self, actor: &str) -> Vec<CapTraceEntry> {
475 let log = self.trace_log.read().await;
476 log.iter()
477 .filter(|entry| entry.actor == actor)
478 .cloned()
479 .collect()
480 }
481}
482
483pub struct SafeExecutor {
485 resource_manager: ResourceManager,
486 tracer: CapTracer,
487}
488
489impl SafeExecutor {
490 pub fn new() -> Self {
491 Self {
492 resource_manager: ResourceManager::new(),
493 tracer: CapTracer::new(),
494 }
495 }
496
497 pub async fn execute_safe<F, Fut, T>(
499 &self,
500 actor: &str,
501 operation: &str,
502 resource: &Cid,
503 cap_check: F,
504 ) -> Result<T, ConcurError>
505 where
506 F: FnOnce() -> Fut,
507 Fut: std::future::Future<Output = Result<T, ConcurError>>,
508 {
509 let mut txn = self.resource_manager.begin_transaction().await?;
511 self.resource_manager.acquire_shared(resource, &mut txn).await?;
512
513 let cap_cid = {
514 let resources = self.resource_manager.resources.read().await;
515 let temp = resources.get(resource)
516 .ok_or(ConcurError::OwnershipViolation)?
517 .read().await
518 .clone();
519 temp
520 };
521
522 let result = cap_check().await;
524
525 let success = result.is_ok();
527 let details = if success { "success" } else { "failed" };
528 self.tracer.record_operation(
529 operation,
530 actor,
531 resource,
532 &cap_cid.cap,
533 success,
534 details,
535 ).await;
536
537 match result {
539 Ok(value) => {
540 self.resource_manager.commit_transaction(txn).await?;
541 Ok(value)
542 }
543 Err(e) => {
544 self.resource_manager.abort_transaction(txn).await?;
545 Err(e)
546 }
547 }
548 }
549}
550
551#[cfg(test)]
552mod tests {
553 use super::*;
554
555 #[tokio::test]
556 async fn test_owned_cap_cid() {
557 let data = "test data".to_string();
558 let cap = Cap::new(0, 100, perms::READ | perms::WRITE);
559 let cid = Cid::hash(data.as_bytes());
560
561 let owned = OwnedCapCid::new(data, cap, cid);
562
563 {
565 let (cap_ref, data_ref) = owned.as_ref();
566 assert!(cap_ref.has_perm(perms::READ));
567 assert_eq!(data_ref, "test data");
568 }
569
570 let mut owned = owned;
572 {
573 let (cap_mut, data_mut) = owned.as_mut();
574 *data_mut = "modified".to_string();
575 cap_mut.perms &= !perms::WRITE; }
577
578 let (final_cap, final_data) = owned.as_ref();
580 assert_eq!(final_data, "modified");
581 assert!(!final_cap.has_perm(perms::WRITE));
582 }
583
584 #[tokio::test]
585 async fn test_capability_functor() {
586 let data = 42;
587 let cap = Cap::new(0, 100, perms::READ | perms::WRITE);
588 let cid = Cid::hash(&data.to_le_bytes());
589
590 let owned = OwnedCapCid::new(data, cap, cid);
591
592 let mapped = owned.cap_map(|x| x * 2);
594 let (_, result) = mapped.as_ref();
595 assert_eq!(*result, 84);
596 }
597
598 #[tokio::test]
599 async fn test_transaction_lifecycle() {
600 let rm = ResourceManager::new();
601 let cid = Cid::hash(b"test resource");
602 let cap = Cap::new(0, 100, perms::READ | perms::WRITE);
603
604 rm.register_resource(cid, cap).await.unwrap();
606
607 let mut txn = rm.begin_transaction().await.unwrap();
609
610 rm.acquire_exclusive(&cid, &mut txn).await.unwrap();
612
613 assert!(txn.check_write_perm(&cid).await.is_ok());
615
616 rm.commit_transaction(txn).await.unwrap();
618 }
619
620 #[tokio::test]
621 async fn test_lease_management() {
622 let lm = LeaseManager::new();
623 let lease_id = 12345;
624
625 let info = LeaseInfo {
626 resource_id: 1,
627 holder: "test_user".to_string(),
628 permissions: perms::READ | perms::WRITE,
629 expires_at: std::time::SystemTime::now()
630 .duration_since(std::time::UNIX_EPOCH)
631 .unwrap()
632 .as_secs() + 3600, auto_renew: true,
634 };
635
636 lm.grant_lease(lease_id, info.clone()).await.unwrap();
638
639 let checked = lm.check_lease(lease_id).await.unwrap();
641 assert_eq!(checked.holder, "test_user");
642
643 lm.revoke_lease(lease_id).await.unwrap();
645
646 assert!(lm.check_lease(lease_id).await.is_err());
648 }
649
650 #[tokio::test]
651 async fn test_capability_tracing() {
652 let tracer = CapTracer::new();
653 let cid = Cid::hash(b"test resource");
654 let cap = Cap::new(0, 100, perms::READ);
655
656 tracer.record_operation(
658 "read",
659 "alice",
660 &cid,
661 &cap,
662 true,
663 "successful read"
664 ).await;
665
666 tracer.record_operation(
667 "write",
668 "bob",
669 &cid,
670 &cap,
671 false,
672 "permission denied"
673 ).await;
674
675 let alice_ops = tracer.get_actor_operations("alice").await;
677 assert_eq!(alice_ops.len(), 1);
678 assert_eq!(alice_ops[0].operation, "read");
679 assert!(alice_ops[0].success);
680
681 let resource_trail = tracer.get_audit_trail(&cid).await;
682 assert_eq!(resource_trail.len(), 2);
683 }
684}