Skip to main content

ic_canister_kit/functions/
permission.rs

1use 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// 权限管理
13
14/// 权限修改参数
15#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
16pub enum PermissionUpdatedArg<Permission: Eq + Hash> {
17    /// 更新用户权限
18    UpdateUserPermission(UserId, Option<HashSet<Permission>>),
19    /// 更新角色权限
20    UpdateRolePermission(String, Option<HashSet<Permission>>),
21    /// 更新用户角色
22    UpdateUserRole(UserId, Option<HashSet<String>>),
23}
24
25/// 权限更新错误
26#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
27pub enum PermissionUpdatedError<Permission> {
28    /// 权限不存在错误
29    InvalidPermission(Permission),
30    /// 角色不存在错误
31    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
45/// 权限管理
46pub trait Permissable<Permission: Eq + Hash> {
47    // 查询
48    ///  当前管理的所有用户 包括直接授权的和通过角色授权的
49    fn permission_users(&self) -> HashSet<&UserId>;
50    ///  当前管理的所有角色
51    fn permission_roles(&self) -> HashSet<&String>;
52
53    ///  某用户被直接授权的权限
54    fn permission_assigned(&self, user_id: &UserId) -> Option<&HashSet<Permission>>;
55    ///  某角色被直接授权的权限
56    fn permission_role_assigned(&self, role: &str) -> Option<&HashSet<Permission>>;
57    /// 某用户被授权的角色
58    fn permission_user_roles(&self, user_id: &UserId) -> Option<&HashSet<String>>;
59
60    // 综合直接授权和角色间接授权的情况
61    // 若权限是默认没有的(Permitted),任意路径包含则有该权限,fallback 是 无
62    // 若权限是默认拥有的(Forbidden),任意路径包含则无该权限,fallback 是 有
63
64    /// 判断用户是否拥有某权限
65    fn permission_has(&self, user_id: &UserId, permission: &Permission) -> bool;
66    /// 获取用户的综合权限情况
67    fn permission_owned(&self, user_id: &UserId) -> HashMap<&Permission, bool>;
68
69    // 修改
70
71    /// 重置管理的权限,防止版本更新导致某一个权限的信息不一致
72    fn permission_reset(&mut self, permissions: HashSet<Permission>);
73    /// 权限更新
74    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
111// ================== 简单实现 ==================
112
113/// 权限功能简单实现
114pub 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    /// 被管理的用户类型
129    #[derive(CandidType, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
130    pub enum Permission {
131        /// 授权类型 默认没有该权限 只有被加入的用户才有该权限
132        Permitted(String),
133        /// 禁止类型 默认拥有该权限 如果被加入了就没有该权限了
134        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        /// 构造许可权限
148        pub fn by_permit(name: &str) -> Self {
149            Permission::Permitted(name.to_string())
150        }
151        /// 构造禁止权限
152        pub fn by_forbid(name: &str) -> Self {
153            Permission::Forbidden(name.to_string())
154        }
155        /// 判断是否许可权限
156        pub fn is_permit(&self) -> bool {
157            matches!(self, Self::Permitted(_))
158        }
159        /// 判断是否禁止权限
160        pub fn is_forbid(&self) -> bool {
161            matches!(self, Self::Forbidden(_))
162        }
163        /// 文本化
164        pub fn name(&self) -> &str {
165            match self {
166                Permission::Permitted(name) => name,
167                Permission::Forbidden(name) => name,
168            }
169        }
170    }
171
172    /// 多个权限对象
173    #[derive(CandidType, Serialize, Deserialize, Debug, Clone, Default)]
174    pub struct Permissions {
175        /// 所有权限种类
176        pub permissions: HashSet<Permission>,
177        /// 用户分配的特别权限, Permitted表示拥有, Forbidden表示禁止
178        pub user_permissions: HashMap<UserId, HashSet<Permission>>,
179        /// 某角色对权限的限制
180        pub role_permissions: HashMap<String, HashSet<Permission>>,
181        /// 用户被授权的角色
182        pub user_roles: HashMap<UserId, HashSet<String>>,
183    }
184
185    impl Permissions {
186        // 检查一定存在权限
187        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        // 检查一定存在角色
202        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        // 查询
216        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            // 单独指定
237            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            // 角色自定
246            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            // 不存在则默认
259            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        // 修改
272        fn permission_reset(&mut self, permissions: HashSet<Permission>) {
273            self.permissions = permissions;
274            // 核对其他数据中的权限是否正确
275            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            // 在副本上依次应用,既保留“先创建角色、再给用户分配角色”的批次语义,
303            // 又保证任意一项失败时原权限状态完全不变。
304            let mut updated = self.clone();
305            for arg in args.iter() {
306                match arg {
307                    PermissionUpdatedArg::UpdateUserPermission(user_id, permissions) => {
308                        // 先检查权限是否都存在
309                        updated.assure_permission_exist(permissions)?;
310
311                        let exist = updated.user_permissions.get(user_id);
312                        if let Some(permissions) = &permissions {
313                            if let Some(exist) = exist
314                                && exist == permissions
315                            {
316                                continue;
317                            }
318                        } else if exist.is_none() {
319                            continue;
320                        }
321                        if let Some(permissions) = permissions {
322                            updated.user_permissions.insert(*user_id, permissions.clone());
323                        } else {
324                            updated.user_permissions.remove(user_id);
325                        }
326                    }
327                    PermissionUpdatedArg::UpdateRolePermission(role, permissions) => {
328                        // 先检查权限是否都存在
329                        updated.assure_permission_exist(permissions)?;
330
331                        let exist = updated.role_permissions.get(role);
332                        if let Some(permissions) = permissions {
333                            if let Some(exist) = exist
334                                && exist == permissions
335                            {
336                                continue;
337                            }
338                        } else if exist.is_none() {
339                            continue;
340                        }
341                        if let Some(permissions) = permissions {
342                            updated.role_permissions.insert(role.clone(), permissions.clone());
343                        } else {
344                            updated.role_permissions.remove(role);
345                            // 移除要检查用户角色数据对不对
346                            let valid_roles: HashSet<String> = updated.role_permissions.keys().cloned().collect();
347                            updated.user_roles.iter_mut().for_each(|(_, roles)| {
348                                let mut removed = Vec::new();
349                                for role in roles.iter() {
350                                    if !valid_roles.contains(role) {
351                                        removed.push(role.clone());
352                                    }
353                                }
354                                for role in removed {
355                                    roles.remove(&role);
356                                }
357                            });
358                        }
359                    }
360                    PermissionUpdatedArg::UpdateUserRole(user_id, roles) => {
361                        // 先检查角色是否都存在
362                        updated.assure_role_exist(roles)?;
363
364                        let exist = updated.user_roles.get(user_id);
365                        if let Some(roles) = &roles {
366                            if let Some(exist) = exist
367                                && exist == roles
368                            {
369                                continue;
370                            }
371                        } else if exist.is_none() {
372                            continue;
373                        }
374                        if let Some(roles) = roles {
375                            updated.user_roles.insert(*user_id, roles.clone());
376                        } else {
377                            updated.user_roles.remove(user_id);
378                        }
379                    }
380                }
381            }
382            *self = updated;
383            Ok(())
384        }
385    }
386
387    impl PermissionUpdatedArg<String> {
388        /// 解析权限,返回 PermissionUpdatedArg
389        ///
390        /// # Arguments
391        ///
392        /// * `f` - 权限解析函数,将字符串解析为 Permission
393        pub fn parse_permission<E, F: Fn(&str) -> Result<Permission, E>>(
394            self,
395            f: F,
396        ) -> Result<PermissionUpdatedArg<Permission>, E> {
397            Ok(match self {
398                PermissionUpdatedArg::UpdateUserPermission(user_id, permissions) => {
399                    PermissionUpdatedArg::UpdateUserPermission(
400                        user_id,
401                        permissions
402                            .map(|ps| ps.into_iter().map(|p| f(&p)).collect::<Result<HashSet<_>, _>>())
403                            .transpose()?,
404                    )
405                }
406                PermissionUpdatedArg::UpdateRolePermission(role, permissions) => {
407                    PermissionUpdatedArg::UpdateRolePermission(
408                        role,
409                        permissions
410                            .map(|ps| ps.into_iter().map(|p| f(&p)).collect::<Result<HashSet<_>, _>>())
411                            .transpose()?,
412                    )
413                }
414                PermissionUpdatedArg::UpdateUserRole(user_id, roles) => {
415                    PermissionUpdatedArg::UpdateUserRole(user_id, roles)
416                }
417            })
418        }
419    }
420
421    // ================= 工具方法 =================
422
423    /// 解析所有权限
424    pub fn parse_all_permissions<'a, F, E>(actions: &[&'a str], parse: F) -> Result<Vec<Permission>, E>
425    where
426        F: Fn(&'a str) -> Result<Permission, E>,
427    {
428        let mut permissions = Vec::with_capacity(actions.len());
429        for name in actions {
430            permissions.push(parse(name)?);
431        }
432        Ok(permissions)
433    }
434
435    /// 超级管理员获取所有授权权限
436    pub fn permitted_permissions(permissions: &HashSet<Permission>) -> HashSet<Permission> {
437        permissions.iter().filter(|p| p.is_permit()).cloned().collect()
438    }
439
440    /// 超级管理员获取所有权限
441    pub fn supers_updated(
442        supers: &[UserId],
443        permissions: &HashSet<Permission>,
444    ) -> Vec<PermissionUpdatedArg<Permission>> {
445        let permitted: HashSet<Permission> = permitted_permissions(permissions);
446        supers
447            .iter()
448            .map(|su| PermissionUpdatedArg::UpdateUserPermission(*su, Some(permitted.clone())))
449            .collect()
450    }
451
452    #[cfg(test)]
453    mod tests {
454        use std::collections::HashSet;
455
456        use candid::Principal;
457
458        use super::{Permission, Permissions};
459        use crate::functions::permission::{Permissable, PermissionUpdatedArg, PermissionUpdatedError};
460
461        fn permissions() -> Permissions {
462            Permissions {
463                permissions: HashSet::from([Permission::by_permit("read")]),
464                ..Default::default()
465            }
466        }
467
468        #[test]
469        fn batch_update_is_atomic_when_a_later_item_is_invalid() {
470            let user = Principal::anonymous();
471            let read = Permission::by_permit("read");
472            let invalid = Permission::by_permit("missing");
473            let mut permissions = permissions();
474
475            let result = permissions.permission_update(vec![
476                PermissionUpdatedArg::UpdateUserPermission(user, Some(HashSet::from([read]))),
477                PermissionUpdatedArg::UpdateUserPermission(user, Some(HashSet::from([invalid]))),
478            ]);
479
480            assert!(matches!(result, Err(PermissionUpdatedError::InvalidPermission(_))));
481            assert!(permissions.user_permissions.is_empty());
482        }
483
484        #[test]
485        fn batch_can_create_and_assign_a_role_atomically() {
486            let user = Principal::anonymous();
487            let read = Permission::by_permit("read");
488            let mut permissions = permissions();
489
490            permissions
491                .permission_update(vec![
492                    PermissionUpdatedArg::UpdateRolePermission(
493                        "reader".to_string(),
494                        Some(HashSet::from([read.clone()])),
495                    ),
496                    PermissionUpdatedArg::UpdateUserRole(user, Some(HashSet::from(["reader".to_string()]))),
497                ])
498                .unwrap();
499
500            assert!(permissions.permission_has(&user, &read));
501        }
502    }
503}