ic_canister_kit/functions/
permission.rs1use std::{
2 collections::{HashMap, HashSet},
3 fmt::{Debug, Display},
4 hash::Hash,
5};
6
7use candid::CandidType;
8use serde::{Deserialize, Serialize};
9
10use crate::{common::option::display_option_by, identity::UserId};
11
12#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
16pub enum PermissionUpdatedArg<Permission: Eq + Hash> {
17 UpdateUserPermission(UserId, Option<HashSet<Permission>>),
19 UpdateRolePermission(String, Option<HashSet<Permission>>),
21 UpdateUserRole(UserId, Option<HashSet<String>>),
23}
24
25#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
27pub enum PermissionUpdatedError<Permission> {
28 InvalidPermission(Permission),
30 InvalidRole(String),
32}
33impl<Permission: Debug> Display for PermissionUpdatedError<Permission> {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 match self {
36 PermissionUpdatedError::InvalidPermission(permission) => {
37 write!(f, "InvalidPermission({permission:?})")
38 }
39 PermissionUpdatedError::InvalidRole(role) => write!(f, "InvalidRole({role})"),
40 }
41 }
42}
43impl<Permission: Debug> std::error::Error for PermissionUpdatedError<Permission> {}
44
45pub trait Permissable<Permission: Eq + Hash> {
47 fn permission_users(&self) -> HashSet<&UserId>;
50 fn permission_roles(&self) -> HashSet<&String>;
52
53 fn permission_assigned(&self, user_id: &UserId) -> Option<&HashSet<Permission>>;
55 fn permission_role_assigned(&self, role: &str) -> Option<&HashSet<Permission>>;
57 fn permission_user_roles(&self, user_id: &UserId) -> Option<&HashSet<String>>;
59
60 fn permission_has(&self, user_id: &UserId, permission: &Permission) -> bool;
66 fn permission_owned(&self, user_id: &UserId) -> HashMap<&Permission, bool>;
68
69 fn permission_reset(&mut self, permissions: HashSet<Permission>);
73 fn permission_update(
75 &mut self,
76 args: Vec<PermissionUpdatedArg<Permission>>,
77 ) -> Result<(), PermissionUpdatedError<Permission>>;
78}
79
80impl Display for PermissionUpdatedArg<String> {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 match self {
83 Self::UpdateUserPermission(user_id, permissions) => f.write_str(&format!(
84 "update user: {} permissions: {}",
85 user_id.to_text(),
86 display_option_by(permissions, |permissions| format!(
87 "[{}]",
88 permissions.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(",")
89 ))
90 )),
91 Self::UpdateRolePermission(role, permissions) => f.write_str(&format!(
92 "update role: {} permissions: {}",
93 role,
94 display_option_by(permissions, |permissions| format!(
95 "[{}]",
96 permissions.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(",")
97 ))
98 )),
99 Self::UpdateUserRole(user_id, roles) => f.write_str(&format!(
100 "update user: {} roles: {}",
101 user_id.to_text(),
102 display_option_by(roles, |roles| format!(
103 "[{}]",
104 roles.iter().cloned().collect::<Vec<_>>().join(",")
105 ))
106 )),
107 }
108 }
109}
110
111pub mod basic {
115 use std::{
116 collections::{HashMap, HashSet},
117 fmt::Display,
118 };
119
120 use candid::CandidType;
121 use serde::{Deserialize, Serialize};
122
123 use crate::{
124 functions::types::{Permissable, PermissionUpdatedArg, PermissionUpdatedError},
125 identity::UserId,
126 };
127
128 #[derive(CandidType, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
130 pub enum Permission {
131 Permitted(String),
133 Forbidden(String),
135 }
136
137 impl Display for Permission {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 match self {
140 Permission::Permitted(name) => write!(f, "Permitted({name})"),
141 Permission::Forbidden(name) => write!(f, "Forbidden({name})"),
142 }
143 }
144 }
145
146 impl Permission {
147 pub fn by_permit(name: &str) -> Self {
149 Permission::Permitted(name.to_string())
150 }
151 pub fn by_forbid(name: &str) -> Self {
153 Permission::Forbidden(name.to_string())
154 }
155 pub fn is_permit(&self) -> bool {
157 matches!(self, Self::Permitted(_))
158 }
159 pub fn is_forbid(&self) -> bool {
161 matches!(self, Self::Forbidden(_))
162 }
163 pub fn name(&self) -> &str {
165 match self {
166 Permission::Permitted(name) => name,
167 Permission::Forbidden(name) => name,
168 }
169 }
170 }
171
172 #[derive(CandidType, Serialize, Deserialize, Debug, Clone, Default)]
174 pub struct Permissions {
175 pub permissions: HashSet<Permission>,
177 pub user_permissions: HashMap<UserId, HashSet<Permission>>,
179 pub role_permissions: HashMap<String, HashSet<Permission>>,
181 pub user_roles: HashMap<UserId, HashSet<String>>,
183 }
184
185 impl Permissions {
186 fn assure_permission_exist(
188 &self,
189 permissions: &Option<HashSet<Permission>>,
190 ) -> Result<(), PermissionUpdatedError<Permission>> {
191 if let Some(permissions) = permissions {
192 for permission in permissions {
193 if !self.permissions.contains(permission) {
194 return Err(PermissionUpdatedError::InvalidPermission(permission.clone()));
195 }
196 }
197 }
198
199 Ok(())
200 }
201 fn assure_role_exist(&self, roles: &Option<HashSet<String>>) -> Result<(), PermissionUpdatedError<Permission>> {
203 if let Some(roles) = roles {
204 for role in roles {
205 if !self.role_permissions.contains_key(role) {
206 return Err(PermissionUpdatedError::InvalidRole(role.clone()));
207 }
208 }
209 }
210 Ok(())
211 }
212 }
213
214 impl Permissable<Permission> for Permissions {
215 fn permission_users(&self) -> HashSet<&UserId> {
217 let mut users: HashSet<&UserId> = self.user_roles.keys().collect();
218 users.extend(self.user_permissions.keys());
219 users
220 }
221 fn permission_roles(&self) -> HashSet<&String> {
222 self.role_permissions.keys().collect()
223 }
224
225 fn permission_assigned(&self, user_id: &UserId) -> Option<&HashSet<Permission>> {
226 self.user_permissions.get(user_id)
227 }
228 fn permission_role_assigned(&self, role: &str) -> Option<&HashSet<Permission>> {
229 self.role_permissions.get(role)
230 }
231 fn permission_user_roles(&self, user_id: &UserId) -> Option<&HashSet<String>> {
232 self.user_roles.get(user_id)
233 }
234
235 fn permission_has(&self, user_id: &UserId, permission: &Permission) -> bool {
236 if let Some(permissions) = self.user_permissions.get(user_id)
238 && permissions.contains(permission)
239 {
240 return match permission {
241 Permission::Permitted(_) => true,
242 Permission::Forbidden(_) => false,
243 };
244 }
245 if let Some(roles) = self.user_roles.get(user_id) {
247 for role in roles {
248 if let Some(permissions) = self.role_permissions.get(role)
249 && permissions.contains(permission)
250 {
251 return match permission {
252 Permission::Permitted(_) => true,
253 Permission::Forbidden(_) => false,
254 };
255 }
256 }
257 }
258 match permission {
260 Permission::Permitted(_) => false,
261 Permission::Forbidden(_) => true,
262 }
263 }
264 fn permission_owned(&self, user_id: &UserId) -> HashMap<&Permission, bool> {
265 self.permissions
266 .iter()
267 .map(|permission| (permission, self.permission_has(user_id, permission)))
268 .collect()
269 }
270
271 fn permission_reset(&mut self, permissions: HashSet<Permission>) {
273 self.permissions = permissions;
274 self.role_permissions.iter_mut().for_each(|(_, permissions)| {
276 let mut removed = Vec::new();
277 for permission in permissions.iter() {
278 if !self.permissions.contains(permission) {
279 removed.push(permission.clone());
280 }
281 }
282 for permission in removed {
283 permissions.remove(&permission);
284 }
285 });
286 self.user_permissions.iter_mut().for_each(|(_, permissions)| {
287 let mut removed = Vec::new();
288 for permission in permissions.iter() {
289 if !self.permissions.contains(permission) {
290 removed.push(permission.clone());
291 }
292 }
293 for permission in removed {
294 permissions.remove(&permission);
295 }
296 });
297 }
298 fn permission_update(
299 &mut self,
300 args: Vec<PermissionUpdatedArg<Permission>>,
301 ) -> Result<(), PermissionUpdatedError<Permission>> {
302 for arg in args.iter() {
303 match arg {
304 PermissionUpdatedArg::UpdateUserPermission(user_id, permissions) => {
305 self.assure_permission_exist(permissions)?;
307
308 let exist = self.user_permissions.get(user_id);
309 if let Some(permissions) = &permissions {
310 if let Some(exist) = exist
311 && exist == permissions
312 {
313 continue;
314 }
315 } else if exist.is_none() {
316 continue;
317 }
318 if let Some(permissions) = permissions {
319 self.user_permissions.insert(*user_id, permissions.clone());
320 } else {
321 self.user_permissions.remove(user_id);
322 }
323 }
324 PermissionUpdatedArg::UpdateRolePermission(role, permissions) => {
325 self.assure_permission_exist(permissions)?;
327
328 let exist = self.role_permissions.get(role);
329 if let Some(permissions) = permissions {
330 if let Some(exist) = exist
331 && exist == permissions
332 {
333 continue;
334 }
335 } else if exist.is_none() {
336 continue;
337 }
338 if let Some(permissions) = permissions {
339 self.role_permissions.insert(role.clone(), permissions.clone());
340 } else {
341 self.role_permissions.remove(role);
342 self.user_roles.iter_mut().for_each(|(_, roles)| {
344 let mut removed = Vec::new();
345 for role in roles.iter() {
346 if !self.role_permissions.contains_key(role) {
347 removed.push(role.clone());
348 }
349 }
350 for role in removed {
351 roles.remove(&role);
352 }
353 });
354 }
355 }
356 PermissionUpdatedArg::UpdateUserRole(user_id, roles) => {
357 self.assure_role_exist(roles)?;
359
360 let exist = self.user_roles.get(user_id);
361 if let Some(roles) = &roles {
362 if let Some(exist) = exist
363 && exist == roles
364 {
365 continue;
366 }
367 } else if exist.is_none() {
368 continue;
369 }
370 if let Some(roles) = roles {
371 self.user_roles.insert(*user_id, roles.clone());
372 } else {
373 self.user_roles.remove(user_id);
374 }
375 }
376 }
377 }
378 Ok(())
379 }
380 }
381
382 impl PermissionUpdatedArg<String> {
383 pub fn parse_permission<E, F: Fn(&str) -> Result<Permission, E>>(
389 self,
390 f: F,
391 ) -> Result<PermissionUpdatedArg<Permission>, E> {
392 Ok(match self {
393 PermissionUpdatedArg::UpdateUserPermission(user_id, permissions) => {
394 PermissionUpdatedArg::UpdateUserPermission(
395 user_id,
396 permissions
397 .map(|ps| ps.into_iter().map(|p| f(&p)).collect::<Result<HashSet<_>, _>>())
398 .transpose()?,
399 )
400 }
401 PermissionUpdatedArg::UpdateRolePermission(role, permissions) => {
402 PermissionUpdatedArg::UpdateRolePermission(
403 role,
404 permissions
405 .map(|ps| ps.into_iter().map(|p| f(&p)).collect::<Result<HashSet<_>, _>>())
406 .transpose()?,
407 )
408 }
409 PermissionUpdatedArg::UpdateUserRole(user_id, roles) => {
410 PermissionUpdatedArg::UpdateUserRole(user_id, roles)
411 }
412 })
413 }
414 }
415
416 pub fn parse_all_permissions<'a, F, E>(actions: &[&'a str], parse: F) -> Result<Vec<Permission>, E>
420 where
421 F: Fn(&'a str) -> Result<Permission, E>,
422 {
423 let mut permissions = Vec::with_capacity(actions.len());
424 for name in actions {
425 permissions.push(parse(name)?);
426 }
427 Ok(permissions)
428 }
429
430 pub fn permitted_permissions(permissions: &HashSet<Permission>) -> HashSet<Permission> {
432 permissions.iter().filter(|p| p.is_permit()).cloned().collect()
433 }
434
435 pub fn supers_updated(
437 supers: &[UserId],
438 permissions: &HashSet<Permission>,
439 ) -> Vec<PermissionUpdatedArg<Permission>> {
440 let permitted: HashSet<Permission> = permitted_permissions(permissions);
441 supers
442 .iter()
443 .map(|su| PermissionUpdatedArg::UpdateUserPermission(*su, Some(permitted.clone())))
444 .collect()
445 }
446}