holochain_integrity_types/capability/
grant.rs1use super::CapSecret;
2use crate::zome::FunctionName;
3use crate::zome::ZomeName;
4use holo_hash::*;
5use serde::Deserialize;
6use serde::Serialize;
7use std::collections::BTreeSet;
8
9#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
19#[allow(clippy::large_enum_variant)]
20pub enum CapGrant {
21 ChainAuthor(AgentPubKey),
25
26 RemoteAgent(ZomeCallCapGrant),
31}
32
33impl From<holo_hash::AgentPubKey> for CapGrant {
34 fn from(agent_hash: holo_hash::AgentPubKey) -> Self {
35 CapGrant::ChainAuthor(agent_hash)
36 }
37}
38
39#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
44pub struct ZomeCallCapGrant {
45 pub tag: String,
48 pub access: CapAccess,
50 pub functions: GrantedFunctions,
52 }
55
56#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
59pub struct DesensitizedZomeCallCapGrant {
60 pub tag: String,
63 pub access: CapAccessInfo,
65 pub functions: GrantedFunctions,
67}
68
69impl From<ZomeCallCapGrant> for DesensitizedZomeCallCapGrant {
70 fn from(zccg: ZomeCallCapGrant) -> Self {
72 DesensitizedZomeCallCapGrant {
73 tag: zccg.tag,
74 access: CapAccessInfo {
75 access_type: zccg.access.as_variant_string().to_string(),
76 assignees: match &zccg.access {
77 CapAccess::Assigned { assignees, .. } => Some(assignees.clone()),
78 _ => None,
79 },
80 },
81 functions: zccg.functions,
82 }
83 }
84}
85
86impl ZomeCallCapGrant {
87 pub fn new(
89 tag: String,
90 access: CapAccess,
91 functions: GrantedFunctions,
92 ) -> Self {
94 Self {
95 tag,
96 access,
97 functions,
98 }
100 }
101}
102
103impl From<ZomeCallCapGrant> for CapGrant {
104 fn from(zccg: ZomeCallCapGrant) -> Self {
106 CapGrant::RemoteAgent(zccg)
107 }
108}
109
110impl CapGrant {
111 pub fn is_valid(
115 &self,
116 given_function: &GrantedFunction,
117 given_agent: &AgentPubKey,
118 given_secret: Option<&CapSecret>,
119 ) -> bool {
120 match self {
121 CapGrant::ChainAuthor(author) => author == given_agent,
123 CapGrant::RemoteAgent(ZomeCallCapGrant {
125 access, functions, ..
126 }) => {
127 let granted = match functions {
129 GrantedFunctions::All => true,
130 GrantedFunctions::Listed(fns) => fns.contains(given_function),
131 };
132 granted
133 && match access {
135 CapAccess::Assigned { assignees, .. } => assignees.contains(given_agent),
137 _ => true,
139 }
140 && match access {
142 CapAccess::Unrestricted => true,
144 CapAccess::Transferable { secret, .. } => given_secret.map(|given| secret == given).unwrap_or(false),
146 CapAccess::Assigned { secret, .. } => given_secret.map(|given| secret == given).unwrap_or(false),
147 }
148 }
149 }
150 }
151}
152
153#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
155#[serde(tag = "type", content = "value", rename_all = "snake_case")]
156pub enum CapAccess {
157 Unrestricted,
159 Transferable {
161 secret: CapSecret,
163 },
164 Assigned {
166 secret: CapSecret,
168 assignees: BTreeSet<AgentPubKey>,
170 },
171}
172
173impl From<()> for CapAccess {
175 fn from(_: ()) -> Self {
176 Self::Unrestricted
177 }
178}
179
180impl From<CapSecret> for CapAccess {
182 fn from(secret: CapSecret) -> Self {
183 Self::Transferable { secret }
184 }
185}
186
187impl From<(CapSecret, BTreeSet<AgentPubKey>)> for CapAccess {
189 fn from((secret, assignees): (CapSecret, BTreeSet<AgentPubKey>)) -> Self {
190 Self::Assigned { secret, assignees }
191 }
192}
193
194impl From<(CapSecret, AgentPubKey)> for CapAccess {
197 fn from((secret, assignee): (CapSecret, AgentPubKey)) -> Self {
198 let mut assignees = BTreeSet::new();
199 assignees.insert(assignee);
200 Self::from((secret, assignees))
201 }
202}
203
204impl CapAccess {
205 pub fn as_variant_string(&self) -> &str {
207 match self {
208 CapAccess::Unrestricted => "unrestricted",
209 CapAccess::Transferable { .. } => "transferable",
210 CapAccess::Assigned { .. } => "assigned",
211 }
212 }
213}
214
215#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
217pub struct CapAccessInfo {
218 access_type: String,
220 assignees: Option<BTreeSet<AgentPubKey>>,
222}
223
224pub type GrantedFunction = (ZomeName, FunctionName);
226#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
229#[serde(tag = "type", content = "value", rename_all = "snake_case")]
230pub enum GrantedFunctions {
231 All,
233 Listed(BTreeSet<GrantedFunction>),
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn cap_grant_is_valid() {
243 let agent1 = AgentPubKey::from_raw_36(vec![1; 36]);
244 let agent2 = AgentPubKey::from_raw_36(vec![2; 36]);
245 let assignees: BTreeSet<_> = [agent1.clone()].into_iter().collect();
246 let secret: CapSecret = [1; 64].into();
247 let secret_wrong: CapSecret = [2; 64].into();
248 let tag = "tag".to_string();
249
250 let g1: CapGrant = ZomeCallCapGrant {
251 tag: tag.clone(),
252 access: CapAccess::Transferable { secret },
253 functions: GrantedFunctions::All,
254 }
255 .into();
256
257 let g2: CapGrant = ZomeCallCapGrant {
258 tag: tag.clone(),
259 access: CapAccess::Assigned {
260 secret,
261 assignees: assignees.clone(),
262 },
263 functions: GrantedFunctions::All,
264 }
265 .into();
266
267 assert!(g1.is_valid(
268 &(ZomeName("zome".into()), FunctionName("fn".into())),
269 &agent1,
270 Some(&secret),
271 ));
272
273 assert!(g1.is_valid(
274 &(ZomeName("zome".into()), FunctionName("fn".into())),
275 &agent2,
276 Some(&secret),
277 ));
278
279 assert!(!g1.is_valid(
280 &(ZomeName("zome".into()), FunctionName("fn".into())),
281 &agent1,
282 Some(&secret_wrong),
283 ));
284
285 assert!(!g1.is_valid(
286 &(ZomeName("zome".into()), FunctionName("fn".into())),
287 &agent1,
288 None,
289 ));
290
291 assert!(g2.is_valid(
292 &(ZomeName("zome".into()), FunctionName("fn".into())),
293 &agent1,
294 Some(&secret),
295 ));
296
297 assert!(!g2.is_valid(
298 &(ZomeName("zome".into()), FunctionName("fn".into())),
299 &agent2,
300 Some(&secret),
301 ));
302
303 assert!(!g2.is_valid(
304 &(ZomeName("zome".into()), FunctionName("fn".into())),
305 &agent1,
306 None,
307 ));
308
309 assert!(!g2.is_valid(
310 &(ZomeName("zome".into()), FunctionName("fn".into())),
311 &agent1,
312 Some(&secret_wrong),
313 ));
314 }
315}