spacedb_access/
capability.rs1use serde::{Deserialize, Serialize};
11
12use crate::error::{AccessError, AccessResult};
13use crate::identity::{Did, Identity};
14
15#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
17pub enum Scope {
18 Collection(String),
20 Document { collection: String, doc_id: String },
22 Function(String),
24}
25
26impl Scope {
27 pub fn covers(&self, requested: &Scope) -> bool {
31 match (self, requested) {
32 (Scope::Collection(c), Scope::Collection(rc)) => c == rc,
33 (Scope::Collection(c), Scope::Document { collection, .. }) => c == collection,
34 (
35 Scope::Document { collection, doc_id },
36 Scope::Document {
37 collection: rc,
38 doc_id: rd,
39 },
40 ) => collection == rc && doc_id == rd,
41 (Scope::Function(f), Scope::Function(rf)) => f == rf,
42 _ => false,
43 }
44 }
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
49pub struct Ops(u8);
50
51impl Ops {
52 pub const NONE: Ops = Ops(0);
53 pub const READ: Ops = Ops(1);
54 pub const WRITE: Ops = Ops(2);
55 pub const COMPUTE: Ops = Ops(4);
56
57 pub fn contains(self, needed: Ops) -> bool {
59 needed.0 != 0 && (self.0 & needed.0) == needed.0
60 }
61
62 pub fn is_empty(self) -> bool {
63 self.0 == 0
64 }
65
66 pub fn is_subset_of(self, other: Ops) -> bool {
69 (self.0 & other.0) == self.0
70 }
71}
72
73impl std::ops::BitOr for Ops {
74 type Output = Ops;
75 fn bitor(self, rhs: Ops) -> Ops {
76 Ops(self.0 | rhs.0)
77 }
78}
79
80#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
82pub struct Capability {
83 pub id: [u8; 16],
85 pub issuer: Did,
87 pub bearer: Did,
89 pub scope: Scope,
91 pub ops: Ops,
93 pub expiry: Option<u64>,
95 pub budget_micro_mata: Option<u64>,
97 pub delegation_depth: u8,
99}
100
101impl Capability {
102 pub fn grant(
105 issuer: impl Into<Did>,
106 bearer: impl Into<Did>,
107 scope: Scope,
108 ops: Ops,
109 ) -> AccessResult<Self> {
110 let mut id = [0u8; 16];
111 getrandom::fill(&mut id).map_err(|e| AccessError::KeyGen(e.to_string()))?;
112 Ok(Self {
113 id,
114 issuer: issuer.into(),
115 bearer: bearer.into(),
116 scope,
117 ops,
118 expiry: None,
119 budget_micro_mata: None,
120 delegation_depth: 0,
121 })
122 }
123
124 pub fn with_expiry(mut self, unix_seconds: u64) -> Self {
125 self.expiry = Some(unix_seconds);
126 self
127 }
128
129 pub fn with_budget(mut self, micro_mata: u64) -> Self {
130 self.budget_micro_mata = Some(micro_mata);
131 self
132 }
133
134 pub fn with_delegation_depth(mut self, depth: u8) -> Self {
135 self.delegation_depth = depth;
136 self
137 }
138
139 pub(crate) fn canonical_bytes(&self) -> AccessResult<Vec<u8>> {
141 postcard::to_allocvec(self).map_err(|e| AccessError::Canonical(e.to_string()))
142 }
143}
144
145#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
147pub struct SignedCapability {
148 pub capability: Capability,
149 pub issuer_signature: Vec<u8>,
151}
152
153impl SignedCapability {
154 pub fn sign(capability: Capability, issuer: &Identity) -> AccessResult<Self> {
158 let bytes = capability.canonical_bytes()?;
159 let issuer_signature = issuer.sign(&bytes);
160 Ok(Self {
161 capability,
162 issuer_signature,
163 })
164 }
165
166 pub fn encode(&self) -> AccessResult<Vec<u8>> {
168 postcard::to_allocvec(self).map_err(|e| AccessError::Canonical(e.to_string()))
169 }
170
171 pub fn decode(bytes: &[u8]) -> AccessResult<Self> {
173 postcard::from_bytes(bytes).map_err(|e| AccessError::Canonical(e.to_string()))
174 }
175}