diff --git a/crates/tranquil-db/src/postgres/backlink.rs b/crates/tranquil-db/src/postgres/backlink.rs index 1928c41..280cd87 100644 --- a/crates/tranquil-db/src/postgres/backlink.rs +++ b/crates/tranquil-db/src/postgres/backlink.rs @@ -4,6 +4,8 @@ use tranquil_db_traits::{Backlink, BacklinkRepository, DbError}; use tranquil_types::{AtUri, Nsid}; use uuid::Uuid; +use super::col; +use super::column_vec; use super::user::map_sqlx_error; pub struct PostgresBacklinkRepository { @@ -49,7 +51,7 @@ impl BacklinkRepository for PostgresBacklinkRepository { .await .map_err(map_sqlx_error)?; - Ok(results.into_iter().map(Into::into).collect()) + column_vec(results, col::BACKLINKS_URI) } async fn add_backlinks(&self, repo_id: Uuid, backlinks: &[Backlink]) -> Result<(), DbError> { diff --git a/crates/tranquil-db/src/postgres/blob.rs b/crates/tranquil-db/src/postgres/blob.rs index f5428b4..6e69a83 100644 --- a/crates/tranquil-db/src/postgres/blob.rs +++ b/crates/tranquil-db/src/postgres/blob.rs @@ -6,7 +6,9 @@ use tranquil_db_traits::{ use tranquil_types::{AtUri, CidLink, Did, Tid}; use uuid::Uuid; +use super::col; use super::user::map_sqlx_error; +use super::{column, column_vec, opt_column}; pub struct PostgresBlobRepository { pool: PgPool, @@ -42,7 +44,7 @@ impl BlobRepository for PostgresBlobRepository { .await .map_err(map_sqlx_error)?; - Ok(result.map(CidLink::from)) + opt_column(result, col::BLOBS_CID) } async fn get_blob_metadata(&self, cid: &CidLink) -> Result, DbError> { @@ -73,10 +75,14 @@ impl BlobRepository for PostgresBlobRepository { .await .map_err(map_sqlx_error)?; - Ok(result.map(|r| BlobWithTakedown { - cid: CidLink::from(r.cid), - takedown_ref: r.takedown_ref, - })) + result + .map(|r| { + Ok(BlobWithTakedown { + cid: column(r.cid, col::BLOBS_CID)?, + takedown_ref: r.takedown_ref, + }) + }) + .transpose() } async fn get_blob_storage_key(&self, cid: &CidLink) -> Result, DbError> { @@ -109,7 +115,7 @@ impl BlobRepository for PostgresBlobRepository { .await .map_err(map_sqlx_error)?; - Ok(results.into_iter().map(CidLink::from).collect()) + column_vec(results, col::BLOBS_CID) } async fn list_blobs_since_rev(&self, did: &Did, since: &Tid) -> Result, DbError> { @@ -124,7 +130,7 @@ impl BlobRepository for PostgresBlobRepository { .await .map_err(map_sqlx_error)?; - Ok(results.into_iter().map(CidLink::from).collect()) + column_vec(results, col::REPO_SEQ_BLOBS) } async fn count_blobs_by_user(&self, user_id: Uuid) -> Result { @@ -244,13 +250,15 @@ impl BlobRepository for PostgresBlobRepository { .await .map_err(map_sqlx_error)?; - Ok(results + results .into_iter() - .map(|r| MissingBlobInfo { - blob_cid: CidLink::from(r.blob_cid), - record_uri: AtUri::from(r.record_uri), + .map(|r| { + Ok(MissingBlobInfo { + blob_cid: column(r.blob_cid, col::RECORD_BLOBS_BLOB_CID)?, + record_uri: column(r.record_uri, col::RECORD_BLOBS_RECORD_URI)?, + }) }) - .collect()) + .collect() } async fn count_distinct_record_blobs(&self, repo_id: Uuid) -> Result { @@ -277,13 +285,15 @@ impl BlobRepository for PostgresBlobRepository { .await .map_err(map_sqlx_error)?; - Ok(results + results .into_iter() - .map(|r| BlobForExport { - cid: CidLink::from(r.cid), - storage_key: r.storage_key, - mime_type: r.mime_type, + .map(|r| { + Ok(BlobForExport { + cid: column(r.cid, col::BLOBS_CID)?, + storage_key: r.storage_key, + mime_type: r.mime_type, + }) }) - .collect()) + .collect() } } diff --git a/crates/tranquil-db/src/postgres/oauth.rs b/crates/tranquil-db/src/postgres/oauth.rs index d26aa84..ca70c3a 100644 --- a/crates/tranquil-db/src/postgres/oauth.rs +++ b/crates/tranquil-db/src/postgres/oauth.rs @@ -11,11 +11,12 @@ use tranquil_oauth::{ SessionId as OAuthSessionId, TokenData, }; use tranquil_types::{ - AuthorizationCode, ClientId, DPoPProofId, DeviceId, Did, Handle, RefreshToken, RequestId, - TokenId, + AuthorizationCode, ClientId, DPoPProofId, DeviceId, Did, RefreshToken, RequestId, TokenId, }; use uuid::Uuid; +use super::col; +use super::column; use super::user::map_sqlx_error; const REGISTRATION_FLOW_EXTENDED_EXPIRY_SECS: i64 = 600; @@ -98,7 +99,7 @@ impl OAuthRepository for PostgresOAuthRepository { did: r .did .parse() - .map_err(|_| DbError::Other("Invalid DID in token".into()))?, + .map_err(|_| DbError::InvalidColumn(col::OAUTH_TOKEN_DID))?, token_id: TokenId::from(r.token_id), created_at: r.created_at, updated_at: r.updated_at, @@ -115,7 +116,7 @@ impl OAuthRepository for PostgresOAuthRepository { .controller_did .map(|s| s.parse()) .transpose() - .map_err(|_| DbError::Other("Invalid controller DID".into()))?, + .map_err(|_| DbError::InvalidColumn(col::OAUTH_TOKEN_CONTROLLER_DID))?, })), None => Ok(None), } @@ -144,7 +145,7 @@ impl OAuthRepository for PostgresOAuthRepository { did: r .did .parse() - .map_err(|_| DbError::Other("Invalid DID in token".into()))?, + .map_err(|_| DbError::InvalidColumn(col::OAUTH_TOKEN_DID))?, token_id: TokenId::from(r.token_id), created_at: r.created_at, updated_at: r.updated_at, @@ -161,7 +162,7 @@ impl OAuthRepository for PostgresOAuthRepository { .controller_did .map(|s| s.parse()) .transpose() - .map_err(|_| DbError::Other("Invalid controller DID".into()))?, + .map_err(|_| DbError::InvalidColumn(col::OAUTH_TOKEN_CONTROLLER_DID))?, }, ))), None => Ok(None), @@ -193,7 +194,7 @@ impl OAuthRepository for PostgresOAuthRepository { did: r .did .parse() - .map_err(|_| DbError::Other("Invalid DID in token".into()))?, + .map_err(|_| DbError::InvalidColumn(col::OAUTH_TOKEN_DID))?, token_id: TokenId::from(r.token_id), created_at: r.created_at, updated_at: r.updated_at, @@ -210,7 +211,7 @@ impl OAuthRepository for PostgresOAuthRepository { .controller_did .map(|s| s.parse()) .transpose() - .map_err(|_| DbError::Other("Invalid controller DID".into()))?, + .map_err(|_| DbError::InvalidColumn(col::OAUTH_TOKEN_CONTROLLER_DID))?, }, ))), None => Ok(None), @@ -326,7 +327,7 @@ impl OAuthRepository for PostgresOAuthRepository { did: r .did .parse() - .map_err(|_| DbError::Other("Invalid DID in token".into()))?, + .map_err(|_| DbError::InvalidColumn(col::OAUTH_TOKEN_DID))?, token_id: TokenId::from(r.token_id), created_at: r.created_at, updated_at: r.updated_at, @@ -343,7 +344,7 @@ impl OAuthRepository for PostgresOAuthRepository { .controller_did .map(|s| s.parse()) .transpose() - .map_err(|_| DbError::Other("Invalid controller DID".into()))?, + .map_err(|_| DbError::InvalidColumn(col::OAUTH_TOKEN_CONTROLLER_DID))?, }) }) .collect() @@ -476,18 +477,14 @@ impl OAuthRepository for PostgresOAuthRepository { client_auth, parameters, expires_at: r.expires_at, - did: r - .did - .map(|s| s.parse()) - .transpose() - .map_err(|_| DbError::Other("Invalid DID in DB".into()))?, + did: r.did.map(|s| s.parse()).transpose().map_err(|_| { + DbError::InvalidColumn(col::OAUTH_AUTHORIZATION_REQUEST_DID) + })?, device_id: r.device_id.map(DeviceId::from), code: r.code.map(AuthorizationCode::from), - controller_did: r - .controller_did - .map(|s| s.parse()) - .transpose() - .map_err(|_| DbError::Other("Invalid controller DID in DB".into()))?, + controller_did: r.controller_did.map(|s| s.parse()).transpose().map_err( + |_| DbError::InvalidColumn(col::OAUTH_AUTHORIZATION_REQUEST_CONTROLLER_DID), + )?, })) } None => Ok(None), @@ -570,18 +567,14 @@ impl OAuthRepository for PostgresOAuthRepository { client_auth, parameters, expires_at: r.expires_at, - did: r - .did - .map(|s| s.parse()) - .transpose() - .map_err(|_| DbError::Other("Invalid DID in DB".into()))?, + did: r.did.map(|s| s.parse()).transpose().map_err(|_| { + DbError::InvalidColumn(col::OAUTH_AUTHORIZATION_REQUEST_DID) + })?, device_id: r.device_id.map(DeviceId::from), code: r.code.map(AuthorizationCode::from), - controller_did: r - .controller_did - .map(|s| s.parse()) - .transpose() - .map_err(|_| DbError::Other("Invalid controller DID in DB".into()))?, + controller_did: r.controller_did.map(|s| s.parse()).transpose().map_err( + |_| DbError::InvalidColumn(col::OAUTH_AUTHORIZATION_REQUEST_CONTROLLER_DID), + )?, })) } None => Ok(None), @@ -813,15 +806,16 @@ impl OAuthRepository for PostgresOAuthRepository { .fetch_all(&self.pool) .await .map_err(map_sqlx_error)?; - Ok(rows - .into_iter() - .map(|r| DeviceAccountRow { - did: Did::from(r.did), - handle: Handle::from(r.handle), - email: r.email, - last_used_at: r.last_used_at, + rows.into_iter() + .map(|r| { + Ok(DeviceAccountRow { + did: column(r.did, col::USERS_DID)?, + handle: column(r.handle, col::USERS_HANDLE)?, + email: r.email, + last_used_at: r.last_used_at, + }) }) - .collect()) + .collect() } async fn verify_account_on_device( @@ -904,7 +898,7 @@ impl OAuthRepository for PostgresOAuthRepository { .map_err(map_sqlx_error)?; Ok(TwoFactorChallenge { id: row.id, - did: Did::from(row.did), + did: column(row.did, col::OAUTH_2FA_CHALLENGE_DID)?, request_uri: RequestId::from(row.request_uri), code: row.code, attempts: row.attempts, @@ -928,15 +922,18 @@ impl OAuthRepository for PostgresOAuthRepository { .fetch_optional(&self.pool) .await .map_err(map_sqlx_error)?; - Ok(row.map(|r| TwoFactorChallenge { - id: r.id, - did: Did::from(r.did), - request_uri: RequestId::from(r.request_uri), - code: r.code, - attempts: r.attempts, - created_at: r.created_at, - expires_at: r.expires_at, - })) + row.map(|r| { + Ok(TwoFactorChallenge { + id: r.id, + did: column(r.did, col::OAUTH_2FA_CHALLENGE_DID)?, + request_uri: RequestId::from(r.request_uri), + code: r.code, + attempts: r.attempts, + created_at: r.created_at, + expires_at: r.expires_at, + }) + }) + .transpose() } async fn increment_2fa_attempts(&self, id: Uuid) -> Result { diff --git a/crates/tranquil-db/src/postgres/session.rs b/crates/tranquil-db/src/postgres/session.rs index 9a5a381..0eef369 100644 --- a/crates/tranquil-db/src/postgres/session.rs +++ b/crates/tranquil-db/src/postgres/session.rs @@ -10,7 +10,9 @@ use tranquil_db_traits::{ use tranquil_types::{Did, Jti, PasswordHash}; use uuid::Uuid; +use super::col; use super::user::map_sqlx_error; +use super::{column, opt_column}; pub struct PostgresSessionRepository { pool: PgPool, @@ -69,21 +71,24 @@ impl SessionRepository for PostgresSessionRepository { .await .map_err(map_sqlx_error)?; - Ok(row.map(|r| SessionToken { - id: SessionId::new(r.id), - did: Did::from(r.did), - access_jti: Jti::from(r.access_jti), - refresh_jti: Jti::from(r.refresh_jti), - access_expires_at: r.access_expires_at, - refresh_expires_at: r.refresh_expires_at, - login_type: LoginType::from_legacy_flag(r.legacy_login), - mfa_verified: r.mfa_verified, - scope: r.scope, - controller_did: r.controller_did.map(Did::from), - app_password_name: r.app_password_name, - created_at: r.created_at, - updated_at: r.updated_at, - })) + row.map(|r| { + Ok(SessionToken { + id: SessionId::new(r.id), + did: column(r.did, col::SESSION_TOKENS_DID)?, + access_jti: Jti::from(r.access_jti), + refresh_jti: Jti::from(r.refresh_jti), + access_expires_at: r.access_expires_at, + refresh_expires_at: r.refresh_expires_at, + login_type: LoginType::from_legacy_flag(r.legacy_login), + mfa_verified: r.mfa_verified, + scope: r.scope, + controller_did: opt_column(r.controller_did, col::SESSION_TOKENS_CONTROLLER_DID)?, + app_password_name: r.app_password_name, + created_at: r.created_at, + updated_at: r.updated_at, + }) + }) + .transpose() } async fn get_session_for_refresh( @@ -104,14 +109,17 @@ impl SessionRepository for PostgresSessionRepository { .await .map_err(map_sqlx_error)?; - Ok(row.map(|r| SessionForRefresh { - id: SessionId::new(r.id), - did: Did::from(r.did), - scope: r.scope, - controller_did: r.controller_did.map(Did::from), - key_bytes: r.key_bytes, - encryption_version: r.encryption_version.unwrap_or(0), - })) + row.map(|r| { + Ok(SessionForRefresh { + id: SessionId::new(r.id), + did: column(r.did, col::SESSION_TOKENS_DID)?, + scope: r.scope, + controller_did: opt_column(r.controller_did, col::SESSION_TOKENS_CONTROLLER_DID)?, + key_bytes: r.key_bytes, + encryption_version: r.encryption_version.unwrap_or(0), + }) + }) + .transpose() } async fn delete_session_by_access_jti( @@ -274,9 +282,9 @@ impl SessionRepository for PostgresSessionRepository { let grace_cutoff = Utc::now() - Duration::seconds(REFRESH_GRACE_PERIOD_SECS); if r.used_at > grace_cutoff { Ok(RefreshGraceLookup::Replay(RefreshGraceReplay { - did: Did::from(r.did), + did: column(r.did, col::SESSION_TOKENS_DID)?, scope: r.scope, - controller_did: r.controller_did.map(Did::from), + controller_did: opt_column(r.controller_did, col::SESSION_TOKENS_CONTROLLER_DID)?, access_jti: Jti::from(r.access_jti), refresh_jti: Jti::from(r.refresh_jti), access_expires_at: r.access_expires_at, @@ -286,7 +294,7 @@ impl SessionRepository for PostgresSessionRepository { })) } else { Ok(RefreshGraceLookup::Compromised { - did: Did::from(r.did), + did: column(r.did, col::SESSION_TOKENS_DID)?, session_id: SessionId::new(r.session_id), key_bytes: r.key_bytes, encryption_version: r.encryption_version.unwrap_or(0), @@ -308,19 +316,23 @@ impl SessionRepository for PostgresSessionRepository { .await .map_err(map_sqlx_error)?; - Ok(rows - .into_iter() - .map(|r| AppPasswordRecord { - id: r.id, - user_id: r.user_id, - name: r.name, - password_hash: PasswordHash::new(r.password_hash), - created_at: r.created_at, - privilege: AppPasswordPrivilege::from_privileged_flag(r.privileged), - scopes: r.scopes, - created_by_controller_did: r.created_by_controller_did.map(Did::from), + rows.into_iter() + .map(|r| { + Ok(AppPasswordRecord { + id: r.id, + user_id: r.user_id, + name: r.name, + password_hash: PasswordHash::new(r.password_hash), + created_at: r.created_at, + privilege: AppPasswordPrivilege::from_privileged_flag(r.privileged), + scopes: r.scopes, + created_by_controller_did: opt_column( + r.created_by_controller_did, + col::APP_PASSWORDS_CREATED_BY_CONTROLLER_DID, + )?, + }) }) - .collect()) + .collect() } async fn get_app_passwords_for_login( @@ -341,19 +353,23 @@ impl SessionRepository for PostgresSessionRepository { .await .map_err(map_sqlx_error)?; - Ok(rows - .into_iter() - .map(|r| AppPasswordRecord { - id: r.id, - user_id: r.user_id, - name: r.name, - password_hash: PasswordHash::new(r.password_hash), - created_at: r.created_at, - privilege: AppPasswordPrivilege::from_privileged_flag(r.privileged), - scopes: r.scopes, - created_by_controller_did: r.created_by_controller_did.map(Did::from), + rows.into_iter() + .map(|r| { + Ok(AppPasswordRecord { + id: r.id, + user_id: r.user_id, + name: r.name, + password_hash: PasswordHash::new(r.password_hash), + created_at: r.created_at, + privilege: AppPasswordPrivilege::from_privileged_flag(r.privileged), + scopes: r.scopes, + created_by_controller_did: opt_column( + r.created_by_controller_did, + col::APP_PASSWORDS_CREATED_BY_CONTROLLER_DID, + )?, + }) }) - .collect()) + .collect() } async fn get_app_password_by_name( @@ -374,16 +390,22 @@ impl SessionRepository for PostgresSessionRepository { .await .map_err(map_sqlx_error)?; - Ok(row.map(|r| AppPasswordRecord { - id: r.id, - user_id: r.user_id, - name: r.name, - password_hash: PasswordHash::new(r.password_hash), - created_at: r.created_at, - privilege: AppPasswordPrivilege::from_privileged_flag(r.privileged), - scopes: r.scopes, - created_by_controller_did: r.created_by_controller_did.map(Did::from), - })) + row.map(|r| { + Ok(AppPasswordRecord { + id: r.id, + user_id: r.user_id, + name: r.name, + password_hash: PasswordHash::new(r.password_hash), + created_at: r.created_at, + privilege: AppPasswordPrivilege::from_privileged_flag(r.privileged), + scopes: r.scopes, + created_by_controller_did: opt_column( + r.created_by_controller_did, + col::APP_PASSWORDS_CREATED_BY_CONTROLLER_DID, + )?, + }) + }) + .transpose() } async fn create_app_password(&self, data: &AppPasswordCreate) -> Result {