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        let is_service_account = user.is_service_account();
21        Self {
22            id: user.id,
23            name: user.name,
24            email: user.email,
25            roles: user.roles,
26            is_active,
27            is_service_account,
28        }
29    }
30}
31
32#[async_trait]
33impl UserProvider for UserService {
34    async fn find_by_id(&self, id: &UserId) -> AuthResult<Option<AuthUser>> {
35        self.find_by_id(id)
36            .await
37            .map(|opt| opt.map(AuthUser::from))
38            .map_err(|e| AuthProviderError::Internal(e.into()))
39    }
40
41    async fn find_by_email(&self, email: &str) -> AuthResult<Option<AuthUser>> {
42        Self::find_by_email(self, email)
43            .await
44            .map(|opt| opt.map(AuthUser::from))
45            .map_err(|e| AuthProviderError::Internal(e.into()))
46    }
47
48    async fn find_by_name(&self, name: &str) -> AuthResult<Option<AuthUser>> {
49        Self::find_by_name(self, name)
50            .await
51            .map(|opt| opt.map(AuthUser::from))
52            .map_err(|e| AuthProviderError::Internal(e.into()))
53    }
54
55    async fn create_user(
56        &self,
57        name: &str,
58        email: &str,
59        full_name: Option<&str>,
60    ) -> AuthResult<AuthUser> {
61        Self::create(self, name, email, full_name, full_name)
62            .await
63            .map(AuthUser::from)
64            .map_err(|e| AuthProviderError::Internal(e.into()))
65    }
66
67    async fn create_anonymous(&self, fingerprint: &str) -> AuthResult<AuthUser> {
68        Self::create_anonymous(self, fingerprint)
69            .await
70            .map(AuthUser::from)
71            .map_err(|e| AuthProviderError::Internal(e.into()))
72    }
73
74    async fn assign_roles(&self, user_id: &UserId, roles: &[String]) -> AuthResult<()> {
75        Self::assign_roles(self, user_id, roles)
76            .await
77            .map(|_| ())
78            .map_err(|e| AuthProviderError::Internal(e.into()))
79    }
80
81    async fn find_or_create_federated(
82        &self,
83        issuer: &str,
84        external_sub: &str,
85        claims: &FederatedIdentityClaims,
86    ) -> AuthResult<UserId> {
87        Self::find_or_create_federated(self, issuer, external_sub, claims)
88            .await
89            .map(|u| u.id)
90            .map_err(|e| AuthProviderError::Internal(e.into()))
91    }
92
93    async fn find_federated(&self, issuer: &str, external_sub: &str) -> AuthResult<Option<UserId>> {
94        self.repository
95            .find_federated(issuer, external_sub)
96            .await
97            .map_err(|e| AuthProviderError::Internal(e.into()))
98    }
99
100    async fn link_federated(
101        &self,
102        issuer: &str,
103        external_sub: &str,
104        user_id: &UserId,
105    ) -> AuthResult<()> {
106        self.repository
107            .link_federated(issuer, external_sub, user_id)
108            .await
109            .map_err(|e| AuthProviderError::Internal(e.into()))
110    }
111
112    async fn promote_anonymous(&self, source: &UserId, target: &UserId) -> AuthResult<u64> {
113        Self::promote_anonymous(self, source, target)
114            .await
115            .map(|result| result.total_rows)
116            .map_err(|e| AuthProviderError::Internal(e.into()))
117    }
118}
119
120impl RoleProvider for UserService {
121    async fn get_roles(&self, user_id: &UserId) -> AuthResult<Vec<String>> {
122        match Self::find_by_id(self, user_id).await {
123            Ok(Some(user)) => Ok(user.roles),
124            Ok(None) => Err(AuthProviderError::UserNotFound),
125            Err(e) => Err(AuthProviderError::Internal(e.into())),
126        }
127    }
128
129    async fn assign_role(&self, user_id: &UserId, role: &str) -> AuthResult<()> {
130        let user = match Self::find_by_id(self, user_id).await {
131            Ok(Some(u)) => u,
132            Ok(None) => return Err(AuthProviderError::UserNotFound),
133            Err(e) => return Err(AuthProviderError::Internal(e.into())),
134        };
135
136        let mut roles = user.roles;
137        let role_str = role.to_owned();
138        if !roles.contains(&role_str) {
139            roles.push(role_str);
140        }
141
142        Self::assign_roles(self, user_id, &roles)
143            .await
144            .map(|_| ())
145            .map_err(|e| AuthProviderError::Internal(e.into()))
146    }
147
148    async fn revoke_role(&self, user_id: &UserId, role: &str) -> AuthResult<()> {
149        let user = match Self::find_by_id(self, user_id).await {
150            Ok(Some(u)) => u,
151            Ok(None) => return Err(AuthProviderError::UserNotFound),
152            Err(e) => return Err(AuthProviderError::Internal(e.into())),
153        };
154
155        let roles: Vec<String> = user.roles.into_iter().filter(|r| r != role).collect();
156
157        Self::assign_roles(self, user_id, &roles)
158            .await
159            .map(|_| ())
160            .map_err(|e| AuthProviderError::Internal(e.into()))
161    }
162
163    async fn list_users_by_role(&self, role: &str) -> AuthResult<Vec<AuthUser>> {
164        let Ok(user_role) = UserRole::from_str(role) else {
165            return Ok(vec![]);
166        };
167
168        Self::list_by_role(self, user_role)
169            .await
170            .map(|users| users.into_iter().map(AuthUser::from).collect())
171            .map_err(|e| AuthProviderError::Internal(e.into()))
172    }
173}