mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-08-01 15:06:05 +00:00
db: check column conversions when mapping rows
Lewis: May this revision serve well! <lu5a@proton.me>
This commit is contained in:
@@ -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> {
|
||||
|
||||
@@ -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<Option<BlobMetadata>, 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<Option<String>, 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<Vec<CidLink>, 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<i64, DbError> {
|
||||
@@ -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<i64, DbError> {
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<i32, DbError> {
|
||||
|
||||
@@ -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<Uuid, DbError> {
|
||||
|
||||
Reference in New Issue
Block a user