1use super::{SqlxStore, entities::oauth_client::Model};
2use crate::{
3 model::{self, ActiveRow, SqlxModel},
4 schema::AuthSchema,
5};
6use alibi_core::{
7 AuthResult,
8 store::{CreateOAuthClient, OAuthClient, OAuthClientStore},
9};
10use async_trait::async_trait;
11impl<S: AuthSchema> SqlxStore<S> {
12 pub(super) async fn session_oauth_tokens(
13 &self,
14 exec: crate::pool::Exec<'_>,
15 kind: alibi_core::store::OAuthTokenKind,
16 session_id: &str,
17 ) -> AuthResult<Vec<alibi_core::store::OAuthToken>> {
18 macro_rules! select {
19 ($model:ty) => {{
20 let mut sql = model::select_model::<$model>(exec);
21 sql.push(" WHERE ");
22 sql.compare(<$model>::TABLE, "sessionId", " = ", session_id);
23 sql.push(" LIMIT ");
24 sql.bind(self.find_many_limit());
25 exec.fetch_all::<$model>(sql)
26 .await?
27 .into_iter()
28 .map(TryInto::try_into)
29 .collect()
30 }};
31 }
32 match kind {
33 alibi_core::store::OAuthTokenKind::Access => {
34 select!(super::entities::oauth_access_token::Model)
35 }
36 alibi_core::store::OAuthTokenKind::Refresh => {
37 select!(super::entities::oauth_refresh_token::Model)
38 }
39 }
40 }
41 pub(super) async fn oauth_client_with_exec(
42 &self,
43 exec: crate::pool::Exec<'_>,
44 client_id: &str,
45 ) -> AuthResult<Option<OAuthClient>> {
46 let mut sql = model::select_model::<Model>(exec);
47 sql.push(" WHERE ");
48 sql.compare(Model::TABLE, "clientId", " = ", client_id);
49 model::limit_one(&mut sql);
50 exec.fetch_optional::<Model>(sql)
51 .await?
52 .map(TryInto::try_into)
53 .transpose()
54 }
55}
56#[async_trait]
57impl<S: AuthSchema> OAuthClientStore for SqlxStore<S> {
58 async fn has_oauth_client_resource(
59 &self,
60 client_id: &str,
61 resource_id: &str,
62 ) -> AuthResult<bool> {
63 use super::entities::oauth_client_resource::Model as Link;
64 let mut sql = model::select_model::<Link>(self.exec());
65 sql.push(" WHERE ");
66 sql.compare(Link::TABLE, "clientId", " = ", client_id);
67 sql.push(" AND ");
68 sql.compare(Link::TABLE, "resourceId", " = ", resource_id);
69 model::limit_one(&mut sql);
70 Ok(self.exec().fetch_optional::<Link>(sql).await?.is_some())
71 }
72 async fn persist_oauth_client_registration(
73 &self,
74 data: alibi_core::store::oauth_clients::PersistOAuthClientRegistration,
75 ) -> Result<
76 Option<OAuthClient>,
77 alibi_core::store::oauth_clients::OAuthClientRegistrationWriteError,
78 > {
79 use super::entities::oauth_client_resource::Model as Link;
80 use alibi_core::store::oauth_clients::OAuthClientRegistrationWriteError as WriteError;
81 let tx = self.begin(true).await?;
82 let exec = crate::pool::Exec::tx(&tx);
83 let mut active = ActiveRow::new();
84 let metadata = data.client.metadata;
85 let patch = alibi_core::store::oauth_clients::metadata_to_columns(metadata.clone())?;
86 if data.replacement.is_none() {
87 active.set(
88 "id",
89 self.generated_id(exec, "oauthClient", "oauthClient", "id")
90 .await?
91 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
92 );
93 active.set("clientId", data.client.client_id.clone());
94 active.set("userId", data.client.user_id);
95 active.set("clientSecret", data.client.client_secret);
96 }
97 let fields = Model::columns_from_metadata(metadata)?.into_active();
98 for (column, value) in fields.present() {
99 if ![
100 "id",
101 "clientId",
102 "userId",
103 "clientSecret",
104 "createdAt",
105 "updatedAt",
106 ]
107 .contains(&column)
108 && (data.replacement.is_none()
109 || patch
110 .get(column)
111 .is_some_and(|value| column != "metadata" || !value.is_null()))
112 {
113 active.set(column, value.clone());
114 }
115 }
116 if data.replacement.is_none() {
117 active.set("createdAt", data.client.created_at);
118 } else if let Some(created_at) = data
119 .replacement
120 .as_ref()
121 .and_then(|replacement| replacement.existing_created_at)
122 {
123 active.set("createdAt", created_at);
124 }
125 active.set("updatedAt", data.updated_at);
126 let client = if let Some(replacement) = &data.replacement {
127 let mut sql = crate::sql::Sql::with(exec.engine(), "UPDATE ");
128 sql.ident(Model::TABLE);
129 sql.push(" SET ");
130 for (index, (column, value)) in active.changed().enumerate() {
131 if index > 0 {
132 sql.push(", ");
133 }
134 sql.ident(column);
135 sql.push(" = ");
136 model::bind_model_value::<Model>(&mut sql, column, value.clone());
137 }
138 sql.push(" WHERE ");
139 sql.compare(Model::TABLE, "clientId", " = ", &data.client.client_id);
140 sql.push(" AND ");
141 sql.compare(
142 Model::TABLE,
143 "clientDiscoveryId",
144 " = ",
145 &replacement.discovery_id,
146 );
147 model::returning::<Model>(&mut sql);
148 let Some(client) = exec.fetch_optional::<Model>(sql).await? else {
149 return Ok(None);
150 };
151 client
152 } else {
153 model::insert::<Model>(exec, &active)
154 .await
155 .map_err(|error| WriteError::classify(error, None))?
156 };
157 let linked_at = chrono::Utc::now();
158 for resource_id in &data.resources {
159 let mut sql = model::select_model::<Link>(exec);
160 sql.push(" WHERE ");
161 sql.compare(Link::TABLE, "clientId", " = ", &data.client.client_id);
162 sql.push(" AND ");
163 sql.compare(Link::TABLE, "resourceId", " = ", resource_id);
164 if exec.fetch_optional::<Link>(sql).await?.is_some() {
165 continue;
166 }
167 let mut link = ActiveRow::new();
168 link.set(
169 "id",
170 self.generated_id(exec, "oauthClientResource", "oauthClientResource", "id")
171 .await?
172 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
173 );
174 link.set("clientId", data.client.client_id.clone());
175 link.set("resourceId", resource_id.clone());
176 link.set("metadata", Option::<serde_json::Value>::None);
177 link.set("createdAt", Some(linked_at));
178 _ = model::insert::<Link>(exec, &link)
179 .await
180 .map_err(|error| WriteError::classify(error, Some(resource_id.clone())))?;
181 }
182 let client = client.try_into()?;
183 tx.commit().await?;
184 Ok(Some(client))
185 }
186 async fn claim_oauth_client_assertion(
187 &self,
188 id: &str,
189 expires_at: chrono::DateTime<chrono::Utc>,
190 ) -> AuthResult<bool> {
191 use super::entities::oauth_client_assertion::Model as Assertion;
192 let mut active = ActiveRow::new();
193 active.set("id", id);
194 active.set("expiresAt", expires_at);
195 match model::insert::<Assertion>(self.exec(), &active).await {
196 Ok(_) => Ok(true),
197 Err(error) => {
198 let mut sql = model::select_model::<Assertion>(self.exec());
199 sql.push(" WHERE ");
200 sql.compare(Assertion::TABLE, "id", " = ", id);
201 match self.exec().fetch_optional::<Assertion>(sql).await {
202 Ok(Some(_)) => Ok(false),
203 _ => Err(error),
204 }
205 }
206 }
207 }
208
209 async fn store_oauth_refresh_replay(&self, id: &str, ciphertext: &str) -> AuthResult<()> {
210 let mut sql = crate::sql::Sql::with(self.exec().engine(), "UPDATE ");
211 sql.ident("oauthRefreshToken");
212 sql.push(" SET ");
213 sql.assign("rotationReplayResponse", ciphertext);
214 sql.push(" WHERE ");
215 sql.compare("oauthRefreshToken", "id", " = ", id);
216 _ = self.exec().execute(sql).await?;
217 Ok(())
218 }
219 async fn claim_oauth_refresh_rotation(
220 &self,
221 token: &str,
222 at: chrono::DateTime<chrono::Utc>,
223 replay_expires: Option<chrono::DateTime<chrono::Utc>>,
224 ) -> AuthResult<bool> {
225 let mut sql = crate::sql::Sql::with(self.exec().engine(), "UPDATE ");
226 sql.ident("oauthRefreshToken");
227 sql.push(" SET ");
228 sql.assign("revoked", at);
229 sql.push(", ");
230 sql.assign("rotatedAt", at);
231 if let Some(expires) = replay_expires {
232 sql.push(", ");
233 sql.assign("rotationReplayExpiresAt", expires);
234 }
235 sql.push(" WHERE ");
236 sql.compare("oauthRefreshToken", "token", " = ", token);
237 sql.push(" AND ");
238 sql.ident("revoked");
239 sql.push(" IS NULL");
240 Ok(self.exec().execute(sql).await? > 0)
241 }
242
243 async fn claim_oauth_refresh_rotation_by_id(
244 &self,
245 id: &str,
246 at: chrono::DateTime<chrono::Utc>,
247 replay_expires: Option<chrono::DateTime<chrono::Utc>>,
248 ) -> AuthResult<bool> {
249 let mut sql = crate::sql::Sql::with(self.exec().engine(), "UPDATE ");
250 sql.ident("oauthRefreshToken");
251 sql.push(" SET ");
252 sql.assign("revoked", at);
253 sql.push(", ");
254 sql.assign("rotatedAt", at);
255 if let Some(expires) = replay_expires {
256 sql.push(", ");
257 sql.assign("rotationReplayExpiresAt", expires);
258 }
259 sql.push(" WHERE ");
260 sql.compare("oauthRefreshToken", "id", " = ", id);
261 sql.push(" AND ");
262 sql.ident("revoked");
263 sql.push(" IS NULL");
264 Ok(self.exec().execute(sql).await? > 0)
265 }
266
267 async fn mark_oauth_token_revoked(
268 &self,
269 kind: alibi_core::store::OAuthTokenKind,
270 id: &str,
271 at: chrono::DateTime<chrono::Utc>,
272 ) -> AuthResult<()> {
273 let table = match kind {
274 alibi_core::store::OAuthTokenKind::Access => "oauthAccessToken",
275 alibi_core::store::OAuthTokenKind::Refresh => "oauthRefreshToken",
276 };
277 let mut sql = crate::sql::Sql::with(self.exec().engine(), "UPDATE ");
278 sql.ident(table);
279 sql.push(" SET ");
280 sql.assign("revoked", at);
281 sql.push(" WHERE ");
282 sql.compare(table, "id", " = ", id);
283 _ = self.exec().execute(sql).await?;
284 Ok(())
285 }
286 async fn list_oauth_tokens_for_session(
287 &self,
288 kind: alibi_core::store::OAuthTokenKind,
289 session_id: &str,
290 ) -> AuthResult<Vec<alibi_core::store::OAuthToken>> {
291 self.session_oauth_tokens(self.exec(), kind, session_id)
292 .await
293 }
294
295 async fn delete_oauth_token(
296 &self,
297 kind: alibi_core::store::OAuthTokenKind,
298 token: &str,
299 ) -> AuthResult<()> {
300 match kind {
301 alibi_core::store::OAuthTokenKind::Access => {
302 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
303 sql.ident(super::entities::oauth_access_token::Model::TABLE);
304 sql.push(" WHERE ");
305 sql.compare(
306 super::entities::oauth_access_token::Model::TABLE,
307 "token",
308 " = ",
309 token,
310 );
311 _ = self.exec().execute(sql).await?;
312 }
313 alibi_core::store::OAuthTokenKind::Refresh => {
314 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
315 sql.ident(super::entities::oauth_refresh_token::Model::TABLE);
316 sql.push(" WHERE ");
317 sql.compare(
318 super::entities::oauth_refresh_token::Model::TABLE,
319 "token",
320 " = ",
321 token,
322 );
323 _ = self.exec().execute(sql).await?;
324 }
325 }
326 Ok(())
327 }
328 async fn revoke_oauth_refresh_token(
329 &self,
330 token: &str,
331 revoked_at: chrono::DateTime<chrono::Utc>,
332 ) -> AuthResult<bool> {
333 let mut lookup =
334 model::select_model::<super::entities::oauth_refresh_token::Model>(self.exec());
335 lookup.push(" WHERE ");
336 lookup.compare("oauthRefreshToken", "token", " = ", token);
337 let Some(row) = self
338 .exec()
339 .fetch_optional::<super::entities::oauth_refresh_token::Model>(lookup)
340 .await?
341 else {
342 return Ok(false);
343 };
344 if row.revoked.is_some() {
345 return Ok(false);
346 }
347 let mut sql = crate::sql::Sql::with(self.exec().engine(), "UPDATE ");
348 sql.ident("oauthRefreshToken");
349 sql.push(" SET ");
350 sql.assign("revoked", revoked_at);
351 sql.push(" WHERE ");
352 sql.compare("oauthRefreshToken", "token", " = ", token);
353 sql.push(" AND ");
354 sql.ident("revoked");
355 sql.push(" IS NULL");
356 Ok(self.exec().execute(sql).await? > 0)
357 }
358 async fn delete_oauth_access_tokens_for_refresh(&self, refresh_id: &str) -> AuthResult<()> {
359 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
360 sql.ident("oauthAccessToken");
361 sql.push(" WHERE ");
362 sql.compare("oauthAccessToken", "refreshId", " = ", refresh_id);
363 _ = self.exec().execute(sql).await?;
364 Ok(())
365 }
366 async fn delete_oauth_refresh_family(&self, client_id: &str, user_id: &str) -> AuthResult<()> {
367 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
368 sql.ident("oauthAccessToken");
369 sql.push(" WHERE ");
370 sql.ident("refreshId");
371 sql.push(" IN (SELECT id FROM ");
372 sql.ident("oauthRefreshToken");
373 sql.push(" WHERE ");
374 sql.compare("oauthRefreshToken", "clientId", " = ", client_id);
375 sql.push(" AND ");
376 sql.compare("oauthRefreshToken", "userId", " = ", user_id);
377 sql.push(" LIMIT ");
378 sql.bind(
379 i64::try_from(self.config().advanced.database.default_find_many_limit)
380 .unwrap_or(i64::MAX),
381 );
382 sql.push(")");
383 _ = self.exec().execute(sql).await?;
384 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
385 sql.ident("oauthRefreshToken");
386 sql.push(" WHERE ");
387 sql.compare("oauthRefreshToken", "clientId", " = ", client_id);
388 sql.push(" AND ");
389 sql.compare("oauthRefreshToken", "userId", " = ", user_id);
390 _ = self.exec().execute(sql).await?;
391 Ok(())
392 }
393 async fn create_oauth_token(
394 &self,
395 kind: alibi_core::store::OAuthTokenKind,
396 data: alibi_core::store::CreateOAuthToken,
397 ) -> AuthResult<alibi_core::store::OAuthToken> {
398 match kind {
399 alibi_core::store::OAuthTokenKind::Access => {
400 let mut active = ActiveRow::new();
401 active.set(
402 "id",
403 self.generated_id(self.exec(), "oauthAccessToken", "oauthAccessToken", "id")
404 .await?
405 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
406 );
407 active.set("token", data.token);
408 active.set("clientId", data.client_id);
409 active.set("userId", data.user_id);
410 active.set("sessionId", data.session_id);
411 active.set("authorizationCodeId", data.authorization_code_id);
412 active.set("createdAt", data.created_at);
413 active.set("expiresAt", data.expires_at);
414 let fields = super::entities::oauth_access_token::Model::columns_from_metadata(
415 data.metadata,
416 )?
417 .into_active();
418 for (column, value) in fields.present() {
419 if ![
420 "id",
421 "token",
422 "clientId",
423 "sessionId",
424 "userId",
425 "authorizationCodeId",
426 "createdAt",
427 "expiresAt",
428 ]
429 .contains(&column)
430 {
431 active.set(column, value.clone());
432 }
433 }
434 model::insert::<super::entities::oauth_access_token::Model>(self.exec(), &active)
435 .await?
436 .try_into()
437 }
438 alibi_core::store::OAuthTokenKind::Refresh => {
439 let mut active = ActiveRow::new();
440 active.set(
441 "id",
442 self.generated_id(self.exec(), "oauthRefreshToken", "oauthRefreshToken", "id")
443 .await?
444 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
445 );
446 active.set("token", data.token);
447 active.set("clientId", data.client_id);
448 active.set("userId", data.user_id);
449 active.set("sessionId", data.session_id);
450 active.set("authorizationCodeId", data.authorization_code_id);
451 active.set("createdAt", data.created_at);
452 active.set("expiresAt", data.expires_at);
453 let fields = super::entities::oauth_refresh_token::Model::columns_from_metadata(
454 data.metadata,
455 )?
456 .into_active();
457 for (column, value) in fields.present() {
458 if ![
459 "id",
460 "token",
461 "clientId",
462 "sessionId",
463 "userId",
464 "authorizationCodeId",
465 "createdAt",
466 "expiresAt",
467 ]
468 .contains(&column)
469 {
470 active.set(column, value.clone());
471 }
472 }
473 model::insert::<super::entities::oauth_refresh_token::Model>(self.exec(), &active)
474 .await?
475 .try_into()
476 }
477 }
478 }
479 async fn get_oauth_token(
480 &self,
481 kind: alibi_core::store::OAuthTokenKind,
482 token: &str,
483 ) -> AuthResult<Option<alibi_core::store::OAuthToken>> {
484 match kind {
485 alibi_core::store::OAuthTokenKind::Access => {
486 let mut sql =
487 model::select_model::<super::entities::oauth_access_token::Model>(self.exec());
488 sql.push(" WHERE ");
489 sql.compare(
490 super::entities::oauth_access_token::Model::TABLE,
491 "token",
492 " = ",
493 token,
494 );
495 self.exec()
496 .fetch_optional::<super::entities::oauth_access_token::Model>(sql)
497 .await?
498 .map(TryInto::try_into)
499 .transpose()
500 }
501 alibi_core::store::OAuthTokenKind::Refresh => {
502 let mut sql =
503 model::select_model::<super::entities::oauth_refresh_token::Model>(self.exec());
504 sql.push(" WHERE ");
505 sql.compare(
506 super::entities::oauth_refresh_token::Model::TABLE,
507 "token",
508 " = ",
509 token,
510 );
511 self.exec()
512 .fetch_optional::<super::entities::oauth_refresh_token::Model>(sql)
513 .await?
514 .map(TryInto::try_into)
515 .transpose()
516 }
517 }
518 }
519 async fn delete_oauth_tokens_for_code(&self, code: &str) -> AuthResult<()> {
520 {
521 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
522 sql.ident(super::entities::oauth_access_token::Model::TABLE);
523 sql.push(" WHERE ");
524 sql.compare(
525 super::entities::oauth_access_token::Model::TABLE,
526 "authorizationCodeId",
527 " = ",
528 code,
529 );
530 _ = self.exec().execute(sql).await?;
531 }
532 {
533 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
534 sql.ident(super::entities::oauth_refresh_token::Model::TABLE);
535 sql.push(" WHERE ");
536 sql.compare(
537 super::entities::oauth_refresh_token::Model::TABLE,
538 "authorizationCodeId",
539 " = ",
540 code,
541 );
542 _ = self.exec().execute(sql).await?;
543 }
544 Ok(())
545 }
546 async fn delete_oauth_client_resource(
547 &self,
548 client_id: &str,
549 resource_id: &str,
550 ) -> AuthResult<()> {
551 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
552 sql.ident(super::entities::oauth_client_resource::Model::TABLE);
553 sql.push(" WHERE ");
554 sql.compare(
555 super::entities::oauth_client_resource::Model::TABLE,
556 "clientId",
557 " = ",
558 client_id,
559 );
560 sql.push(" AND ");
561 sql.compare(
562 super::entities::oauth_client_resource::Model::TABLE,
563 "resourceId",
564 " = ",
565 resource_id,
566 );
567 self.exec().execute(sql).await.map(|_| ())
568 }
569 async fn create_oauth_client_resource(
570 &self,
571 data: alibi_core::store::CreateOAuthClientResource,
572 ) -> AuthResult<alibi_core::store::OAuthClientResource> {
573 let mut active = ActiveRow::new();
574 active.set(
575 "id",
576 self.generated_id(
577 self.exec(),
578 "oauthClientResource",
579 "oauthClientResource",
580 "id",
581 )
582 .await?
583 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
584 );
585 active.set("clientId", data.client_id);
586 active.set("resourceId", data.resource_id);
587 active.set("metadata", Option::<serde_json::Value>::None);
588 active.set("createdAt", Some(data.created_at));
589 model::insert::<super::entities::oauth_client_resource::Model>(self.exec(), &active)
590 .await?
591 .try_into()
592 }
593 async fn delete_oauth_resource(&self, identifier: &str) -> AuthResult<()> {
594 let mut links = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
595 links.ident(super::entities::oauth_client_resource::Model::TABLE);
596 links.push(" WHERE ");
597 links.compare(
598 super::entities::oauth_client_resource::Model::TABLE,
599 "resourceId",
600 " = ",
601 identifier,
602 );
603 _ = self.exec().execute(links).await?;
604 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
605 sql.ident(super::entities::oauth_resource::Model::TABLE);
606 sql.push(" WHERE ");
607 sql.compare(
608 super::entities::oauth_resource::Model::TABLE,
609 "identifier",
610 " = ",
611 identifier,
612 );
613 self.exec().execute(sql).await.map(|_| ())
614 }
615 async fn update_oauth_resource(
616 &self,
617 identifier: &str,
618 metadata: serde_json::Value,
619 updated_at: chrono::DateTime<chrono::Utc>,
620 ) -> AuthResult<Option<alibi_core::store::OAuthResource>> {
621 let Some(row) = self.get_oauth_resource(identifier).await? else {
622 return Ok(None);
623 };
624 let mut active = ActiveRow::new();
625 active.set("id", row.id);
626 let fields =
627 super::entities::oauth_resource::Model::columns_from_metadata(metadata)?.into_active();
628 for (column, value) in fields.present() {
629 if !["id", "identifier", "createdAt", "updatedAt"].contains(&column) {
630 active.set(column, value.clone());
631 }
632 }
633 active.set("updatedAt", updated_at);
634 model::update::<super::entities::oauth_resource::Model>(self.exec(), &active)
635 .await?
636 .map(TryInto::try_into)
637 .transpose()
638 }
639 async fn list_oauth_resources(&self) -> AuthResult<Vec<alibi_core::store::OAuthResource>> {
640 let mut sql = model::select_model::<super::entities::oauth_resource::Model>(self.exec());
641 sql.push(" LIMIT ");
642 sql.bind(self.find_many_limit());
643 self.exec()
644 .fetch_all::<super::entities::oauth_resource::Model>(sql)
645 .await?
646 .into_iter()
647 .map(TryInto::try_into)
648 .collect()
649 }
650 async fn list_oauth_client_resources(
651 &self,
652 client_id: &str,
653 ) -> AuthResult<Vec<alibi_core::store::OAuthClientResource>> {
654 let mut sql =
655 model::select_model::<super::entities::oauth_client_resource::Model>(self.exec());
656 sql.push(" WHERE ");
657 sql.compare(
658 super::entities::oauth_client_resource::Model::TABLE,
659 "clientId",
660 " = ",
661 client_id,
662 );
663 sql.push(" LIMIT ");
664 sql.bind(self.find_many_limit());
665 self.exec()
666 .fetch_all::<super::entities::oauth_client_resource::Model>(sql)
667 .await?
668 .into_iter()
669 .map(TryInto::try_into)
670 .collect()
671 }
672 async fn create_oauth_resource(
673 &self,
674 data: alibi_core::store::CreateOAuthResource,
675 ) -> AuthResult<alibi_core::store::OAuthResource> {
676 let mut active = ActiveRow::new();
677 active.set(
678 "id",
679 self.generated_id(self.exec(), "oauthResource", "oauthResource", "id")
680 .await?
681 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
682 );
683 active.set("identifier", data.identifier);
684 let fields = super::entities::oauth_resource::Model::columns_from_metadata(data.metadata)?
685 .into_active();
686 for (column, value) in fields.present() {
687 if !["id", "identifier", "createdAt", "updatedAt"].contains(&column) {
688 active.set(column, value.clone());
689 }
690 }
691 active.set("createdAt", data.created_at);
692 active.set("updatedAt", data.created_at);
693 model::insert::<super::entities::oauth_resource::Model>(self.exec(), &active)
694 .await?
695 .try_into()
696 }
697 async fn get_oauth_resource(
698 &self,
699 identifier: &str,
700 ) -> AuthResult<Option<alibi_core::store::OAuthResource>> {
701 let mut sql = model::select_model::<super::entities::oauth_resource::Model>(self.exec());
702 sql.push(" WHERE ");
703 sql.compare(
704 super::entities::oauth_resource::Model::TABLE,
705 "identifier",
706 " = ",
707 identifier,
708 );
709 model::limit_one(&mut sql);
710 self.exec()
711 .fetch_optional::<super::entities::oauth_resource::Model>(sql)
712 .await?
713 .map(TryInto::try_into)
714 .transpose()
715 }
716 async fn create_oauth_consent(
717 &self,
718 data: alibi_core::store::CreateOAuthConsent,
719 ) -> AuthResult<alibi_core::store::OAuthConsent> {
720 let mut active = ActiveRow::new();
721 active.set(
722 "id",
723 self.generated_id(self.exec(), "oauthConsent", "oauthConsent", "id")
724 .await?
725 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
726 );
727 active.set("clientId", data.client_id);
728 active.set("userId", data.user_id);
729 let fields = super::entities::oauth_consent::Model::columns_from_metadata(data.metadata)?
730 .into_active();
731 for (column, value) in fields.present() {
732 if !["id", "clientId", "userId", "createdAt", "updatedAt"].contains(&column) {
733 active.set(column, value.clone());
734 }
735 }
736 active.set("createdAt", data.created_at);
737 active.set("updatedAt", data.created_at);
738 model::insert::<super::entities::oauth_consent::Model>(self.exec(), &active)
739 .await?
740 .try_into()
741 }
742 async fn list_oauth_consents_by_user(
743 &self,
744 user_id: &str,
745 ) -> AuthResult<Vec<alibi_core::store::OAuthConsent>> {
746 let mut sql = model::select_model::<super::entities::oauth_consent::Model>(self.exec());
747 sql.push(" WHERE ");
748 sql.compare(
749 super::entities::oauth_consent::Model::TABLE,
750 "userId",
751 " = ",
752 user_id,
753 );
754 sql.push(" LIMIT ");
755 sql.bind(self.find_many_limit());
756 self.exec()
757 .fetch_all::<super::entities::oauth_consent::Model>(sql)
758 .await?
759 .into_iter()
760 .map(TryInto::try_into)
761 .collect()
762 }
763 async fn update_oauth_consent_metadata(
764 &self,
765 id: &str,
766 metadata: serde_json::Value,
767 updated_at: chrono::DateTime<chrono::Utc>,
768 ) -> AuthResult<Option<alibi_core::store::OAuthConsent>> {
769 let mut active = ActiveRow::new();
770 active.set("id", id.to_owned());
771 let fields =
772 super::entities::oauth_consent::Model::columns_from_metadata(metadata)?.into_active();
773 for (column, value) in fields.present() {
774 if !["id", "clientId", "userId", "createdAt", "updatedAt"].contains(&column) {
775 active.set(column, value.clone());
776 }
777 }
778 active.set("updatedAt", updated_at);
779 model::update::<super::entities::oauth_consent::Model>(self.exec(), &active)
780 .await?
781 .map(TryInto::try_into)
782 .transpose()
783 }
784 async fn create_oauth_client(&self, data: CreateOAuthClient) -> AuthResult<OAuthClient> {
785 let mut active = ActiveRow::new();
786 active.set(
787 "id",
788 self.generated_id(self.exec(), "oauthClient", "oauthClient", "id")
789 .await?
790 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
791 );
792 active.set("clientId", data.client_id);
793 active.set("userId", data.user_id);
794 active.set("clientSecret", data.client_secret);
795 let fields = Model::columns_from_metadata(data.metadata)?.into_active();
796 for (column, value) in fields.present() {
797 if ![
798 "id",
799 "clientId",
800 "userId",
801 "clientSecret",
802 "createdAt",
803 "updatedAt",
804 ]
805 .contains(&column)
806 {
807 active.set(column, value.clone());
808 }
809 }
810 active.set("createdAt", data.created_at);
811 active.set("updatedAt", data.created_at);
812 model::insert::<Model>(self.exec(), &active)
813 .await?
814 .try_into()
815 }
816 async fn get_oauth_consent(
817 &self,
818 id: &str,
819 ) -> AuthResult<Option<alibi_core::store::OAuthConsent>> {
820 let mut sql = model::select_model::<super::entities::oauth_consent::Model>(self.exec());
821 sql.push(" WHERE ");
822 sql.compare(
823 super::entities::oauth_consent::Model::TABLE,
824 "id",
825 " = ",
826 id,
827 );
828 model::limit_one(&mut sql);
829 self.exec()
830 .fetch_optional::<super::entities::oauth_consent::Model>(sql)
831 .await?
832 .map(TryInto::try_into)
833 .transpose()
834 }
835 async fn get_oauth_client(&self, client_id: &str) -> AuthResult<Option<OAuthClient>> {
836 self.oauth_client_with_exec(self.exec(), client_id).await
837 }
838 async fn list_oauth_clients_by_user(&self, user_id: &str) -> AuthResult<Vec<OAuthClient>> {
839 let mut sql = model::select_model::<Model>(self.exec());
840 sql.push(" WHERE ");
841 sql.compare(Model::TABLE, "userId", " = ", user_id);
842 sql.push(" LIMIT ");
843 sql.bind(self.find_many_limit());
844 self.exec()
845 .fetch_all::<Model>(sql)
846 .await?
847 .into_iter()
848 .map(TryInto::try_into)
849 .collect()
850 }
851 async fn delete_oauth_consent(&self, id: &str) -> AuthResult<()> {
852 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
853 sql.ident(super::entities::oauth_consent::Model::TABLE);
854 sql.push(" WHERE ");
855 sql.compare(
856 super::entities::oauth_consent::Model::TABLE,
857 "id",
858 " = ",
859 id,
860 );
861 self.exec().execute(sql).await.map(|_| ())
862 }
863 async fn delete_oauth_client(&self, client_id: &str) -> AuthResult<()> {
864 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
865 sql.ident(Model::TABLE);
866 sql.push(" WHERE ");
867 sql.compare(Model::TABLE, "clientId", " = ", client_id);
868 self.exec().execute(sql).await.map(|_| ())
869 }
870 async fn update_oauth_client_secret(
871 &self,
872 client_id: &str,
873 secret: String,
874 updated_at: chrono::DateTime<chrono::Utc>,
875 ) -> AuthResult<Option<OAuthClient>> {
876 let Some(client) = self.get_oauth_client(client_id).await? else {
877 return Ok(None);
878 };
879 let mut active = ActiveRow::new();
880 active.set("id", client.id);
881 active.set("clientSecret", Some(secret));
882 active.set("updatedAt", updated_at);
883 model::update::<Model>(self.exec(), &active)
884 .await?
885 .map(TryInto::try_into)
886 .transpose()
887 }
888 async fn update_oauth_client_metadata(
889 &self,
890 client_id: &str,
891 metadata: serde_json::Value,
892 updated_at: chrono::DateTime<chrono::Utc>,
893 ) -> AuthResult<Option<OAuthClient>> {
894 let Some(client) = self.get_oauth_client(client_id).await? else {
895 return Ok(None);
896 };
897 let mut active = ActiveRow::new();
898 active.set("id", client.id);
899 let fields = Model::columns_from_metadata(metadata)?.into_active();
900 for (column, value) in fields.present() {
901 if ![
902 "id",
903 "clientId",
904 "userId",
905 "clientSecret",
906 "createdAt",
907 "updatedAt",
908 ]
909 .contains(&column)
910 {
911 active.set(column, value.clone());
912 }
913 }
914 active.set("updatedAt", updated_at);
915 model::update::<Model>(self.exec(), &active)
916 .await?
917 .map(TryInto::try_into)
918 .transpose()
919 }
920}