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