Skip to main content

systemprompt_users/services/
admin_service.rs

1//! Administrative user management operations.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use systemprompt_identifiers::UserId;
7
8use crate::UserService;
9use crate::error::Result;
10use crate::models::{User, UserRole};
11
12#[derive(Debug)]
13pub struct UserAdminService {
14    user_service: UserService,
15}
16
17impl UserAdminService {
18    pub const fn new(user_service: UserService) -> Self {
19        Self { user_service }
20    }
21
22    pub async fn find_user(&self, identifier: &str) -> Result<Option<User>> {
23        if uuid::Uuid::parse_str(identifier).is_ok() {
24            let user_id = UserId::new(identifier);
25            if let Some(user) = self.user_service.find_by_id(&user_id).await? {
26                return Ok(Some(user));
27            }
28        }
29
30        if identifier.contains('@') {
31            return self.user_service.find_by_email(identifier).await;
32        }
33
34        self.user_service.find_by_name(identifier).await
35    }
36
37    pub async fn promote_to_admin(&self, user_identifier: &str) -> Result<PromoteResult> {
38        let user = self.find_user(user_identifier).await?;
39        let admin_role = UserRole::Admin.as_str().to_owned();
40        let user_role = UserRole::User.as_str().to_owned();
41
42        match user {
43            Some(u) => {
44                let current_roles = u.roles.clone();
45
46                if current_roles.contains(&admin_role) {
47                    return Ok(PromoteResult::AlreadyAdmin(u));
48                }
49
50                let mut new_roles = current_roles;
51                if !new_roles.contains(&admin_role) {
52                    new_roles.push(admin_role);
53                }
54                if !new_roles.contains(&user_role) {
55                    new_roles.push(user_role);
56                }
57
58                let updated = self.user_service.assign_roles(&u.id, &new_roles).await?;
59                Ok(PromoteResult::Promoted(updated, new_roles))
60            },
61            None => Ok(PromoteResult::UserNotFound),
62        }
63    }
64
65    pub async fn demote_from_admin(&self, user_identifier: &str) -> Result<DemoteResult> {
66        let user = self.find_user(user_identifier).await?;
67        let admin_role = UserRole::Admin.as_str();
68        let user_role = UserRole::User.as_str().to_owned();
69
70        match user {
71            Some(u) => {
72                let current_roles = u.roles.clone();
73
74                if !current_roles.contains(&admin_role.to_owned()) {
75                    return Ok(DemoteResult::NotAdmin(u));
76                }
77
78                let new_roles: Vec<String> = current_roles
79                    .into_iter()
80                    .filter(|r| r != admin_role)
81                    .collect();
82
83                let mut final_roles = new_roles;
84                if !final_roles.contains(&user_role) {
85                    final_roles.push(user_role);
86                }
87
88                let updated = self.user_service.assign_roles(&u.id, &final_roles).await?;
89                Ok(DemoteResult::Demoted(updated, final_roles))
90            },
91            None => Ok(DemoteResult::UserNotFound),
92        }
93    }
94}
95
96#[derive(Debug)]
97pub enum PromoteResult {
98    Promoted(User, Vec<String>),
99    AlreadyAdmin(User),
100    UserNotFound,
101}
102
103#[derive(Debug)]
104pub enum DemoteResult {
105    Demoted(User, Vec<String>),
106    NotAdmin(User),
107    UserNotFound,
108}