Skip to main content

systemprompt_users/services/user/
provider.rs

1//! `UserProvider` implementation over `UserService`.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use std::str::FromStr;
7
8use async_trait::async_trait;
9use systemprompt_identifiers::UserId;
10use systemprompt_traits::auth::{
11    AuthProviderError, AuthResult, AuthUser, FederatedIdentityClaims, RoleProvider, UserProvider,
12};
13
14use super::UserService;
15use crate::models::{User, UserRole};
16
17impl From<User> for AuthUser {
18    fn from(user: User) -> Self {
19        let is_active = user.is_active();
20        Self {
21            id: user.id,
22            name: user.name,
23            email: user.email,
24            roles: user.roles,
25            is_active,
26        }
27    }
28}
29
30#[async_trait]
31impl UserProvider for UserService {
32    async fn find_by_id(&self, id: &UserId) -> AuthResult<Option<AuthUser>> {
33        self.find_by_id(id)
34            .await
35            .map(|opt| opt.map(AuthUser::from))
36            .map_err(|e| AuthProviderError::Internal(e.to_string()))
37    }
38
39    async fn find_by_email(&self, email: &str) -> AuthResult<Option<AuthUser>> {
40        Self::find_by_email(self, email)
41            .await
42            .map(|opt| opt.map(AuthUser::from))
43            .map_err(|e| AuthProviderError::Internal(e.to_string()))
44    }
45
46    async fn find_by_name(&self, name: &str) -> AuthResult<Option<AuthUser>> {
47        Self::find_by_name(self, name)
48            .await
49            .map(|opt| opt.map(AuthUser::from))
50            .map_err(|e| AuthProviderError::Internal(e.to_string()))
51    }
52
53    async fn create_user(
54        &self,
55        name: &str,
56        email: &str,
57        full_name: Option<&str>,
58    ) -> AuthResult<AuthUser> {
59        Self::create(self, name, email, full_name, full_name)
60            .await
61            .map(AuthUser::from)
62            .map_err(|e| AuthProviderError::Internal(e.to_string()))
63    }
64
65    async fn create_anonymous(&self, fingerprint: &str) -> AuthResult<AuthUser> {
66        Self::create_anonymous(self, fingerprint)
67            .await
68            .map(AuthUser::from)
69            .map_err(|e| AuthProviderError::Internal(e.to_string()))
70    }
71
72    async fn assign_roles(&self, user_id: &UserId, roles: &[String]) -> AuthResult<()> {
73        Self::assign_roles(self, user_id, roles)
74            .await
75            .map(|_| ())
76            .map_err(|e| AuthProviderError::Internal(e.to_string()))
77    }
78
79    async fn find_or_create_federated(
80        &self,
81        issuer: &str,
82        external_sub: &str,
83        claims: &FederatedIdentityClaims,
84    ) -> AuthResult<UserId> {
85        Self::find_or_create_federated(self, issuer, external_sub, claims)
86            .await
87            .map(|u| u.id)
88            .map_err(|e| AuthProviderError::Internal(e.to_string()))
89    }
90
91    async fn promote_anonymous(&self, source: &UserId, target: &UserId) -> AuthResult<u64> {
92        Self::promote_anonymous(self, source, target)
93            .await
94            .map(|result| result.total_rows)
95            .map_err(|e| AuthProviderError::Internal(e.to_string()))
96    }
97}
98
99#[async_trait]
100impl RoleProvider for UserService {
101    async fn get_roles(&self, user_id: &UserId) -> AuthResult<Vec<String>> {
102        match Self::find_by_id(self, user_id).await {
103            Ok(Some(user)) => Ok(user.roles),
104            Ok(None) => Err(AuthProviderError::UserNotFound),
105            Err(e) => Err(AuthProviderError::Internal(e.to_string())),
106        }
107    }
108
109    async fn assign_role(&self, user_id: &UserId, role: &str) -> AuthResult<()> {
110        let user = match Self::find_by_id(self, user_id).await {
111            Ok(Some(u)) => u,
112            Ok(None) => return Err(AuthProviderError::UserNotFound),
113            Err(e) => return Err(AuthProviderError::Internal(e.to_string())),
114        };
115
116        let mut roles = user.roles;
117        let role_str = role.to_owned();
118        if !roles.contains(&role_str) {
119            roles.push(role_str);
120        }
121
122        Self::assign_roles(self, user_id, &roles)
123            .await
124            .map(|_| ())
125            .map_err(|e| AuthProviderError::Internal(e.to_string()))
126    }
127
128    async fn revoke_role(&self, user_id: &UserId, role: &str) -> AuthResult<()> {
129        let user = match Self::find_by_id(self, user_id).await {
130            Ok(Some(u)) => u,
131            Ok(None) => return Err(AuthProviderError::UserNotFound),
132            Err(e) => return Err(AuthProviderError::Internal(e.to_string())),
133        };
134
135        let roles: Vec<String> = user.roles.into_iter().filter(|r| r != role).collect();
136
137        Self::assign_roles(self, user_id, &roles)
138            .await
139            .map(|_| ())
140            .map_err(|e| AuthProviderError::Internal(e.to_string()))
141    }
142
143    async fn list_users_by_role(&self, role: &str) -> AuthResult<Vec<AuthUser>> {
144        let Ok(user_role) = UserRole::from_str(role) else {
145            return Ok(vec![]);
146        };
147
148        Self::find_by_role(self, user_role)
149            .await
150            .map(|users| users.into_iter().map(AuthUser::from).collect())
151            .map_err(|e| AuthProviderError::Internal(e.to_string()))
152    }
153}