diff --git a/rfd-api/src/context.rs b/rfd-api/src/context.rs index cf98e86..624b0fc 100644 --- a/rfd-api/src/context.rs +++ b/rfd-api/src/context.rs @@ -11,12 +11,14 @@ use rfd_model::{ storage::{ AccessTokenStore, ApiKeyFilter, ApiKeyStore, ApiUserFilter, ApiUserProviderFilter, ApiUserProviderStore, ApiUserStore, JobStore, ListPagination, LoginAttemptFilter, - LoginAttemptStore, RfdFilter, RfdPdfFilter, RfdPdfStore, RfdRevisionFilter, - RfdRevisionStore, RfdStore, StoreError, OAuthClientStore, OAuthClientSecretStore, OAuthClientRedirectUriStore, + LoginAttemptStore, OAuthClientFilter, OAuthClientRedirectUriStore, OAuthClientSecretStore, + OAuthClientStore, RfdFilter, RfdPdfFilter, RfdPdfStore, RfdRevisionFilter, + RfdRevisionStore, RfdStore, StoreError, }, AccessToken, ApiUser, ApiUserProvider, InvalidValueError, Job, LoginAttempt, NewAccessToken, - NewApiKey, NewApiUser, NewApiUserProvider, NewJob, NewLoginAttempt, OAuthClient, OAuthClientRedirectUri, - NewOAuthClient, NewOAuthClientSecret, NewOAuthClientRedirectUri, OAuthClientSecret + NewApiKey, NewApiUser, NewApiUserProvider, NewJob, NewLoginAttempt, NewOAuthClient, + NewOAuthClientRedirectUri, NewOAuthClientSecret, OAuthClient, OAuthClientRedirectUri, + OAuthClientSecret, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -641,36 +643,65 @@ impl ApiContext { } pub async fn create_oauth_client(&self) -> Result { - OAuthClientStore::upsert(&*self.storage, NewOAuthClient { - id: Uuid::new_v4(), - }).await + OAuthClientStore::upsert(&*self.storage, NewOAuthClient { id: Uuid::new_v4() }).await } - pub async fn create_oauth_secret(&self, client_id: &Uuid) -> Result { - let secret = String::new(); + pub async fn get_oauth_client(&self, id: &Uuid) -> Result, StoreError> { + OAuthClientStore::get(&*self.storage, id, false).await + } - OAuthClientSecretStore::upsert(&*self.storage, NewOAuthClientSecret { - id: Uuid::new_v4(), - oauth_client_id: *client_id, - secret: secret.clone(), - }).await?; + pub async fn list_oauth_clients(&self) -> Result, StoreError> { + OAuthClientStore::list( + &*self.storage, + OAuthClientFilter::default(), + &ListPagination::default(), + ) + .await + } - Ok(secret) + pub async fn add_oauth_secret( + &self, + client_id: &Uuid, + secret: &str, + ) -> Result { + OAuthClientSecretStore::upsert( + &*self.storage, + NewOAuthClientSecret { + id: Uuid::new_v4(), + oauth_client_id: *client_id, + secret: secret.to_string(), + }, + ) + .await } - pub async fn delete_oauth_secret(&self, id: &Uuid) -> Result, StoreError> { + pub async fn delete_oauth_secret( + &self, + id: &Uuid, + ) -> Result, StoreError> { OAuthClientSecretStore::delete(&*self.storage, id).await } - pub async fn add_oauth_rediret_uri(&self, client_id: &Uuid, uri: &str) -> Result { - OAuthClientRedirectUriStore::upsert(&*self.storage, NewOAuthClientRedirectUri { - id: Uuid::new_v4(), - oauth_client_id: *client_id, - redirect_uri: uri.to_string() - }).await + pub async fn add_oauth_redirect_uri( + &self, + client_id: &Uuid, + uri: &str, + ) -> Result { + OAuthClientRedirectUriStore::upsert( + &*self.storage, + NewOAuthClientRedirectUri { + id: Uuid::new_v4(), + oauth_client_id: *client_id, + redirect_uri: uri.to_string(), + }, + ) + .await } - pub async fn delete_oauth_rediret_uri(&self, id: &Uuid) -> Result, StoreError> { + pub async fn delete_oauth_redirect_uri( + &self, + id: &Uuid, + ) -> Result, StoreError> { OAuthClientRedirectUriStore::delete(&*self.storage, id).await } } @@ -684,8 +715,9 @@ pub(crate) mod tests { AccessTokenStore, ApiKeyStore, ApiUserProviderStore, ApiUserStore, JobStore, ListPagination, LoginAttemptStore, MockAccessTokenStore, MockApiKeyStore, MockApiUserProviderStore, MockApiUserStore, MockJobStore, MockLoginAttemptStore, - MockRfdPdfStore, MockRfdRevisionStore, MockRfdStore, RfdPdfStore, RfdRevisionStore, - RfdStore, MockOAuthClientStore, MockOAuthClientSecretStore, MockOAuthClientRedirectUriStore, OAuthClientStore, OAuthClientRedirectUriStore, OAuthClientSecretStore, + MockOAuthClientRedirectUriStore, MockOAuthClientSecretStore, MockOAuthClientStore, + MockRfdPdfStore, MockRfdRevisionStore, MockRfdStore, OAuthClientRedirectUriStore, + OAuthClientSecretStore, OAuthClientStore, RfdPdfStore, RfdRevisionStore, RfdStore, }, ApiKey, ApiUserProvider, NewAccessToken, NewApiKey, NewApiUser, NewApiUserProvider, NewJob, NewLoginAttempt, NewRfd, NewRfdPdf, NewRfdRevision, @@ -1094,8 +1126,16 @@ pub(crate) mod tests { #[async_trait] impl OAuthClientStore for MockStorage { - async fn get(&self, id: &uuid::Uuid, deleted: bool) -> Result, rfd_model::storage::StoreError> { - self.oauth_client_store.as_ref().unwrap().get(id, deleted).await + async fn get( + &self, + id: &uuid::Uuid, + deleted: bool, + ) -> Result, rfd_model::storage::StoreError> { + self.oauth_client_store + .as_ref() + .unwrap() + .get(id, deleted) + .await } async fn list( @@ -1103,40 +1143,80 @@ pub(crate) mod tests { filter: rfd_model::storage::OAuthClientFilter, pagination: &ListPagination, ) -> Result, rfd_model::storage::StoreError> { - self.oauth_client_store.as_ref().unwrap().list(filter, pagination).await + self.oauth_client_store + .as_ref() + .unwrap() + .list(filter, pagination) + .await } async fn upsert( &self, client: rfd_model::NewOAuthClient, ) -> Result { - self.oauth_client_store.as_ref().unwrap().upsert(client).await + self.oauth_client_store + .as_ref() + .unwrap() + .upsert(client) + .await } - async fn delete(&self, id: &uuid::Uuid) -> Result, rfd_model::storage::StoreError> { + async fn delete( + &self, + id: &uuid::Uuid, + ) -> Result, rfd_model::storage::StoreError> { self.oauth_client_store.as_ref().unwrap().delete(id).await } } #[async_trait] impl OAuthClientSecretStore for MockStorage { - async fn upsert(&self, secret: rfd_model::NewOAuthClientSecret) -> Result { - self.oauth_client_secret_store.as_ref().unwrap().upsert(secret).await + async fn upsert( + &self, + secret: rfd_model::NewOAuthClientSecret, + ) -> Result { + self.oauth_client_secret_store + .as_ref() + .unwrap() + .upsert(secret) + .await } - async fn delete(&self, id: &uuid::Uuid) -> Result, rfd_model::storage::StoreError> { - self.oauth_client_secret_store.as_ref().unwrap().delete(id).await + async fn delete( + &self, + id: &uuid::Uuid, + ) -> Result, rfd_model::storage::StoreError> { + self.oauth_client_secret_store + .as_ref() + .unwrap() + .delete(id) + .await } } #[async_trait] impl OAuthClientRedirectUriStore for MockStorage { - async fn upsert(&self, redirect_uri: rfd_model::NewOAuthClientRedirectUri) -> Result { - self.oauth_client_redirect_uri_store.as_ref().unwrap().upsert(redirect_uri).await + async fn upsert( + &self, + redirect_uri: rfd_model::NewOAuthClientRedirectUri, + ) -> Result { + self.oauth_client_redirect_uri_store + .as_ref() + .unwrap() + .upsert(redirect_uri) + .await } - async fn delete(&self, id: &uuid::Uuid) -> Result, rfd_model::storage::StoreError> { - self.oauth_client_redirect_uri_store.as_ref().unwrap().delete(id).await + async fn delete( + &self, + id: &uuid::Uuid, + ) -> Result, rfd_model::storage::StoreError> + { + self.oauth_client_redirect_uri_store + .as_ref() + .unwrap() + .delete(id) + .await } } } diff --git a/rfd-api/src/endpoints/login/oauth/client.rs b/rfd-api/src/endpoints/login/oauth/client.rs index 8a72caa..2f3687a 100644 --- a/rfd-api/src/endpoints/login/oauth/client.rs +++ b/rfd-api/src/endpoints/login/oauth/client.rs @@ -1,11 +1,44 @@ -use dropshot::{endpoint, RequestContext, HttpError, HttpResponseOk, Path, TypedBody}; -use rfd_model::{OAuthClient, OAuthClientSecret}; +use dropshot::{endpoint, HttpError, HttpResponseOk, Path, RequestContext, TypedBody}; +use http::StatusCode; +use rfd_model::{OAuthClient, OAuthClientRedirectUri, OAuthClientSecret}; use schemars::JsonSchema; use serde::Deserialize; use tracing::instrument; use uuid::Uuid; -use crate::{context::ApiContext, ApiCaller}; +use crate::{ + context::ApiContext, error::ApiError, permissions::ApiPermission, util::response::client_error, + ApiCaller, +}; + +/// List OAuth clients +#[endpoint { + method = GET, + path = "/oauth/client" +}] +#[instrument(skip(rqctx), fields(request_id = rqctx.request_id), err(Debug))] +pub async fn list_oauth_clients( + rqctx: RequestContext, +) -> Result>, HttpError> { + let ctx = rqctx.context(); + let auth = ctx.authn_token(&rqctx).await?; + let caller = ctx.get_caller(&auth).await?; + list_oauth_clients_op(ctx, &caller).await +} + +#[instrument(skip(ctx, caller), fields(caller = ?caller.id), err(Debug))] +async fn list_oauth_clients_op( + ctx: &ApiContext, + caller: &ApiCaller, +) -> Result>, HttpError> { + if caller.can(&ApiPermission::GetOAuthClientAll) { + Ok(HttpResponseOk( + ctx.list_oauth_clients().await.map_err(ApiError::Storage)?, + )) + } else { + Err(client_error(StatusCode::FORBIDDEN, "Unauthorized")) + } +} /// Create a new OAuth Client #[endpoint { @@ -27,37 +60,54 @@ async fn create_oauth_client_op( ctx: &ApiContext, caller: &ApiCaller, ) -> Result, HttpError> { - unimplemented!() + if caller.can(&ApiPermission::CreateOAuthClient) { + Ok(HttpResponseOk( + ctx.create_oauth_client().await.map_err(ApiError::Storage)?, + )) + } else { + Err(client_error(StatusCode::FORBIDDEN, "Unauthorized")) + } } #[derive(Debug, Clone, Deserialize, JsonSchema)] -pub struct DeleteOAuthClientPath { +pub struct GetOAuthClientPath { pub client_id: Uuid, } -/// Delete a OAuth Client +/// Get an new OAuth Client #[endpoint { - method = DELETE, + method = GET, path = "/oauth/client/{client_id}" }] #[instrument(skip(rqctx), fields(request_id = rqctx.request_id), err(Debug))] -pub async fn delete_oauth_client( +pub async fn get_oauth_client( rqctx: RequestContext, - path: Path -) -> Result, HttpError> { + path: Path, +) -> Result>, HttpError> { let ctx = rqctx.context(); let auth = ctx.authn_token(&rqctx).await?; let caller = ctx.get_caller(&auth).await?; - delete_oauth_client_op(ctx, &caller, &path.into_inner()).await + get_oauth_client_op(ctx, &caller, &path.into_inner()).await } #[instrument(skip(ctx, caller), fields(caller = ?caller.id), err(Debug))] -async fn delete_oauth_client_op( +async fn get_oauth_client_op( ctx: &ApiContext, caller: &ApiCaller, - path: &DeleteOAuthClientPath, -) -> Result, HttpError> { - unimplemented!() + path: &GetOAuthClientPath, +) -> Result>, HttpError> { + if caller.any(&[ + &ApiPermission::GetOAuthClientAll, + &ApiPermission::GetOAuthClient(path.client_id), + ]) { + Ok(HttpResponseOk( + ctx.get_oauth_client(&path.client_id) + .await + .map_err(ApiError::Storage)?, + )) + } else { + Err(client_error(StatusCode::FORBIDDEN, "Unauthorized")) + } } #[derive(Debug, Clone, Deserialize, JsonSchema)] @@ -73,7 +123,7 @@ pub struct AddOAuthClientSecretPath { #[instrument(skip(rqctx), fields(request_id = rqctx.request_id), err(Debug))] pub async fn create_oauth_client_secret( rqctx: RequestContext, - path: Path + path: Path, ) -> Result, HttpError> { let ctx = rqctx.context(); let auth = ctx.authn_token(&rqctx).await?; @@ -87,7 +137,18 @@ async fn create_oauth_client_secret_op( caller: &ApiCaller, path: &AddOAuthClientSecretPath, ) -> Result, HttpError> { - unimplemented!() + if caller.can(&ApiPermission::CreateOAuthClientSecret(path.client_id)) { + let secret = String::new(); + let mut client_secret = ctx + .add_oauth_secret(&path.client_id, &secret) + .await + .map_err(ApiError::Storage)?; + client_secret.secret = secret; + + Ok(HttpResponseOk(client_secret)) + } else { + Err(client_error(StatusCode::FORBIDDEN, "Unauthorized")) + } } #[derive(Debug, Clone, Deserialize, JsonSchema)] @@ -98,14 +159,14 @@ pub struct DeleteOAuthClientSecretPath { /// Delete an OAuth client secret #[endpoint { - method = POST, + method = DELETE, path = "/oauth/client/{client_id}/secret/{secret_id}" }] #[instrument(skip(rqctx), fields(request_id = rqctx.request_id), err(Debug))] pub async fn delete_oauth_client_secret( rqctx: RequestContext, - path: Path -) -> Result, HttpError> { + path: Path, +) -> Result>, HttpError> { let ctx = rqctx.context(); let auth = ctx.authn_token(&rqctx).await?; let caller = ctx.get_caller(&auth).await?; @@ -117,8 +178,16 @@ async fn delete_oauth_client_secret_op( ctx: &ApiContext, caller: &ApiCaller, path: &DeleteOAuthClientSecretPath, -) -> Result, HttpError> { - unimplemented!() +) -> Result>, HttpError> { + if caller.can(&ApiPermission::DeleteOAuthClientSecret(path.secret_id)) { + Ok(HttpResponseOk( + ctx.delete_oauth_secret(&path.secret_id) + .await + .map_err(ApiError::Storage)?, + )) + } else { + Err(client_error(StatusCode::FORBIDDEN, "Unauthorized")) + } } #[derive(Debug, Clone, Deserialize, JsonSchema)] @@ -141,11 +210,11 @@ pub async fn create_oauth_client_redirect_uri( rqctx: RequestContext, path: Path, body: TypedBody, -) -> Result, HttpError> { +) -> Result, HttpError> { let ctx = rqctx.context(); let auth = ctx.authn_token(&rqctx).await?; let caller = ctx.get_caller(&auth).await?; - create_oauth_client_redirect_uri_op(ctx, &caller, &path.into_inner()).await + create_oauth_client_redirect_uri_op(ctx, &caller, &path.into_inner(), body.into_inner()).await } #[instrument(skip(ctx, caller), fields(caller = ?caller.id), err(Debug))] @@ -153,26 +222,35 @@ async fn create_oauth_client_redirect_uri_op( ctx: &ApiContext, caller: &ApiCaller, path: &AddOAuthClientRedirectPath, -) -> Result, HttpError> { - unimplemented!() + body: AddOAuthClientRedirectBody, +) -> Result, HttpError> { + if caller.can(&ApiPermission::CreateOAuthClientRedirectUri(path.client_id)) { + Ok(HttpResponseOk( + ctx.add_oauth_redirect_uri(&path.client_id, &body.redirect_uri) + .await + .map_err(ApiError::Storage)?, + )) + } else { + Err(client_error(StatusCode::FORBIDDEN, "Unauthorized")) + } } #[derive(Debug, Clone, Deserialize, JsonSchema)] -pub struct DeleteOAuthClientRediretPath { +pub struct DeleteOAuthClientRedirectPath { pub client_id: Uuid, pub redirect_uri_id: Uuid, } -/// Delete an OAuth client secret +/// Delete an OAuth client redirect uri #[endpoint { - method = POST, + method = DELETE, path = "/oauth/client/{client_id}/redirect_uri/{redirect_uri_id}" }] #[instrument(skip(rqctx), fields(request_id = rqctx.request_id), err(Debug))] pub async fn delete_oauth_client_redirect_uri( rqctx: RequestContext, - path: Path -) -> Result, HttpError> { + path: Path, +) -> Result>, HttpError> { let ctx = rqctx.context(); let auth = ctx.authn_token(&rqctx).await?; let caller = ctx.get_caller(&auth).await?; @@ -183,8 +261,15 @@ pub async fn delete_oauth_client_redirect_uri( async fn delete_oauth_client_redirect_uri_op( ctx: &ApiContext, caller: &ApiCaller, - path: &DeleteOAuthClientSecretPath, -) -> Result, HttpError> { - unimplemented!() + path: &DeleteOAuthClientRedirectPath, +) -> Result>, HttpError> { + if caller.can(&ApiPermission::DeleteOAuthClientRedirectUri(path.client_id)) { + Ok(HttpResponseOk( + ctx.delete_oauth_redirect_uri(&path.redirect_uri_id) + .await + .map_err(ApiError::Storage)?, + )) + } else { + Err(client_error(StatusCode::FORBIDDEN, "Unauthorized")) + } } - diff --git a/rfd-api/src/permissions.rs b/rfd-api/src/permissions.rs index b239dd9..5bdf35f 100644 --- a/rfd-api/src/permissions.rs +++ b/rfd-api/src/permissions.rs @@ -21,6 +21,7 @@ impl ExpandPermission for Permissions { #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] pub enum ApiPermission { + // User information permissions CreateApiUserToken(Uuid), CreateApiUserTokenSelf, CreateApiUserTokenAll, @@ -37,9 +38,23 @@ pub enum ApiPermission { UpdateApiUserSelf, UpdateApiUserAll, + // RFD access permissions GetRfd(BTreeSet), GetAllRfds, GetAssignedRfds, + GetAllDiscussions, + + // OAuth client manage permissions + CreateOAuthClient, + GetOAuthClientAll, + GetOAuthClient(Uuid), + CreateOAuthClientSecret(Uuid), + GetOAuthClientSecret(Uuid), + DeleteOAuthClientSecret(Uuid), + CreateOAuthClientRedirectUri(Uuid), + GetOAuthClientRedirectUri(Uuid), + DeleteOAuthClientRedirectUri(Uuid), + DeleteOAuthClient(Uuid), } impl ApiPermission { diff --git a/rfd-api/src/server.rs b/rfd-api/src/server.rs index e07ebfb..ceaa79d 100644 --- a/rfd-api/src/server.rs +++ b/rfd-api/src/server.rs @@ -15,7 +15,12 @@ use crate::{ }, login::oauth::{ authz_code::{authz_code_exchange, authz_code_redirect, authz_code_return}, - device_token::{exchange_device_token, get_device_provider}, client::{create_oauth_client, delete_oauth_client_redirect_uri, create_oauth_client_redirect_uri, delete_oauth_client_secret, create_oauth_client_secret, delete_oauth_client}, + client::{ + create_oauth_client, create_oauth_client_redirect_uri, create_oauth_client_secret, + delete_oauth_client_redirect_uri, delete_oauth_client_secret, get_oauth_client, + list_oauth_clients, + }, + device_token::{exchange_device_token, get_device_provider}, }, rfd::get_rfd, webhook::github_webhook, @@ -82,8 +87,9 @@ pub fn server( api.register(delete_api_user_token).unwrap(); // OAuth Client Management + api.register(list_oauth_clients).unwrap(); api.register(create_oauth_client).unwrap(); - api.register(delete_oauth_client).unwrap(); + api.register(get_oauth_client).unwrap(); api.register(create_oauth_client_secret).unwrap(); api.register(delete_oauth_client_secret).unwrap(); api.register(create_oauth_client_redirect_uri).unwrap(); diff --git a/rfd-model/src/db.rs b/rfd-model/src/db.rs index c2af447..4e77633 100644 --- a/rfd-model/src/db.rs +++ b/rfd-model/src/db.rs @@ -6,8 +6,8 @@ use uuid::Uuid; use crate::{ permissions::Permissions, schema::{ - api_key, api_user, api_user_access_token, api_user_provider, job, login_attempt, rfd, - rfd_pdf, rfd_revision, oauth_client, oauth_client_secret, oauth_client_redirect_uri, + api_key, api_user, api_user_access_token, api_user_provider, job, login_attempt, + oauth_client, oauth_client_redirect_uri, oauth_client_secret, rfd, rfd_pdf, rfd_revision, }, schema_ext::{ContentFormat, LoginAttemptState, PdfSource}, }; @@ -163,4 +163,4 @@ pub struct OAuthClientRedirectUriModel { pub redirect_uri: String, pub created_at: DateTime, pub deleted_at: Option>, -} \ No newline at end of file +} diff --git a/rfd-model/src/lib.rs b/rfd-model/src/lib.rs index 832a99d..983aa9e 100644 --- a/rfd-model/src/lib.rs +++ b/rfd-model/src/lib.rs @@ -1,7 +1,10 @@ use std::{collections::BTreeMap, fmt::Display}; use chrono::{DateTime, Utc}; -use db::{JobModel, LoginAttemptModel, RfdModel, RfdPdfModel, RfdRevisionModel, OAuthClientRedirectUriModel, OAuthClientSecretModel}; +use db::{ + JobModel, LoginAttemptModel, OAuthClientRedirectUriModel, OAuthClientSecretModel, RfdModel, + RfdPdfModel, RfdRevisionModel, +}; use partial_struct::partial; use permissions::Permissions; use schema_ext::{ContentFormat, LoginAttemptState, PdfSource}; @@ -349,7 +352,7 @@ impl From for OAuthClientSecret { oauth_client_id: value.oauth_client_id, secret: value.secret, created_at: value.created_at, - deleted_at:value.deleted_at, + deleted_at: value.deleted_at, } } } @@ -373,7 +376,7 @@ impl From for OAuthClientRedirectUri { oauth_client_id: value.oauth_client_id, redirect_uri: value.redirect_uri, created_at: value.created_at, - deleted_at:value.deleted_at, + deleted_at: value.deleted_at, } } -} \ No newline at end of file +} diff --git a/rfd-model/src/storage/mod.rs b/rfd-model/src/storage/mod.rs index e8f9f8d..956bda2 100644 --- a/rfd-model/src/storage/mod.rs +++ b/rfd-model/src/storage/mod.rs @@ -12,9 +12,9 @@ use crate::{ permissions::Permission, schema_ext::{LoginAttemptState, PdfSource}, AccessToken, ApiKey, ApiUser, ApiUserProvider, Job, LoginAttempt, NewAccessToken, NewApiKey, - NewApiUser, NewApiUserProvider, NewJob, NewLoginAttempt, NewRfd, NewRfdPdf, NewRfdRevision, - Rfd, RfdPdf, RfdRevision, OAuthClient, NewOAuthClient, OAuthClientSecret, OAuthClientRedirectUri, - NewOAuthClientSecret, NewOAuthClientRedirectUri, + NewApiUser, NewApiUserProvider, NewJob, NewLoginAttempt, NewOAuthClient, + NewOAuthClientRedirectUri, NewOAuthClientSecret, NewRfd, NewRfdPdf, NewRfdRevision, + OAuthClient, OAuthClientRedirectUri, OAuthClientSecret, Rfd, RfdPdf, RfdRevision, }; pub mod postgres; @@ -360,6 +360,9 @@ pub trait OAuthClientSecretStore { #[cfg_attr(feature = "mock", automock)] #[async_trait] pub trait OAuthClientRedirectUriStore { - async fn upsert(&self, redirect_uri: NewOAuthClientRedirectUri) -> Result; + async fn upsert( + &self, + redirect_uri: NewOAuthClientRedirectUri, + ) -> Result; async fn delete(&self, id: &Uuid) -> Result, StoreError>; -} \ No newline at end of file +} diff --git a/rfd-model/src/storage/postgres.rs b/rfd-model/src/storage/postgres.rs index 2ebff2d..8b650a3 100644 --- a/rfd-model/src/storage/postgres.rs +++ b/rfd-model/src/storage/postgres.rs @@ -1,4 +1,7 @@ -use std::{time::Duration, collections::{BTreeMap, BTreeSet}}; +use std::{ + collections::{BTreeMap, BTreeSet}, + time::Duration, +}; use async_bb8_diesel::{AsyncRunQueryDsl, ConnectionError, ConnectionManager, OptionalExtension}; use async_trait::async_trait; @@ -18,25 +21,27 @@ use uuid::Uuid; use crate::{ db::{ ApiKeyModel, ApiUserAccessTokenModel, ApiUserModel, ApiUserProviderModel, JobModel, - LoginAttemptModel, RfdModel, RfdPdfModel, RfdRevisionModel, OAuthClientSecretModel, OAuthClientModel, OAuthClientRedirectUriModel, + LoginAttemptModel, OAuthClientModel, OAuthClientRedirectUriModel, OAuthClientSecretModel, + RfdModel, RfdPdfModel, RfdRevisionModel, }, permissions::{Permission, Permissions}, schema::{ - api_key, api_user, api_user_access_token, api_user_provider, job, login_attempt, rfd, - rfd_pdf, rfd_revision, oauth_client, oauth_client_secret, oauth_client_redirect_uri, + api_key, api_user, api_user_access_token, api_user_provider, job, login_attempt, + oauth_client, oauth_client_redirect_uri, oauth_client_secret, rfd, rfd_pdf, rfd_revision, }, storage::StoreError, AccessToken, ApiKey, ApiUser, ApiUserProvider, Job, LoginAttempt, NewAccessToken, NewApiKey, - NewApiUser, NewApiUserProvider, NewJob, NewLoginAttempt, NewRfd, NewRfdPdf, NewRfdRevision, - Rfd, RfdPdf, RfdRevision, OAuthClient, NewOAuthClient, OAuthClientRedirectUri, OAuthClientSecret, - NewOAuthClientSecret, NewOAuthClientRedirectUri + NewApiUser, NewApiUserProvider, NewJob, NewLoginAttempt, NewOAuthClient, + NewOAuthClientRedirectUri, NewOAuthClientSecret, NewRfd, NewRfdPdf, NewRfdRevision, + OAuthClient, OAuthClientRedirectUri, OAuthClientSecret, Rfd, RfdPdf, RfdRevision, }; use super::{ AccessTokenFilter, AccessTokenStore, ApiKeyFilter, ApiKeyStore, ApiUserFilter, ApiUserProviderFilter, ApiUserProviderStore, ApiUserStore, JobFilter, JobStore, ListPagination, - LoginAttemptFilter, LoginAttemptStore, RfdFilter, RfdPdfFilter, RfdPdfStore, RfdRevisionFilter, - RfdRevisionStore, RfdStore, OAuthClientStore, OAuthClientFilter, OAuthClientSecretStore, OAuthClientRedirectUriStore, + LoginAttemptFilter, LoginAttemptStore, OAuthClientFilter, OAuthClientRedirectUriStore, + OAuthClientSecretStore, OAuthClientStore, RfdFilter, RfdPdfFilter, RfdPdfStore, + RfdRevisionFilter, RfdRevisionStore, RfdStore, }; pub type DbPool = Pool>; @@ -966,7 +971,7 @@ impl OAuthClientStore for PostgresStore { self, OAuthClientFilter { id: Some(vec![*id]), - deleted + deleted, }, &ListPagination::default().limit(1), ) @@ -985,10 +990,7 @@ impl OAuthClientStore for PostgresStore { .inner_join(oauth_client_redirect_uri::table) .into_boxed(); - let OAuthClientFilter { - id, - deleted, - } = filter; + let OAuthClientFilter { id, deleted } = filter; if let Some(id) = id { query = query.filter(oauth_client::id.eq_any(id)); @@ -1002,38 +1004,42 @@ impl OAuthClientStore for PostgresStore { .offset(pagination.offset) .limit(pagination.limit) .order(oauth_client::created_at.desc()) - .load_async::<(OAuthClientModel, OAuthClientSecretModel, OAuthClientRedirectUriModel)>(&self.conn) + .load_async::<( + OAuthClientModel, + OAuthClientSecretModel, + OAuthClientRedirectUriModel, + )>(&self.conn) .await? .into_iter() - .fold(BTreeMap::new(), |mut clients, (client, secret, redirect)| { - let value = clients.entry(client.id).or_insert((client, BTreeSet::::new(), BTreeSet::::new())); - value.1.insert(secret.into()); - value.2.insert(redirect.into()); - clients - }) + .fold( + BTreeMap::new(), + |mut clients, (client, secret, redirect)| { + let value = clients.entry(client.id).or_insert(( + client, + BTreeSet::::new(), + BTreeSet::::new(), + )); + value.1.insert(secret.into()); + value.2.insert(redirect.into()); + clients + }, + ) .into_iter() - .map(|(_, (client, secrets, redirect_uris))| { - OAuthClient { - id: client.id, - secrets: secrets.into_iter().collect::>(), - redirect_uris: redirect_uris.into_iter().collect::>(), - created_at: client.created_at, - deleted_at: client.deleted_at, - } + .map(|(_, (client, secrets, redirect_uris))| OAuthClient { + id: client.id, + secrets: secrets.into_iter().collect::>(), + redirect_uris: redirect_uris.into_iter().collect::>(), + created_at: client.created_at, + deleted_at: client.deleted_at, }) .collect::>(); Ok(clients) } - async fn upsert( - &self, - client: NewOAuthClient, - ) -> Result { + async fn upsert(&self, client: NewOAuthClient) -> Result { let client_m: OAuthClientModel = insert_into(oauth_client::dsl::oauth_client) - .values( - oauth_client::id.eq(client.id) - ) + .values(oauth_client::id.eq(client.id)) .get_result_async(&self.conn) .await?; @@ -1060,14 +1066,15 @@ impl OAuthClientStore for PostgresStore { #[async_trait] impl OAuthClientSecretStore for PostgresStore { async fn upsert(&self, secret: NewOAuthClientSecret) -> Result { - let secret_m: OAuthClientSecretModel = insert_into(oauth_client_secret::dsl::oauth_client_secret) - .values(( - oauth_client_secret::id.eq(secret.id), - oauth_client_secret::oauth_client_id.eq(secret.oauth_client_id), - oauth_client_secret::secret.eq(secret.secret), - )) - .get_result_async(&self.conn) - .await?; + let secret_m: OAuthClientSecretModel = + insert_into(oauth_client_secret::dsl::oauth_client_secret) + .values(( + oauth_client_secret::id.eq(secret.id), + oauth_client_secret::oauth_client_id.eq(secret.oauth_client_id), + oauth_client_secret::secret.eq(secret.secret), + )) + .get_result_async(&self.conn) + .await?; Ok(OAuthClientSecret { id: secret_m.id, @@ -1100,15 +1107,19 @@ impl OAuthClientSecretStore for PostgresStore { #[async_trait] impl OAuthClientRedirectUriStore for PostgresStore { - async fn upsert(&self, redirect_uri: NewOAuthClientRedirectUri) -> Result { - let redirect_uri_m: OAuthClientRedirectUriModel = insert_into(oauth_client_redirect_uri::dsl::oauth_client_redirect_uri) - .values(( - oauth_client_redirect_uri::id.eq(redirect_uri.id), - oauth_client_redirect_uri::oauth_client_id.eq(redirect_uri.oauth_client_id), - oauth_client_redirect_uri::redirect_uri.eq(redirect_uri.redirect_uri), - )) - .get_result_async(&self.conn) - .await?; + async fn upsert( + &self, + redirect_uri: NewOAuthClientRedirectUri, + ) -> Result { + let redirect_uri_m: OAuthClientRedirectUriModel = + insert_into(oauth_client_redirect_uri::dsl::oauth_client_redirect_uri) + .values(( + oauth_client_redirect_uri::id.eq(redirect_uri.id), + oauth_client_redirect_uri::oauth_client_id.eq(redirect_uri.oauth_client_id), + oauth_client_redirect_uri::redirect_uri.eq(redirect_uri.redirect_uri), + )) + .get_result_async(&self.conn) + .await?; Ok(OAuthClientRedirectUri { id: redirect_uri_m.id,