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;
11#[async_trait]
12impl<S: AuthSchema> OAuthClientStore for SqlxStore<S> {
13 async fn delete_oauth_token(
14 &self,
15 kind: alibi_core::store::OAuthTokenKind,
16 token: &str,
17 ) -> AuthResult<()> {
18 match kind {
19 alibi_core::store::OAuthTokenKind::Access => {
20 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
21 sql.ident(super::entities::oauth_access_token::Model::TABLE);
22 sql.push(" WHERE ");
23 sql.compare(
24 super::entities::oauth_access_token::Model::TABLE,
25 "token",
26 " = ",
27 token,
28 );
29 _ = self.exec().execute(sql).await?;
30 }
31 alibi_core::store::OAuthTokenKind::Refresh => {
32 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
33 sql.ident(super::entities::oauth_refresh_token::Model::TABLE);
34 sql.push(" WHERE ");
35 sql.compare(
36 super::entities::oauth_refresh_token::Model::TABLE,
37 "token",
38 " = ",
39 token,
40 );
41 _ = self.exec().execute(sql).await?;
42 }
43 }
44 Ok(())
45 }
46 async fn revoke_oauth_refresh_token(
47 &self,
48 token: &str,
49 revoked_at: chrono::DateTime<chrono::Utc>,
50 ) -> AuthResult<bool> {
51 let mut lookup =
52 model::select_model::<super::entities::oauth_refresh_token::Model>(self.exec());
53 lookup.push(" WHERE ");
54 lookup.compare("oauthRefreshToken", "token", " = ", token);
55 let Some(row) = self
56 .exec()
57 .fetch_optional::<super::entities::oauth_refresh_token::Model>(lookup)
58 .await?
59 else {
60 return Ok(false);
61 };
62 if row.revoked.is_some() {
63 return Ok(false);
64 }
65 let mut sql = crate::sql::Sql::with(self.exec().engine(), "UPDATE ");
66 sql.ident("oauthRefreshToken");
67 sql.push(" SET ");
68 sql.assign("revoked", revoked_at);
69 sql.push(" WHERE ");
70 sql.compare("oauthRefreshToken", "token", " = ", token);
71 sql.push(" AND ");
72 sql.ident("revoked");
73 sql.push(" IS NULL");
74 Ok(self.exec().execute(sql).await? > 0)
75 }
76 async fn delete_oauth_access_tokens_for_refresh(&self, refresh_id: &str) -> AuthResult<()> {
77 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
78 sql.ident("oauthAccessToken");
79 sql.push(" WHERE ");
80 sql.compare("oauthAccessToken", "refreshId", " = ", refresh_id);
81 _ = self.exec().execute(sql).await?;
82 Ok(())
83 }
84 async fn delete_oauth_refresh_family(&self, client_id: &str, user_id: &str) -> AuthResult<()> {
85 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
86 sql.ident("oauthAccessToken");
87 sql.push(" WHERE ");
88 sql.ident("refreshId");
89 sql.push(" IN (SELECT id FROM ");
90 sql.ident("oauthRefreshToken");
91 sql.push(" WHERE ");
92 sql.compare("oauthRefreshToken", "clientId", " = ", client_id);
93 sql.push(" AND ");
94 sql.compare("oauthRefreshToken", "userId", " = ", user_id);
95 sql.push(" LIMIT ");
96 sql.bind(
97 i64::try_from(self.config().advanced.database.default_find_many_limit)
98 .unwrap_or(i64::MAX),
99 );
100 sql.push(")");
101 _ = self.exec().execute(sql).await?;
102 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
103 sql.ident("oauthRefreshToken");
104 sql.push(" WHERE ");
105 sql.compare("oauthRefreshToken", "clientId", " = ", client_id);
106 sql.push(" AND ");
107 sql.compare("oauthRefreshToken", "userId", " = ", user_id);
108 _ = self.exec().execute(sql).await?;
109 Ok(())
110 }
111 async fn create_oauth_token(
112 &self,
113 kind: alibi_core::store::OAuthTokenKind,
114 data: alibi_core::store::CreateOAuthToken,
115 ) -> AuthResult<alibi_core::store::OAuthToken> {
116 match kind {
117 alibi_core::store::OAuthTokenKind::Access => {
118 let mut active = ActiveRow::new();
119 active.set(
120 "id",
121 self.generated_id(self.exec(), "oauthAccessToken", "oauthAccessToken", "id")
122 .await?
123 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
124 );
125 active.set("token", data.token);
126 active.set("clientId", data.client_id);
127 active.set("userId", data.user_id);
128 active.set("sessionId", data.session_id);
129 active.set("authorizationCodeId", data.authorization_code_id);
130 active.set("createdAt", data.created_at);
131 active.set("expiresAt", data.expires_at);
132 let fields = super::entities::oauth_access_token::Model::columns_from_metadata(
133 data.metadata,
134 )?
135 .into_active();
136 for (column, value) in fields.present() {
137 if ![
138 "id",
139 "token",
140 "clientId",
141 "sessionId",
142 "userId",
143 "authorizationCodeId",
144 "createdAt",
145 "expiresAt",
146 ]
147 .contains(&column)
148 {
149 active.set(column, value.clone());
150 }
151 }
152 model::insert::<super::entities::oauth_access_token::Model>(self.exec(), &active)
153 .await?
154 .try_into()
155 }
156 alibi_core::store::OAuthTokenKind::Refresh => {
157 let mut active = ActiveRow::new();
158 active.set(
159 "id",
160 self.generated_id(self.exec(), "oauthRefreshToken", "oauthRefreshToken", "id")
161 .await?
162 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
163 );
164 active.set("token", data.token);
165 active.set("clientId", data.client_id);
166 active.set("userId", data.user_id);
167 active.set("sessionId", data.session_id);
168 active.set("authorizationCodeId", data.authorization_code_id);
169 active.set("createdAt", data.created_at);
170 active.set("expiresAt", data.expires_at);
171 let fields = super::entities::oauth_refresh_token::Model::columns_from_metadata(
172 data.metadata,
173 )?
174 .into_active();
175 for (column, value) in fields.present() {
176 if ![
177 "id",
178 "token",
179 "clientId",
180 "sessionId",
181 "userId",
182 "authorizationCodeId",
183 "createdAt",
184 "expiresAt",
185 ]
186 .contains(&column)
187 {
188 active.set(column, value.clone());
189 }
190 }
191 model::insert::<super::entities::oauth_refresh_token::Model>(self.exec(), &active)
192 .await?
193 .try_into()
194 }
195 }
196 }
197 async fn get_oauth_token(
198 &self,
199 kind: alibi_core::store::OAuthTokenKind,
200 token: &str,
201 ) -> AuthResult<Option<alibi_core::store::OAuthToken>> {
202 match kind {
203 alibi_core::store::OAuthTokenKind::Access => {
204 let mut sql =
205 model::select_model::<super::entities::oauth_access_token::Model>(self.exec());
206 sql.push(" WHERE ");
207 sql.compare(
208 super::entities::oauth_access_token::Model::TABLE,
209 "token",
210 " = ",
211 token,
212 );
213 self.exec()
214 .fetch_optional::<super::entities::oauth_access_token::Model>(sql)
215 .await?
216 .map(TryInto::try_into)
217 .transpose()
218 }
219 alibi_core::store::OAuthTokenKind::Refresh => {
220 let mut sql =
221 model::select_model::<super::entities::oauth_refresh_token::Model>(self.exec());
222 sql.push(" WHERE ");
223 sql.compare(
224 super::entities::oauth_refresh_token::Model::TABLE,
225 "token",
226 " = ",
227 token,
228 );
229 self.exec()
230 .fetch_optional::<super::entities::oauth_refresh_token::Model>(sql)
231 .await?
232 .map(TryInto::try_into)
233 .transpose()
234 }
235 }
236 }
237 async fn delete_oauth_tokens_for_code(&self, code: &str) -> AuthResult<()> {
238 {
239 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
240 sql.ident(super::entities::oauth_access_token::Model::TABLE);
241 sql.push(" WHERE ");
242 sql.compare(
243 super::entities::oauth_access_token::Model::TABLE,
244 "authorizationCodeId",
245 " = ",
246 code,
247 );
248 _ = self.exec().execute(sql).await?;
249 }
250 {
251 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
252 sql.ident(super::entities::oauth_refresh_token::Model::TABLE);
253 sql.push(" WHERE ");
254 sql.compare(
255 super::entities::oauth_refresh_token::Model::TABLE,
256 "authorizationCodeId",
257 " = ",
258 code,
259 );
260 _ = self.exec().execute(sql).await?;
261 }
262 Ok(())
263 }
264 async fn delete_oauth_client_resource(
265 &self,
266 client_id: &str,
267 resource_id: &str,
268 ) -> AuthResult<()> {
269 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
270 sql.ident(super::entities::oauth_client_resource::Model::TABLE);
271 sql.push(" WHERE ");
272 sql.compare(
273 super::entities::oauth_client_resource::Model::TABLE,
274 "clientId",
275 " = ",
276 client_id,
277 );
278 sql.push(" AND ");
279 sql.compare(
280 super::entities::oauth_client_resource::Model::TABLE,
281 "resourceId",
282 " = ",
283 resource_id,
284 );
285 self.exec().execute(sql).await.map(|_| ())
286 }
287 async fn create_oauth_client_resource(
288 &self,
289 data: alibi_core::store::CreateOAuthClientResource,
290 ) -> AuthResult<alibi_core::store::OAuthClientResource> {
291 let mut active = ActiveRow::new();
292 active.set(
293 "id",
294 self.generated_id(
295 self.exec(),
296 "oauthClientResource",
297 "oauthClientResource",
298 "id",
299 )
300 .await?
301 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
302 );
303 active.set("clientId", data.client_id);
304 active.set("resourceId", data.resource_id);
305 active.set("metadata", Option::<String>::None);
306 active.set("createdAt", Some(data.created_at));
307 model::insert::<super::entities::oauth_client_resource::Model>(self.exec(), &active)
308 .await?
309 .try_into()
310 }
311 async fn delete_oauth_resource(&self, identifier: &str) -> AuthResult<()> {
312 let mut links = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
313 links.ident(super::entities::oauth_client_resource::Model::TABLE);
314 links.push(" WHERE ");
315 links.compare(
316 super::entities::oauth_client_resource::Model::TABLE,
317 "resourceId",
318 " = ",
319 identifier,
320 );
321 _ = self.exec().execute(links).await?;
322 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
323 sql.ident(super::entities::oauth_resource::Model::TABLE);
324 sql.push(" WHERE ");
325 sql.compare(
326 super::entities::oauth_resource::Model::TABLE,
327 "identifier",
328 " = ",
329 identifier,
330 );
331 self.exec().execute(sql).await.map(|_| ())
332 }
333 async fn update_oauth_resource(
334 &self,
335 identifier: &str,
336 metadata: serde_json::Value,
337 updated_at: chrono::DateTime<chrono::Utc>,
338 ) -> AuthResult<Option<alibi_core::store::OAuthResource>> {
339 let Some(row) = self.get_oauth_resource(identifier).await? else {
340 return Ok(None);
341 };
342 let mut active = ActiveRow::new();
343 active.set("id", row.id);
344 let fields =
345 super::entities::oauth_resource::Model::columns_from_metadata(metadata)?.into_active();
346 for (column, value) in fields.present() {
347 if !["id", "identifier", "createdAt", "updatedAt"].contains(&column) {
348 active.set(column, value.clone());
349 }
350 }
351 active.set("updatedAt", updated_at);
352 model::update::<super::entities::oauth_resource::Model>(self.exec(), &active)
353 .await?
354 .map(TryInto::try_into)
355 .transpose()
356 }
357 async fn list_oauth_resources(&self) -> AuthResult<Vec<alibi_core::store::OAuthResource>> {
358 let mut sql = model::select_model::<super::entities::oauth_resource::Model>(self.exec());
359 sql.push(" LIMIT ");
360 sql.bind(self.find_many_limit());
361 self.exec()
362 .fetch_all::<super::entities::oauth_resource::Model>(sql)
363 .await?
364 .into_iter()
365 .map(TryInto::try_into)
366 .collect()
367 }
368 async fn list_oauth_client_resources(
369 &self,
370 client_id: &str,
371 ) -> AuthResult<Vec<alibi_core::store::OAuthClientResource>> {
372 let mut sql =
373 model::select_model::<super::entities::oauth_client_resource::Model>(self.exec());
374 sql.push(" WHERE ");
375 sql.compare(
376 super::entities::oauth_client_resource::Model::TABLE,
377 "clientId",
378 " = ",
379 client_id,
380 );
381 sql.push(" LIMIT ");
382 sql.bind(self.find_many_limit());
383 self.exec()
384 .fetch_all::<super::entities::oauth_client_resource::Model>(sql)
385 .await?
386 .into_iter()
387 .map(TryInto::try_into)
388 .collect()
389 }
390 async fn create_oauth_resource(
391 &self,
392 data: alibi_core::store::CreateOAuthResource,
393 ) -> AuthResult<alibi_core::store::OAuthResource> {
394 let mut active = ActiveRow::new();
395 active.set(
396 "id",
397 self.generated_id(self.exec(), "oauthResource", "oauthResource", "id")
398 .await?
399 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
400 );
401 active.set("identifier", data.identifier);
402 let fields = super::entities::oauth_resource::Model::columns_from_metadata(data.metadata)?
403 .into_active();
404 for (column, value) in fields.present() {
405 if !["id", "identifier", "createdAt", "updatedAt"].contains(&column) {
406 active.set(column, value.clone());
407 }
408 }
409 active.set("createdAt", data.created_at);
410 active.set("updatedAt", data.created_at);
411 model::insert::<super::entities::oauth_resource::Model>(self.exec(), &active)
412 .await?
413 .try_into()
414 }
415 async fn get_oauth_resource(
416 &self,
417 identifier: &str,
418 ) -> AuthResult<Option<alibi_core::store::OAuthResource>> {
419 let mut sql = model::select_model::<super::entities::oauth_resource::Model>(self.exec());
420 sql.push(" WHERE ");
421 sql.compare(
422 super::entities::oauth_resource::Model::TABLE,
423 "identifier",
424 " = ",
425 identifier,
426 );
427 model::limit_one(&mut sql);
428 self.exec()
429 .fetch_optional::<super::entities::oauth_resource::Model>(sql)
430 .await?
431 .map(TryInto::try_into)
432 .transpose()
433 }
434 async fn create_oauth_consent(
435 &self,
436 data: alibi_core::store::CreateOAuthConsent,
437 ) -> AuthResult<alibi_core::store::OAuthConsent> {
438 let mut active = ActiveRow::new();
439 active.set(
440 "id",
441 self.generated_id(self.exec(), "oauthConsent", "oauthConsent", "id")
442 .await?
443 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
444 );
445 active.set("clientId", data.client_id);
446 active.set("userId", data.user_id);
447 let fields = super::entities::oauth_consent::Model::columns_from_metadata(data.metadata)?
448 .into_active();
449 for (column, value) in fields.present() {
450 if !["id", "clientId", "userId", "createdAt", "updatedAt"].contains(&column) {
451 active.set(column, value.clone());
452 }
453 }
454 active.set("createdAt", data.created_at);
455 active.set("updatedAt", data.created_at);
456 model::insert::<super::entities::oauth_consent::Model>(self.exec(), &active)
457 .await?
458 .try_into()
459 }
460 async fn list_oauth_consents_by_user(
461 &self,
462 user_id: &str,
463 ) -> AuthResult<Vec<alibi_core::store::OAuthConsent>> {
464 let mut sql = model::select_model::<super::entities::oauth_consent::Model>(self.exec());
465 sql.push(" WHERE ");
466 sql.compare(
467 super::entities::oauth_consent::Model::TABLE,
468 "userId",
469 " = ",
470 user_id,
471 );
472 sql.push(" LIMIT ");
473 sql.bind(self.find_many_limit());
474 self.exec()
475 .fetch_all::<super::entities::oauth_consent::Model>(sql)
476 .await?
477 .into_iter()
478 .map(TryInto::try_into)
479 .collect()
480 }
481 async fn update_oauth_consent_metadata(
482 &self,
483 id: &str,
484 metadata: serde_json::Value,
485 updated_at: chrono::DateTime<chrono::Utc>,
486 ) -> AuthResult<Option<alibi_core::store::OAuthConsent>> {
487 let mut active = ActiveRow::new();
488 active.set("id", id.to_owned());
489 let fields =
490 super::entities::oauth_consent::Model::columns_from_metadata(metadata)?.into_active();
491 for (column, value) in fields.present() {
492 if !["id", "clientId", "userId", "createdAt", "updatedAt"].contains(&column) {
493 active.set(column, value.clone());
494 }
495 }
496 active.set("updatedAt", updated_at);
497 model::update::<super::entities::oauth_consent::Model>(self.exec(), &active)
498 .await?
499 .map(TryInto::try_into)
500 .transpose()
501 }
502 async fn create_oauth_client(&self, data: CreateOAuthClient) -> AuthResult<OAuthClient> {
503 let mut active = ActiveRow::new();
504 active.set(
505 "id",
506 self.generated_id(self.exec(), "oauthClient", "oauthClient", "id")
507 .await?
508 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
509 );
510 active.set("clientId", data.client_id);
511 active.set("userId", data.user_id);
512 active.set("clientSecret", data.client_secret);
513 let fields = Model::columns_from_metadata(data.metadata)?.into_active();
514 for (column, value) in fields.present() {
515 if ![
516 "id",
517 "clientId",
518 "userId",
519 "clientSecret",
520 "createdAt",
521 "updatedAt",
522 ]
523 .contains(&column)
524 {
525 active.set(column, value.clone());
526 }
527 }
528 active.set("createdAt", data.created_at);
529 active.set("updatedAt", data.created_at);
530 model::insert::<Model>(self.exec(), &active)
531 .await?
532 .try_into()
533 }
534 async fn get_oauth_consent(
535 &self,
536 id: &str,
537 ) -> AuthResult<Option<alibi_core::store::OAuthConsent>> {
538 let mut sql = model::select_model::<super::entities::oauth_consent::Model>(self.exec());
539 sql.push(" WHERE ");
540 sql.compare(
541 super::entities::oauth_consent::Model::TABLE,
542 "id",
543 " = ",
544 id,
545 );
546 model::limit_one(&mut sql);
547 self.exec()
548 .fetch_optional::<super::entities::oauth_consent::Model>(sql)
549 .await?
550 .map(TryInto::try_into)
551 .transpose()
552 }
553 async fn get_oauth_client(&self, client_id: &str) -> AuthResult<Option<OAuthClient>> {
554 let mut sql = model::select_model::<Model>(self.exec());
555 sql.push(" WHERE ");
556 sql.compare(Model::TABLE, "clientId", " = ", client_id);
557 model::limit_one(&mut sql);
558 self.exec()
559 .fetch_optional::<Model>(sql)
560 .await?
561 .map(TryInto::try_into)
562 .transpose()
563 }
564 async fn list_oauth_clients_by_user(&self, user_id: &str) -> AuthResult<Vec<OAuthClient>> {
565 let mut sql = model::select_model::<Model>(self.exec());
566 sql.push(" WHERE ");
567 sql.compare(Model::TABLE, "userId", " = ", user_id);
568 sql.push(" LIMIT ");
569 sql.bind(self.find_many_limit());
570 self.exec()
571 .fetch_all::<Model>(sql)
572 .await?
573 .into_iter()
574 .map(TryInto::try_into)
575 .collect()
576 }
577 async fn delete_oauth_consent(&self, id: &str) -> AuthResult<()> {
578 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
579 sql.ident(super::entities::oauth_consent::Model::TABLE);
580 sql.push(" WHERE ");
581 sql.compare(
582 super::entities::oauth_consent::Model::TABLE,
583 "id",
584 " = ",
585 id,
586 );
587 self.exec().execute(sql).await.map(|_| ())
588 }
589 async fn delete_oauth_client(&self, client_id: &str) -> AuthResult<()> {
590 let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
591 sql.ident(Model::TABLE);
592 sql.push(" WHERE ");
593 sql.compare(Model::TABLE, "clientId", " = ", client_id);
594 self.exec().execute(sql).await.map(|_| ())
595 }
596 async fn update_oauth_client_secret(
597 &self,
598 client_id: &str,
599 secret: String,
600 updated_at: chrono::DateTime<chrono::Utc>,
601 ) -> AuthResult<Option<OAuthClient>> {
602 let Some(client) = self.get_oauth_client(client_id).await? else {
603 return Ok(None);
604 };
605 let mut active = ActiveRow::new();
606 active.set("id", client.id);
607 active.set("clientSecret", Some(secret));
608 active.set("updatedAt", updated_at);
609 model::update::<Model>(self.exec(), &active)
610 .await?
611 .map(TryInto::try_into)
612 .transpose()
613 }
614 async fn update_oauth_client_metadata(
615 &self,
616 client_id: &str,
617 metadata: serde_json::Value,
618 updated_at: chrono::DateTime<chrono::Utc>,
619 ) -> AuthResult<Option<OAuthClient>> {
620 let Some(client) = self.get_oauth_client(client_id).await? else {
621 return Ok(None);
622 };
623 let mut active = ActiveRow::new();
624 active.set("id", client.id);
625 let fields = Model::columns_from_metadata(metadata)?.into_active();
626 for (column, value) in fields.present() {
627 if ![
628 "id",
629 "clientId",
630 "userId",
631 "clientSecret",
632 "createdAt",
633 "updatedAt",
634 ]
635 .contains(&column)
636 {
637 active.set(column, value.clone());
638 }
639 }
640 active.set("updatedAt", updated_at);
641 model::update::<Model>(self.exec(), &active)
642 .await?
643 .map(TryInto::try_into)
644 .transpose()
645 }
646}