1use crate::{
2 admin::{Login, TenantAuthAdmin},
3 pg::{PGConnection, StoreError},
4 types::user::{
5 AuthMethod, CreateUser, LoginMethod, LoginResult, UpdateUser, User, UserRole,
6 UserSearchClauses,
7 },
8};
9use haste_fhir_operation_error::OperationOutcomeError;
10use haste_jwt::TenantId;
11use sqlx::{Acquire, Postgres, QueryBuilder};
12
13fn login<'a, 'c, Connection: Acquire<'c, Database = Postgres> + Send + 'a>(
14 connection: Connection,
15 tenant: &'a TenantId,
16 method: &'a LoginMethod,
17) -> impl Future<Output = Result<LoginResult, OperationOutcomeError>> + Send + 'a {
18 async move {
19 let mut conn = connection.acquire().await.map_err(StoreError::SQLXError)?;
20 match method {
21 LoginMethod::EmailPassword { email, password } => {
22 let user = sqlx::query_as!(
23 User,
24 r#"
25 SELECT id, tenant as "tenant: TenantId", email, role as "role: UserRole", method as "method: AuthMethod", provider_id FROM users WHERE tenant = $1 AND method = $2 AND email = $3 AND password = crypt($4, password)
26 "#,
27 tenant.as_ref(),
28 AuthMethod::EmailPassword as AuthMethod,
29 email,
30 password
31 ).fetch_optional(&mut *conn).await.map_err(StoreError::from)?;
32
33 if let Some(user) = user {
34 Ok(LoginResult::Success { user })
35 } else {
36 Ok(LoginResult::Failure)
37 }
38 }
39 LoginMethod::OIDC {
40 email: _,
41 provider_id: _,
42 } => Ok(LoginResult::Failure),
43 }
44 }
45}
46
47impl Login for PGConnection {
48 async fn login(
49 &self,
50 tenant: &TenantId,
51 method: &LoginMethod,
52 ) -> Result<LoginResult, haste_fhir_operation_error::OperationOutcomeError> {
53 match &self {
54 PGConnection::Pool(pool, _) => {
55 let res = login(pool, tenant, method).await?;
56 Ok(res)
57 }
58 PGConnection::Transaction(tx, _) => {
59 let mut tx = tx.lock().await;
60
61 let res = login(&mut *tx, tenant, method).await?;
62 Ok(res)
63 }
64 }
65 }
66}
67
68fn create_user<'a, 'c, Connection: Acquire<'c, Database = Postgres> + Send + 'a>(
69 connection: Connection,
70 tenant: &'a TenantId,
71 new_user: CreateUser,
72) -> impl Future<Output = Result<User, OperationOutcomeError>> + Send + 'a {
73 async move {
74 let mut conn = connection.acquire().await.map_err(StoreError::SQLXError)?;
75
76 let mut query_builder = QueryBuilder::new(
77 r#"
78 INSERT INTO users(tenant, id, email, role, method, provider_id, password)
79 "#,
80 );
81
82 query_builder.push(" VALUES (");
83
84 let mut seperator = query_builder.separated(", ");
85
86 seperator
87 .push_bind(tenant.as_ref())
88 .push_bind(new_user.id)
89 .push_bind(new_user.email)
90 .push_bind(new_user.role as UserRole)
91 .push_bind(new_user.method as AuthMethod);
92
93 if let Some(provider_id) = new_user.provider_id {
94 seperator.push_bind(provider_id);
95 } else {
96 seperator.push_bind(None::<String>);
97 }
98
99 if let Some(password) = new_user.password {
100 seperator
101 .push("crypt(")
102 .push_bind_unseparated(password)
103 .push_unseparated(", gen_salt('bf'))");
104 } else {
105 seperator.push_bind(None::<String>);
106 }
107
108 query_builder.push(r#") RETURNING id, tenant, provider_id, email, role , method"#);
109
110 let query = query_builder.build_query_as();
111
112 let user = query
113 .fetch_one(&mut *conn)
114 .await
115 .map_err(StoreError::SQLXError)?;
116
117 Ok(user)
118 }
119}
120
121fn read_user<'a, 'c, Connection: Acquire<'c, Database = Postgres> + Send + 'a>(
122 connection: Connection,
123 tenant: &'a TenantId,
124 id: &'a str,
125) -> impl Future<Output = Result<Option<User>, OperationOutcomeError>> + Send + 'a {
126 async move {
127 let mut conn = connection.acquire().await.map_err(StoreError::SQLXError)?;
128 let user = sqlx::query_as!(
129 User,
130 r#"
131 SELECT id, tenant as "tenant: TenantId", provider_id, email, role as "role: UserRole", method as "method: AuthMethod"
132 FROM users
133 WHERE tenant = $1 AND id = $2
134 "#,
135 tenant.as_ref(),
136 id
137 ).fetch_optional(&mut *conn).await.map_err(StoreError::SQLXError)?;
138
139 Ok(user)
140 }
141}
142
143fn update_user<'a, 'c, Connection: Acquire<'c, Database = Postgres> + Send + 'a>(
144 connection: Connection,
145 tenant: &'a TenantId,
146 model: UpdateUser,
147) -> impl Future<Output = Result<User, OperationOutcomeError>> + Send + 'a {
148 async move {
149 let mut conn = connection.acquire().await.map_err(StoreError::SQLXError)?;
150 let mut query_builder = QueryBuilder::new(
151 r#"
152 UPDATE users SET
153 "#,
154 );
155
156 let mut update_clauses = query_builder.separated(", ");
157
158 if let Some(provider_id) = model.provider_id {
159 update_clauses
160 .push(" provider_id = ")
161 .push_bind_unseparated(provider_id);
162 }
163
164 if let Some(email) = model.email.as_ref() {
165 update_clauses
166 .push(" email = ")
167 .push_bind_unseparated(email);
168 }
169
170 if let Some(role) = model.role.as_ref() {
171 update_clauses.push(" role = ").push_bind_unseparated(role);
172 }
173
174 if let Some(method) = model.method.as_ref() {
175 update_clauses
176 .push(" method = ")
177 .push_bind_unseparated(method);
178 }
179
180 if let Some(password) = model.password {
181 update_clauses
182 .push(" password = crypt(")
183 .push_bind_unseparated(password)
184 .push_unseparated(", gen_salt('bf'))");
185 }
186
187 update_clauses
188 .push(" tenant = ")
189 .push_bind_unseparated(tenant.as_ref());
190
191 query_builder.push(" WHERE id = ");
192 query_builder.push_bind(model.id);
193
194 query_builder.push(r#" RETURNING id, tenant, provider_id, email, role, method"#);
195
196 let query = query_builder.build_query_as();
197
198 let user = query
199 .fetch_one(&mut *conn)
200 .await
201 .map_err(StoreError::SQLXError)?;
202
203 Ok(user)
204 }
205}
206
207fn delete_user<'a, 'c, Connection: Acquire<'c, Database = Postgres> + Send + 'a>(
208 connection: Connection,
209 tenant: &'a TenantId,
210 id: &'a str,
211) -> impl Future<Output = Result<(), OperationOutcomeError>> + Send + 'a {
212 async move {
213 let mut conn = connection.acquire().await.map_err(StoreError::SQLXError)?;
214 let _user = sqlx::query_as!(
215 User,
216 r#"
217 DELETE FROM users
218 WHERE tenant = $1 AND id = $2
219 RETURNING id, tenant as "tenant: TenantId", provider_id, email, role as "role: UserRole", method as "method: AuthMethod"
220 "#,
221 tenant.as_ref(),
222 id
223 ).fetch_optional(&mut *conn).await.map_err(StoreError::SQLXError)?;
224
225 Ok(())
226 }
227}
228
229fn search_user<'a, 'c, Connection: Acquire<'c, Database = Postgres> + Send + 'a>(
230 connection: Connection,
231 tenant: &'a TenantId,
232 clauses: &'a UserSearchClauses,
233) -> impl Future<Output = Result<Vec<User>, OperationOutcomeError>> + Send + 'a {
234 async move {
235 let mut conn = connection.acquire().await.map_err(StoreError::SQLXError)?;
236 let mut query_builder: QueryBuilder<Postgres> = QueryBuilder::new(
237 r#"SELECT id, tenant, email, role, method, provider_id FROM users WHERE "#,
238 );
239
240 let mut seperator = query_builder.separated(" AND ");
241 seperator
242 .push(" tenant = ")
243 .push_bind_unseparated(tenant.as_ref());
244
245 if let Some(email) = clauses.email.as_ref() {
246 seperator.push(" email = ").push_bind_unseparated(email);
247 }
248
249 if let Some(role) = clauses.role.as_ref() {
250 seperator.push(" role = ").push_bind_unseparated(role);
251 }
252
253 if let Some(method) = clauses.method.as_ref() {
254 seperator.push(" method = ").push_bind_unseparated(method);
255 }
256
257 let query = query_builder.build_query_as();
258
259 let users: Vec<User> = query
260 .fetch_all(&mut *conn)
261 .await
262 .map_err(StoreError::from)?;
263
264 Ok(users)
265 }
266}
267
268impl<Key: AsRef<str> + Send + Sync>
269 TenantAuthAdmin<CreateUser, User, UserSearchClauses, UpdateUser, Key> for PGConnection
270{
271 async fn create(
272 &self,
273 tenant: &TenantId,
274 new_user: CreateUser,
275 ) -> Result<User, OperationOutcomeError> {
276 match self {
277 PGConnection::Pool(pool, _) => {
278 let res = create_user(pool, tenant, new_user).await?;
279 Ok(res)
280 }
281 PGConnection::Transaction(tx, _) => {
282 let mut tx = tx.lock().await;
283 let res = create_user(&mut *tx, tenant, new_user).await?;
284 Ok(res)
285 }
286 }
287 }
288
289 async fn read(
290 &self,
291 tenant: &TenantId,
292 id: &Key,
293 ) -> Result<Option<User>, OperationOutcomeError> {
294 match self {
295 PGConnection::Pool(pool, _) => {
296 let res = read_user(pool, tenant, id.as_ref()).await?;
297 Ok(res)
298 }
299 PGConnection::Transaction(tx, _) => {
300 let mut tx = tx.lock().await;
301 let res = read_user(&mut *tx, tenant, id.as_ref()).await?;
302 Ok(res)
303 }
304 }
305 }
306
307 async fn update(
308 &self,
309 tenant: &TenantId,
310 user: UpdateUser,
311 ) -> Result<User, OperationOutcomeError> {
312 match self {
313 PGConnection::Pool(pool, _) => {
314 let res = update_user(pool, &tenant, user).await?;
315 Ok(res)
316 }
317 PGConnection::Transaction(tx, _) => {
318 let mut tx = tx.lock().await;
319 let res = update_user(&mut *tx, &tenant, user).await?;
320 Ok(res)
321 }
322 }
323 }
324
325 async fn delete(&self, tenant: &TenantId, id: &Key) -> Result<(), OperationOutcomeError> {
326 match self {
327 PGConnection::Pool(pool, _) => {
328 let res = delete_user(pool, tenant, id.as_ref()).await?;
329 Ok(res)
330 }
331 PGConnection::Transaction(tx, _) => {
332 let mut tx = tx.lock().await;
333 let res = delete_user(&mut *tx, tenant, id.as_ref()).await?;
334 Ok(res)
335 }
336 }
337 }
338
339 async fn search(
340 &self,
341 tenant: &TenantId,
342 clauses: &UserSearchClauses,
343 ) -> Result<Vec<User>, OperationOutcomeError> {
344 match self {
345 PGConnection::Pool(pool, _) => {
346 let res = search_user(pool, tenant, clauses).await?;
347 Ok(res)
348 }
349 PGConnection::Transaction(tx, _) => {
350 let mut tx = tx.lock().await;
351 let res = search_user(&mut *tx, tenant, clauses).await?;
352 Ok(res)
353 }
354 }
355 }
356}