junobuild_shared/
controllers.rs1use crate::constants_internal::REVOKED_CONTROLLERS;
2use crate::env::{CONSOLE, OBSERVATORY};
3use crate::types::interface::SetController;
4use crate::types::state::{Controller, ControllerId, ControllerScope, Controllers, UserId};
5use crate::utils::{principal_anonymous, principal_equal, principal_not_anonymous};
6use candid::Principal;
7use ic_cdk::api::{is_controller as is_canister_controller, time};
8use ic_cdk::id;
9use std::collections::HashMap;
10
11pub fn init_controllers(new_controllers: &[UserId]) -> Controllers {
19 let mut controllers: Controllers = Controllers::new();
20
21 let controller_data: SetController = SetController {
22 metadata: HashMap::new(),
23 expires_at: None,
24 scope: ControllerScope::Admin,
25 };
26
27 set_controllers(new_controllers, &controller_data, &mut controllers);
28
29 controllers
30}
31
32pub fn set_controllers(
39 new_controllers: &[UserId],
40 controller_data: &SetController,
41 controllers: &mut Controllers,
42) {
43 for controller_id in new_controllers {
44 let existing_controller = controllers.get(controller_id);
45
46 let now = time();
47
48 let created_at: u64 = match existing_controller {
49 None => now,
50 Some(existing_controller) => existing_controller.created_at,
51 };
52
53 let updated_at: u64 = now;
54
55 let controller: Controller = Controller {
56 metadata: controller_data.metadata.clone(),
57 created_at,
58 updated_at,
59 expires_at: controller_data.expires_at,
60 scope: controller_data.scope.clone(),
61 };
62
63 controllers.insert(*controller_id, controller);
64 }
65}
66
67pub fn delete_controllers(remove_controllers: &[UserId], controllers: &mut Controllers) {
73 for c in remove_controllers {
74 controllers.remove(c);
75 }
76}
77
78pub fn is_controller(caller: UserId, controllers: &Controllers) -> bool {
87 principal_not_anonymous(caller)
88 && (caller_is_self(caller)
89 || controllers
90 .iter()
91 .any(|(&controller_id, _)| principal_equal(controller_id, caller)))
92}
93
94pub fn is_admin_controller(caller: UserId, controllers: &Controllers) -> bool {
103 is_canister_controller(&caller)
104 && principal_not_anonymous(caller)
105 && controllers
106 .iter()
107 .any(|(&controller_id, controller)| match controller.scope {
108 ControllerScope::Write => false,
109 ControllerScope::Admin => principal_equal(controller_id, caller),
110 })
111}
112
113pub fn into_controller_ids(controllers: &Controllers) -> Vec<ControllerId> {
121 controllers
122 .clone()
123 .into_keys()
124 .collect::<Vec<ControllerId>>()
125}
126
127pub fn assert_max_number_of_controllers(
137 current_controllers: &Controllers,
138 controllers_ids: &[ControllerId],
139 max_controllers: usize,
140) -> Result<(), String> {
141 let current_controller_ids = into_controller_ids(current_controllers);
142
143 let new_controller_ids = controllers_ids.iter().copied().filter(|id| {
144 !current_controller_ids
145 .iter()
146 .any(|current_id| current_id == id)
147 });
148
149 if current_controller_ids.len() + new_controller_ids.count() > max_controllers {
150 return Err(format!(
151 "Maximum number of controllers ({}) is already reached.",
152 max_controllers
153 ));
154 }
155
156 Ok(())
157}
158
159pub fn assert_controllers(controllers_ids: &[ControllerId]) -> Result<(), String> {
167 assert_no_anonymous_controller(controllers_ids)?;
168 assert_no_revoked_controller(controllers_ids)?;
169
170 Ok(())
171}
172
173fn assert_no_anonymous_controller(controllers_ids: &[ControllerId]) -> Result<(), String> {
181 let has_anonymous = controllers_ids
182 .iter()
183 .any(|controller_id| principal_anonymous(*controller_id));
184
185 match has_anonymous {
186 true => Err("Anonymous controller not allowed.".to_string()),
187 false => Ok(()),
188 }
189}
190
191fn assert_no_revoked_controller(controllers_ids: &[ControllerId]) -> Result<(), String> {
199 let has_revoked = controllers_ids.iter().any(controller_revoked);
201
202 match has_revoked {
203 true => Err("Revoked controller not allowed.".to_string()),
204 false => Ok(()),
205 }
206}
207
208pub fn caller_is_console(caller: UserId) -> bool {
216 let console = Principal::from_text(CONSOLE).unwrap();
217
218 principal_equal(caller, console)
219}
220
221pub fn caller_is_observatory(caller: UserId) -> bool {
229 let observatory = Principal::from_text(OBSERVATORY).unwrap();
230
231 principal_equal(caller, observatory)
232}
233
234pub fn caller_is_self(caller: UserId) -> bool {
242 let itself = id();
243
244 principal_equal(caller, itself)
245}
246
247pub fn filter_admin_controllers(controllers: &Controllers) -> Controllers {
255 controllers
256 .clone()
257 .into_iter()
258 .filter(|(_, controller)| match controller.scope {
259 ControllerScope::Write => false,
260 ControllerScope::Admin => true,
261 })
262 .collect()
263}
264
265fn controller_revoked(controller_id: &ControllerId) -> bool {
266 REVOKED_CONTROLLERS.iter().any(|revoked_controller_id| {
267 principal_equal(
268 Principal::from_text(revoked_controller_id).unwrap(),
269 *controller_id,
270 )
271 })
272}