Skip to main content

junobuild_collections/assert/
stores.rs

1use crate::types::rules::Permission;
2use candid::Principal;
3use junobuild_shared::segments::controllers::controller_can_write;
4use junobuild_shared::types::state::{Controllers, UserId};
5use junobuild_shared::utils::{principal_not_anonymous, principal_not_anonymous_and_equal};
6
7pub fn assert_permission(
8    permission: &Permission,
9    owner: Principal,
10    caller: Principal,
11    controllers: &Controllers,
12) -> bool {
13    assert_permission_with(permission, owner, caller, controllers, controller_can_write)
14}
15
16pub fn assert_permission_with(
17    permission: &Permission,
18    owner: Principal,
19    caller: Principal,
20    controllers: &Controllers,
21    is_allowed_controller: fn(UserId, &Controllers) -> bool,
22) -> bool {
23    match permission {
24        Permission::Public => true,
25        Permission::Private => is_owner_and_valid(caller, owner, controllers),
26        Permission::Managed => {
27            // if owner, then it's either not a controller or a valid controller
28            // else if valid controller
29            is_owner_and_valid(caller, owner, controllers)
30                || controller_can_write(caller, controllers)
31        }
32        Permission::Controllers => is_allowed_controller(caller, controllers),
33    }
34}
35
36/// If a document or asset is about to be created for the first time, it can be initialized without further rules unless the collection is set as controller and the caller is not a controller.
37/// This can be useful e.g. when a collection read permission is set to public but only the administrator can add content.
38pub fn assert_create_permission(
39    permission: &Permission,
40    caller: Principal,
41    controllers: &Controllers,
42) -> bool {
43    assert_create_permission_with(permission, caller, controllers, controller_can_write)
44}
45
46pub fn assert_create_permission_with(
47    permission: &Permission,
48    caller: Principal,
49    controllers: &Controllers,
50    is_allowed_controller: fn(UserId, &Controllers) -> bool,
51) -> bool {
52    match permission {
53        Permission::Public => true,
54        Permission::Controllers => is_allowed_controller(caller, controllers),
55        _ => {
56            assert_not_anonymous(caller)
57                && (is_not_controller(caller, controllers)
58                    || controller_can_write(caller, controllers))
59        }
60    }
61}
62
63fn is_owner(caller: Principal, owner: Principal) -> bool {
64    principal_not_anonymous_and_equal(caller, owner)
65}
66
67fn assert_not_anonymous(caller: Principal) -> bool {
68    principal_not_anonymous(caller)
69}
70
71pub fn public_permission(permission: &Permission) -> bool {
72    matches!(permission, Permission::Public)
73}
74
75fn is_controller(caller: Principal, controllers: &Controllers) -> bool {
76    controllers.contains_key(&caller)
77}
78
79fn is_not_controller(caller: Principal, controllers: &Controllers) -> bool {
80    !is_controller(caller, controllers)
81}
82
83fn is_owner_and_valid(caller: Principal, owner: Principal, controllers: &Controllers) -> bool {
84    is_owner(caller, owner)
85        && (is_not_controller(caller, controllers) || controller_can_write(caller, controllers))
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use junobuild_shared::types::state::{Controller, ControllerScope, Controllers};
92    use std::collections::HashMap;
93
94    fn test_principal(id: u8) -> Principal {
95        Principal::from_slice(&[id])
96    }
97
98    fn create_controller(scope: ControllerScope, expires_at: Option<u64>) -> Controller {
99        Controller {
100            metadata: HashMap::new(),
101            created_at: 1000,
102            updated_at: 1000,
103            expires_at,
104            scope,
105            kind: None,
106        }
107    }
108
109    fn mock_time() -> u64 {
110        1_000_000_000_000
111    }
112
113    #[test]
114    fn test_is_owner() {
115        let owner = test_principal(1);
116        let caller = test_principal(1);
117        let other = test_principal(2);
118
119        assert!(is_owner(caller, owner));
120        assert!(!is_owner(other, owner));
121        assert!(!is_owner(Principal::anonymous(), owner));
122    }
123
124    #[test]
125    fn test_is_controller() {
126        let mut controllers = Controllers::new();
127        let controller_principal = test_principal(1);
128        let non_controller = test_principal(2);
129
130        controllers.insert(
131            controller_principal,
132            create_controller(ControllerScope::Write, None),
133        );
134
135        assert!(is_controller(controller_principal, &controllers));
136        assert!(!is_controller(non_controller, &controllers));
137    }
138
139    #[test]
140    fn test_public_permission_allows_anyone() {
141        let controllers = Controllers::new();
142        let owner = test_principal(1);
143        let caller = test_principal(2);
144
145        assert!(assert_permission(
146            &Permission::Public,
147            owner,
148            caller,
149            &controllers
150        ));
151    }
152
153    #[test]
154    fn test_private_permission_allows_owner_not_controller() {
155        let controllers = Controllers::new();
156        let owner = test_principal(1);
157
158        assert!(assert_permission(
159            &Permission::Private,
160            owner,
161            owner,
162            &controllers
163        ));
164    }
165
166    #[test]
167    fn test_private_permission_allows_owner_valid_controller() {
168        let mut controllers = Controllers::new();
169        let owner = test_principal(1);
170
171        controllers.insert(
172            owner,
173            create_controller(ControllerScope::Write, Some(mock_time() + 1000)),
174        );
175
176        assert!(assert_permission(
177            &Permission::Private,
178            owner,
179            owner,
180            &controllers
181        ));
182    }
183
184    #[test]
185    fn test_private_permission_rejects_owner_expired_controller() {
186        let mut controllers = Controllers::new();
187        let owner = test_principal(1);
188
189        controllers.insert(
190            owner,
191            create_controller(ControllerScope::Write, Some(mock_time() - 1)),
192        );
193
194        assert!(!assert_permission(
195            &Permission::Private,
196            owner,
197            owner,
198            &controllers
199        ));
200    }
201
202    #[test]
203    fn test_private_permission_rejects_non_owner() {
204        let controllers = Controllers::new();
205        let owner = test_principal(1);
206        let caller = test_principal(2);
207
208        assert!(!assert_permission(
209            &Permission::Private,
210            owner,
211            caller,
212            &controllers
213        ));
214    }
215
216    #[test]
217    fn test_managed_permission_allows_owner_not_controller() {
218        let controllers = Controllers::new();
219        let owner = test_principal(1);
220
221        assert!(assert_permission(
222            &Permission::Managed,
223            owner,
224            owner,
225            &controllers
226        ));
227    }
228
229    #[test]
230    fn test_managed_permission_allows_owner_valid_controller() {
231        let mut controllers = Controllers::new();
232        let owner = test_principal(1);
233
234        controllers.insert(
235            owner,
236            create_controller(ControllerScope::Write, Some(mock_time() + 1000)),
237        );
238
239        assert!(assert_permission(
240            &Permission::Managed,
241            owner,
242            owner,
243            &controllers
244        ));
245    }
246
247    #[test]
248    fn test_managed_permission_rejects_owner_expired_controller() {
249        let mut controllers = Controllers::new();
250        let owner = test_principal(1);
251
252        controllers.insert(
253            owner,
254            create_controller(ControllerScope::Write, Some(mock_time() - 1)),
255        );
256
257        assert!(!assert_permission(
258            &Permission::Managed,
259            owner,
260            owner,
261            &controllers
262        ));
263    }
264
265    #[test]
266    fn test_managed_permission_allows_valid_write_controller() {
267        let mut controllers = Controllers::new();
268        let owner = test_principal(1);
269        let controller = test_principal(2);
270
271        controllers.insert(
272            controller,
273            create_controller(ControllerScope::Write, Some(mock_time() + 1000)),
274        );
275
276        assert!(assert_permission(
277            &Permission::Managed,
278            owner,
279            controller,
280            &controllers
281        ));
282    }
283
284    #[test]
285    fn test_managed_permission_rejects_expired_write_controller() {
286        let mut controllers = Controllers::new();
287        let owner = test_principal(1);
288        let controller = test_principal(2);
289
290        controllers.insert(
291            controller,
292            create_controller(ControllerScope::Write, Some(mock_time() - 1)),
293        );
294
295        assert!(!assert_permission(
296            &Permission::Managed,
297            owner,
298            controller,
299            &controllers
300        ));
301    }
302
303    #[test]
304    fn test_managed_permission_rejects_submit_controller() {
305        let mut controllers = Controllers::new();
306        let owner = test_principal(1);
307        let controller = test_principal(2);
308
309        controllers.insert(controller, create_controller(ControllerScope::Submit, None));
310
311        assert!(!assert_permission(
312            &Permission::Managed,
313            owner,
314            controller,
315            &controllers
316        ));
317    }
318
319    #[test]
320    fn test_controllers_permission_allows_valid_controller() {
321        let mut controllers = Controllers::new();
322        let owner = test_principal(1);
323        let controller = test_principal(2);
324
325        controllers.insert(
326            controller,
327            create_controller(ControllerScope::Write, Some(mock_time() + 1000)),
328        );
329
330        assert!(assert_permission(
331            &Permission::Controllers,
332            owner,
333            controller,
334            &controllers
335        ));
336    }
337
338    #[test]
339    fn test_controllers_permission_rejects_expired_controller() {
340        let mut controllers = Controllers::new();
341        let owner = test_principal(1);
342        let controller = test_principal(2);
343
344        controllers.insert(
345            controller,
346            create_controller(ControllerScope::Write, Some(mock_time() - 1)),
347        );
348
349        assert!(!assert_permission(
350            &Permission::Controllers,
351            owner,
352            controller,
353            &controllers
354        ));
355    }
356
357    // Create permission tests
358    #[test]
359    fn test_create_public_allows_anyone() {
360        let controllers = Controllers::new();
361        let caller = test_principal(1);
362
363        assert!(assert_create_permission(
364            &Permission::Public,
365            caller,
366            &controllers
367        ));
368    }
369
370    #[test]
371    fn test_create_private_allows_non_controller() {
372        let controllers = Controllers::new();
373        let caller = test_principal(1);
374
375        assert!(assert_create_permission(
376            &Permission::Private,
377            caller,
378            &controllers
379        ));
380    }
381
382    #[test]
383    fn test_create_private_allows_valid_controller() {
384        let mut controllers = Controllers::new();
385        let caller = test_principal(1);
386
387        controllers.insert(
388            caller,
389            create_controller(ControllerScope::Write, Some(mock_time() + 1000)),
390        );
391
392        assert!(assert_create_permission(
393            &Permission::Private,
394            caller,
395            &controllers
396        ));
397    }
398
399    #[test]
400    fn test_create_private_rejects_expired_controller() {
401        let mut controllers = Controllers::new();
402        let caller = test_principal(1);
403
404        controllers.insert(
405            caller,
406            create_controller(ControllerScope::Write, Some(mock_time() - 1)),
407        );
408
409        assert!(!assert_create_permission(
410            &Permission::Private,
411            caller,
412            &controllers
413        ));
414    }
415
416    #[test]
417    fn test_create_controllers_allows_valid_write_controller() {
418        let mut controllers = Controllers::new();
419        let caller = test_principal(1);
420
421        controllers.insert(
422            caller,
423            create_controller(ControllerScope::Write, Some(mock_time() + 1000)),
424        );
425
426        assert!(assert_create_permission(
427            &Permission::Controllers,
428            caller,
429            &controllers
430        ));
431    }
432
433    #[test]
434    fn test_create_controllers_rejects_submit_controller() {
435        let mut controllers = Controllers::new();
436        let caller = test_principal(1);
437
438        controllers.insert(caller, create_controller(ControllerScope::Submit, None));
439
440        assert!(!assert_create_permission(
441            &Permission::Controllers,
442            caller,
443            &controllers
444        ));
445    }
446}