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 let Ok(user_id) = UserId::try_new(identifier)
24            && let Some(user) = self.user_service.find_by_id(&user_id).await?
25        {
26            return Ok(Some(user));
27        }
28
29        if identifier.contains('@') {
30            return self.user_service.find_by_email(identifier).await;
31        }
32
33        self.user_service.find_by_name(identifier).await
34    }
35
36    pub async fn promote_to_admin(&self, user_identifier: &str) -> Result<PromoteResult> {
37        let user = self.find_user(user_identifier).await?;
38        let admin_role = UserRole::Admin.as_str().to_owned();
39        let user_role = UserRole::User.as_str().to_owned();
40
41        match user {
42            Some(u) => {
43                let current_roles = u.roles.clone();
44
45                if current_roles.contains(&admin_role) {
46                    return Ok(PromoteResult::AlreadyAdmin(u));
47                }
48
49                let mut new_roles = current_roles;
50                if !new_roles.contains(&admin_role) {
51                    new_roles.push(admin_role);
52                }
53                if !new_roles.contains(&user_role) {
54                    new_roles.push(user_role);
55                }
56
57                let updated = self.user_service.assign_roles(&u.id, &new_roles).await?;
58                Ok(PromoteResult::Promoted(updated, new_roles))
59            },
60            None => Ok(PromoteResult::UserNotFound),
61        }
62    }
63
64    pub async fn demote_from_admin(&self, user_identifier: &str) -> Result<DemoteResult> {
65        let user = self.find_user(user_identifier).await?;
66        let admin_role = UserRole::Admin.as_str();
67        let user_role = UserRole::User.as_str().to_owned();
68
69        match user {
70            Some(u) => {
71                let current_roles = u.roles.clone();
72
73                if !current_roles.contains(&admin_role.to_owned()) {
74                    return Ok(DemoteResult::NotAdmin(u));
75                }
76
77                let new_roles: Vec<String> = current_roles
78                    .into_iter()
79                    .filter(|r| r != admin_role)
80                    .collect();
81
82                let mut final_roles = new_roles;
83                if !final_roles.contains(&user_role) {
84                    final_roles.push(user_role);
85                }
86
87                let updated = self.user_service.assign_roles(&u.id, &final_roles).await?;
88                Ok(DemoteResult::Demoted(updated, final_roles))
89            },
90            None => Ok(DemoteResult::UserNotFound),
91        }
92    }
93}
94
95#[derive(Debug)]
96pub enum PromoteResult {
97    Promoted(User, Vec<String>),
98    AlreadyAdmin(User),
99    UserNotFound,
100}
101
102#[derive(Debug)]
103pub enum DemoteResult {
104    Demoted(User, Vec<String>),
105    NotAdmin(User),
106    UserNotFound,
107}