mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-09-19 16:54:14 +00:00
db: newtype PasswordHash for users & app passwords
Lewis: May this revision serve well! <lu5a@proton.me>
This commit is contained in:
@@ -5,7 +5,7 @@ use tracing::error;
|
||||
use tranquil_db_traits::{CommsChannel, DidWebOverrides, SessionRepository, UserRepository};
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::api::error::DbResultExt;
|
||||
use tranquil_pds::types::{AtIdentifier, Did, Handle};
|
||||
use tranquil_pds::types::{AtIdentifier, Did, Handle, PasswordHash};
|
||||
|
||||
pub struct ResolvedRepo {
|
||||
pub user_id: uuid::Uuid,
|
||||
@@ -216,7 +216,7 @@ pub async fn verify_credential(
|
||||
session_repo: &dyn SessionRepository,
|
||||
user_id: uuid::Uuid,
|
||||
password: &str,
|
||||
password_hash: Option<&str>,
|
||||
password_hash: Option<&PasswordHash>,
|
||||
) -> Option<CredentialMatch> {
|
||||
let main_valid = password_hash
|
||||
.map(|h| bcrypt::verify(password, h).unwrap_or(false))
|
||||
@@ -230,7 +230,7 @@ pub async fn verify_credential(
|
||||
.unwrap_or_default();
|
||||
app_passwords
|
||||
.into_iter()
|
||||
.find(|app| bcrypt::verify(password, &app.password_hash).unwrap_or(false))
|
||||
.find(|app| bcrypt::verify(password, app.password_hash.as_str()).unwrap_or(false))
|
||||
.map(|app| {
|
||||
let scopes = app.scopes.unwrap_or_else(|| {
|
||||
if app.privilege.is_privileged() {
|
||||
@@ -247,14 +247,16 @@ pub async fn verify_credential(
|
||||
})
|
||||
}
|
||||
|
||||
pub fn hash_or_internal_error(value: &str) -> Result<String, ApiError> {
|
||||
bcrypt::hash(value, DEFAULT_COST).map_err(|e| {
|
||||
error!("Bcrypt hash error: {:?}", e);
|
||||
ApiError::InternalError(None)
|
||||
})
|
||||
pub fn hash_or_internal_error(value: &str) -> Result<PasswordHash, ApiError> {
|
||||
bcrypt::hash(value, DEFAULT_COST)
|
||||
.map(PasswordHash::new)
|
||||
.map_err(|e| {
|
||||
error!("Bcrypt hash error: {:?}", e);
|
||||
ApiError::InternalError(None)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn hash_password_async(password: &str) -> Result<String, ApiError> {
|
||||
pub async fn hash_password_async(password: &str) -> Result<PasswordHash, ApiError> {
|
||||
let password = password.to_string();
|
||||
tokio::task::spawn_blocking(move || hash(password, DEFAULT_COST))
|
||||
.await
|
||||
@@ -262,6 +264,7 @@ pub async fn hash_password_async(password: &str) -> Result<String, ApiError> {
|
||||
error!("Failed to spawn blocking task: {:?}", e);
|
||||
ApiError::InternalError(None)
|
||||
})?
|
||||
.map(PasswordHash::new)
|
||||
.map_err(|e| {
|
||||
error!("Failed to hash password: {:?}", e);
|
||||
ApiError::InternalError(None)
|
||||
|
||||
@@ -637,7 +637,7 @@ pub async fn delete_account(
|
||||
state.repos.session.as_ref(),
|
||||
user_id,
|
||||
password,
|
||||
password_hash.as_deref(),
|
||||
password_hash.as_ref(),
|
||||
)
|
||||
.await
|
||||
.is_none()
|
||||
|
||||
@@ -74,7 +74,7 @@ pub async fn reauth_password(
|
||||
.log_db_err("fetching password hash")?
|
||||
.ok_or(ApiError::AccountNotFound)?;
|
||||
|
||||
let password_valid = bcrypt::verify(&input.password, &password_hash).unwrap_or(false);
|
||||
let password_valid = bcrypt::verify(&input.password, password_hash.as_str()).unwrap_or(false);
|
||||
|
||||
if !password_valid {
|
||||
let app_password_hashes = state
|
||||
|
||||
@@ -112,7 +112,7 @@ pub async fn create_session(
|
||||
state.repos.session.as_ref(),
|
||||
row.id,
|
||||
&input.password,
|
||||
row.password_hash.as_deref(),
|
||||
row.password_hash.as_ref(),
|
||||
)
|
||||
.await;
|
||||
let (app_password_name, app_password_scopes, app_password_controller) = match credential {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use tranquil_types::Did;
|
||||
use tranquil_types::{Did, Jti, PasswordHash};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::DbError;
|
||||
@@ -134,7 +134,7 @@ pub struct AppPasswordRecord {
|
||||
pub id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub name: String,
|
||||
pub password_hash: String,
|
||||
pub password_hash: PasswordHash,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub privilege: AppPasswordPrivilege,
|
||||
pub scopes: Option<String>,
|
||||
@@ -145,7 +145,7 @@ pub struct AppPasswordRecord {
|
||||
pub struct AppPasswordCreate {
|
||||
pub user_id: Uuid,
|
||||
pub name: String,
|
||||
pub password_hash: String,
|
||||
pub password_hash: PasswordHash,
|
||||
pub privilege: AppPasswordPrivilege,
|
||||
pub scopes: Option<String>,
|
||||
pub created_by_controller_did: Option<Did>,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tranquil_types::{Did, Handle};
|
||||
use tranquil_types::{
|
||||
AtIdentifier, CidLink, Did, Handle, InviteCode, Jti, PasswordHash, Tid, TokenId,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{ChannelVerificationStatus, CommsChannel, DbError, SsoProviderType};
|
||||
@@ -78,7 +80,7 @@ pub struct UserEmailInfo {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UserLoginCheck {
|
||||
pub did: Did,
|
||||
pub password_hash: Option<String>,
|
||||
pub password_hash: Option<PasswordHash>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -86,7 +88,7 @@ pub struct UserLoginInfo {
|
||||
pub id: Uuid,
|
||||
pub did: Did,
|
||||
pub email: Option<String>,
|
||||
pub password_hash: Option<String>,
|
||||
pub password_hash: Option<PasswordHash>,
|
||||
pub password_required: bool,
|
||||
pub two_factor_enabled: bool,
|
||||
pub preferred_comms_channel: CommsChannel,
|
||||
@@ -313,7 +315,7 @@ pub trait UserRepository: Send + Sync {
|
||||
|
||||
async fn has_passkeys(&self, did: &Did) -> Result<bool, DbError>;
|
||||
|
||||
async fn get_password_hash_by_did(&self, did: &Did) -> Result<Option<String>, DbError>;
|
||||
async fn get_password_hash_by_did(&self, did: &Did) -> Result<Option<PasswordHash>, DbError>;
|
||||
|
||||
async fn get_passkeys_for_user(&self, did: &Did) -> Result<Vec<StoredPasskey>, DbError>;
|
||||
|
||||
@@ -466,13 +468,16 @@ pub trait UserRepository: Send + Sync {
|
||||
did: &Did,
|
||||
) -> Result<Option<UserIdAndPasswordHash>, DbError>;
|
||||
|
||||
async fn update_password_hash(&self, user_id: Uuid, password_hash: &str)
|
||||
-> Result<(), DbError>;
|
||||
async fn update_password_hash(
|
||||
&self,
|
||||
user_id: Uuid,
|
||||
password_hash: &PasswordHash,
|
||||
) -> Result<(), DbError>;
|
||||
|
||||
async fn reset_password_with_sessions(
|
||||
&self,
|
||||
user_id: Uuid,
|
||||
password_hash: &str,
|
||||
password_hash: &PasswordHash,
|
||||
) -> Result<PasswordResetResult, DbError>;
|
||||
|
||||
async fn activate_account(&self, did: &Did) -> Result<bool, DbError>;
|
||||
@@ -495,7 +500,7 @@ pub trait UserRepository: Send + Sync {
|
||||
async fn set_new_user_password(
|
||||
&self,
|
||||
user_id: Uuid,
|
||||
password_hash: &str,
|
||||
password_hash: &PasswordHash,
|
||||
) -> Result<(), DbError>;
|
||||
|
||||
async fn get_user_key_by_did(&self, did: &Did) -> Result<Option<UserKeyInfo>, DbError>;
|
||||
@@ -699,7 +704,7 @@ pub struct AccountSearchResult {
|
||||
pub struct UserAuthInfo {
|
||||
pub id: Uuid,
|
||||
pub did: Did,
|
||||
pub password_hash: Option<String>,
|
||||
pub password_hash: Option<PasswordHash>,
|
||||
pub deactivated_at: Option<DateTime<Utc>>,
|
||||
pub takedown_ref: Option<String>,
|
||||
pub channel_verification: ChannelVerificationStatus,
|
||||
@@ -863,7 +868,7 @@ pub struct UserLoginFull {
|
||||
pub id: Uuid,
|
||||
pub did: Did,
|
||||
pub handle: Handle,
|
||||
pub password_hash: Option<String>,
|
||||
pub password_hash: Option<PasswordHash>,
|
||||
pub email: Option<String>,
|
||||
pub deactivated_at: Option<DateTime<Utc>>,
|
||||
pub takedown_ref: Option<String>,
|
||||
@@ -914,13 +919,13 @@ pub struct UserResetCodeInfo {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UserPasswordInfo {
|
||||
pub id: Uuid,
|
||||
pub password_hash: Option<String>,
|
||||
pub password_hash: Option<PasswordHash>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UserIdAndPasswordHash {
|
||||
pub id: Uuid,
|
||||
pub password_hash: String,
|
||||
pub password_hash: PasswordHash,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -932,7 +937,7 @@ pub struct PasswordResetResult {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UserForDeletion {
|
||||
pub id: Uuid,
|
||||
pub password_hash: Option<String>,
|
||||
pub password_hash: Option<PasswordHash>,
|
||||
pub handle: Handle,
|
||||
}
|
||||
|
||||
@@ -988,7 +993,7 @@ pub struct CreatePasswordAccountInput {
|
||||
pub handle: Handle,
|
||||
pub email: Option<String>,
|
||||
pub did: Did,
|
||||
pub password_hash: String,
|
||||
pub password_hash: PasswordHash,
|
||||
pub preferred_comms_channel: CommsChannel,
|
||||
pub discord_username: Option<String>,
|
||||
pub telegram_username: Option<String>,
|
||||
@@ -1044,7 +1049,7 @@ pub struct CreatePasskeyAccountInput {
|
||||
pub discord_username: Option<String>,
|
||||
pub telegram_username: Option<String>,
|
||||
pub signal_username: Option<String>,
|
||||
pub setup_token_hash: String,
|
||||
pub setup_token_hash: PasswordHash,
|
||||
pub setup_expires_at: DateTime<Utc>,
|
||||
pub deactivated_at: Option<DateTime<Utc>>,
|
||||
pub encrypted_key_bytes: Vec<u8>,
|
||||
@@ -1086,13 +1091,13 @@ pub struct CompletePasskeySetupInput {
|
||||
pub user_id: Uuid,
|
||||
pub did: Did,
|
||||
pub app_password_name: String,
|
||||
pub app_password_hash: String,
|
||||
pub app_password_hash: PasswordHash,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RecoverPasskeyAccountInput {
|
||||
pub did: Did,
|
||||
pub password_hash: String,
|
||||
pub password_hash: PasswordHash,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@@ -7,7 +7,7 @@ use tranquil_db_traits::{
|
||||
SessionForRefresh, SessionId, SessionListItem, SessionMfaStatus, SessionRefreshData,
|
||||
SessionRepository, SessionToken, SessionTokenCreate,
|
||||
};
|
||||
use tranquil_types::Did;
|
||||
use tranquil_types::{Did, Jti, PasswordHash};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::user::map_sqlx_error;
|
||||
@@ -318,7 +318,7 @@ impl SessionRepository for PostgresSessionRepository {
|
||||
id: r.id,
|
||||
user_id: r.user_id,
|
||||
name: r.name,
|
||||
password_hash: r.password_hash,
|
||||
password_hash: PasswordHash::new(r.password_hash),
|
||||
created_at: r.created_at,
|
||||
privilege: AppPasswordPrivilege::from_privileged_flag(r.privileged),
|
||||
scopes: r.scopes,
|
||||
@@ -351,7 +351,7 @@ impl SessionRepository for PostgresSessionRepository {
|
||||
id: r.id,
|
||||
user_id: r.user_id,
|
||||
name: r.name,
|
||||
password_hash: r.password_hash,
|
||||
password_hash: PasswordHash::new(r.password_hash),
|
||||
created_at: r.created_at,
|
||||
privilege: AppPasswordPrivilege::from_privileged_flag(r.privileged),
|
||||
scopes: r.scopes,
|
||||
@@ -382,7 +382,7 @@ impl SessionRepository for PostgresSessionRepository {
|
||||
id: r.id,
|
||||
user_id: r.user_id,
|
||||
name: r.name,
|
||||
password_hash: r.password_hash,
|
||||
password_hash: PasswordHash::new(r.password_hash),
|
||||
created_at: r.created_at,
|
||||
privilege: AppPasswordPrivilege::from_privileged_flag(r.privileged),
|
||||
scopes: r.scopes,
|
||||
@@ -399,7 +399,7 @@ impl SessionRepository for PostgresSessionRepository {
|
||||
"#,
|
||||
data.user_id,
|
||||
data.name,
|
||||
data.password_hash,
|
||||
data.password_hash.as_str(),
|
||||
data.privilege.is_privileged(),
|
||||
data.scopes,
|
||||
data.created_by_controller_did.as_ref().map(|d| d.as_str())
|
||||
@@ -510,7 +510,7 @@ impl SessionRepository for PostgresSessionRepository {
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
|
||||
Ok(rows)
|
||||
Ok(rows.into_iter().map(PasswordHash::new).collect())
|
||||
}
|
||||
|
||||
async fn refresh_session_atomic(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use sqlx::PgPool;
|
||||
use tranquil_types::{Did, Handle};
|
||||
use tranquil_types::{AtIdentifier, Did, Handle, Jti, PasswordHash, TokenId};
|
||||
use uuid::Uuid;
|
||||
|
||||
use tranquil_db_traits::{
|
||||
@@ -314,7 +314,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
Ok(row.map(|r| UserAuthInfo {
|
||||
id: r.id,
|
||||
did: Did::from(r.did),
|
||||
password_hash: r.password_hash,
|
||||
password_hash: r.password_hash.map(PasswordHash::new),
|
||||
deactivated_at: r.deactivated_at,
|
||||
takedown_ref: r.takedown_ref,
|
||||
channel_verification: ChannelVerificationStatus::from_db_row(
|
||||
@@ -654,7 +654,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
async fn admin_update_password(&self, did: &Did, password_hash: &str) -> Result<u64, DbError> {
|
||||
let result = sqlx::query!(
|
||||
"UPDATE users SET password_hash = $1 WHERE did = $2",
|
||||
password_hash,
|
||||
password_hash.as_str(),
|
||||
did.as_str()
|
||||
)
|
||||
.execute(&self.pool)
|
||||
@@ -927,7 +927,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
Ok(count.unwrap_or(0) > 0)
|
||||
}
|
||||
|
||||
async fn get_password_hash_by_did(&self, did: &Did) -> Result<Option<String>, DbError> {
|
||||
async fn get_password_hash_by_did(&self, did: &Did) -> Result<Option<PasswordHash>, DbError> {
|
||||
let row = sqlx::query_scalar!(
|
||||
"SELECT password_hash FROM users WHERE did = $1",
|
||||
did.as_str()
|
||||
@@ -936,7 +936,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
|
||||
Ok(row.flatten())
|
||||
Ok(row.flatten().map(PasswordHash::new))
|
||||
}
|
||||
|
||||
async fn get_passkeys_for_user(&self, did: &Did) -> Result<Vec<StoredPasskey>, DbError> {
|
||||
@@ -1431,7 +1431,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
.map(|opt| {
|
||||
opt.map(|r| UserLoginCheck {
|
||||
did: Did::from(r.did),
|
||||
password_hash: r.password_hash,
|
||||
password_hash: r.password_hash.map(PasswordHash::new),
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1460,7 +1460,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
id: row.id,
|
||||
did: Did::from(row.did),
|
||||
email: row.email,
|
||||
password_hash: row.password_hash,
|
||||
password_hash: row.password_hash.map(PasswordHash::new),
|
||||
password_required: row.password_required,
|
||||
two_factor_enabled: row.two_factor_enabled,
|
||||
preferred_comms_channel: row.preferred_comms_channel,
|
||||
@@ -1622,7 +1622,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
id: row.id,
|
||||
did: Did::from(row.did),
|
||||
handle: Handle::from(row.handle),
|
||||
password_hash: row.password_hash,
|
||||
password_hash: row.password_hash.map(PasswordHash::new),
|
||||
email: row.email,
|
||||
deactivated_at: row.deactivated_at,
|
||||
takedown_ref: row.takedown_ref,
|
||||
@@ -1831,7 +1831,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
opt.and_then(|row| {
|
||||
row.password_hash.map(|hash| UserIdAndPasswordHash {
|
||||
id: row.id,
|
||||
password_hash: hash,
|
||||
password_hash: PasswordHash::new(hash),
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1840,11 +1840,11 @@ impl UserRepository for PostgresUserRepository {
|
||||
async fn update_password_hash(
|
||||
&self,
|
||||
user_id: Uuid,
|
||||
password_hash: &str,
|
||||
password_hash: &PasswordHash,
|
||||
) -> Result<(), DbError> {
|
||||
sqlx::query!(
|
||||
"UPDATE users SET password_hash = $1 WHERE id = $2",
|
||||
password_hash,
|
||||
password_hash.as_str(),
|
||||
user_id
|
||||
)
|
||||
.execute(&self.pool)
|
||||
@@ -1856,13 +1856,13 @@ impl UserRepository for PostgresUserRepository {
|
||||
async fn reset_password_with_sessions(
|
||||
&self,
|
||||
user_id: Uuid,
|
||||
password_hash: &str,
|
||||
password_hash: &PasswordHash,
|
||||
) -> Result<PasswordResetResult, DbError> {
|
||||
let mut tx = self.pool.begin().await.map_err(map_sqlx_error)?;
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE users SET password_hash = $1, password_reset_code = NULL, password_reset_code_expires_at = NULL, password_required = TRUE WHERE id = $2",
|
||||
password_hash,
|
||||
password_hash.as_str(),
|
||||
user_id
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
@@ -1950,7 +1950,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
.map(|opt| {
|
||||
opt.map(|row| UserPasswordInfo {
|
||||
id: row.id,
|
||||
password_hash: row.password_hash,
|
||||
password_hash: row.password_hash.map(PasswordHash::new),
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1969,11 +1969,11 @@ impl UserRepository for PostgresUserRepository {
|
||||
async fn set_new_user_password(
|
||||
&self,
|
||||
user_id: Uuid,
|
||||
password_hash: &str,
|
||||
password_hash: &PasswordHash,
|
||||
) -> Result<(), DbError> {
|
||||
sqlx::query!(
|
||||
"UPDATE users SET password_hash = $1, password_required = TRUE WHERE id = $2",
|
||||
password_hash,
|
||||
password_hash.as_str(),
|
||||
user_id
|
||||
)
|
||||
.execute(&self.pool)
|
||||
@@ -2004,7 +2004,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
.map(|opt| {
|
||||
opt.map(|row| UserForDeletion {
|
||||
id: row.id,
|
||||
password_hash: row.password_hash,
|
||||
password_hash: row.password_hash.map(PasswordHash::new),
|
||||
handle: Handle::from(row.handle),
|
||||
})
|
||||
})
|
||||
@@ -3113,7 +3113,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
"INSERT INTO app_passwords (user_id, name, password_hash, privileged) VALUES ($1, $2, $3, FALSE)",
|
||||
input.user_id,
|
||||
input.app_password_name,
|
||||
input.app_password_hash
|
||||
input.app_password_hash.as_str()
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
@@ -3140,7 +3140,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE users SET password_hash = $1, password_required = TRUE, recovery_token = NULL, recovery_token_expires_at = NULL WHERE did = $2",
|
||||
input.password_hash,
|
||||
input.password_hash.as_str(),
|
||||
input.did.as_str()
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
|
||||
@@ -144,7 +144,7 @@ async fn seed_user(repos: &PostgresRepositories, did: &Did, handle: &Handle) ->
|
||||
handle: handle.clone(),
|
||||
email: None,
|
||||
did: did.clone(),
|
||||
password_hash: "parity-test-hash".to_string(),
|
||||
password_hash: tranquil_types::PasswordHash::new("parity-test-hash"),
|
||||
preferred_comms_channel: CommsChannel::Email,
|
||||
discord_username: None,
|
||||
telegram_username: None,
|
||||
|
||||
@@ -29,7 +29,7 @@ use tranquil_db_traits::{
|
||||
use tranquil_oauth::{AuthorizedClientData, DeviceData, RequestData, TokenData};
|
||||
use tranquil_types::{
|
||||
AtUri, AuthorizationCode, CidLink, ClientId, DPoPProofId, DeviceId, Did, Handle, Nsid,
|
||||
RefreshToken, RequestId, Rkey, TokenId,
|
||||
InviteCode, Jti, Nsid, PasswordHash, RefreshToken, RequestId, Rkey, Tid, TokenId,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -1734,7 +1734,9 @@ impl<S: StorageIO + 'static> tranquil_db_traits::SessionRepository for Metastore
|
||||
tx,
|
||||
},
|
||||
))?;
|
||||
recv(rx).await
|
||||
recv(rx)
|
||||
.await
|
||||
.map(|hashes: Vec<String>| hashes.into_iter().map(PasswordHash::new).collect())
|
||||
}
|
||||
|
||||
async fn refresh_session_atomic(
|
||||
@@ -3733,7 +3735,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::UserRepository for MetastoreCli
|
||||
self.pool
|
||||
.send(MetastoreRequest::User(UserRequest::AdminUpdatePassword {
|
||||
did: did.clone(),
|
||||
password_hash: password_hash.to_owned(),
|
||||
password_hash: password_hash.as_str().to_owned(),
|
||||
tx,
|
||||
}))?;
|
||||
recv(rx).await
|
||||
@@ -4032,14 +4034,16 @@ impl<S: StorageIO + 'static> tranquil_db_traits::UserRepository for MetastoreCli
|
||||
recv(rx).await
|
||||
}
|
||||
|
||||
async fn get_password_hash_by_did(&self, did: &Did) -> Result<Option<String>, DbError> {
|
||||
async fn get_password_hash_by_did(&self, did: &Did) -> Result<Option<PasswordHash>, DbError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool
|
||||
.send(MetastoreRequest::User(UserRequest::GetPasswordHashByDid {
|
||||
did: did.clone(),
|
||||
tx,
|
||||
}))?;
|
||||
recv(rx).await
|
||||
recv(rx)
|
||||
.await
|
||||
.map(|hash: Option<String>| hash.map(PasswordHash::new))
|
||||
}
|
||||
|
||||
async fn get_passkeys_for_user(&self, did: &Did) -> Result<Vec<StoredPasskey>, DbError> {
|
||||
@@ -4556,13 +4560,13 @@ impl<S: StorageIO + 'static> tranquil_db_traits::UserRepository for MetastoreCli
|
||||
async fn update_password_hash(
|
||||
&self,
|
||||
user_id: Uuid,
|
||||
password_hash: &str,
|
||||
password_hash: &PasswordHash,
|
||||
) -> Result<(), DbError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool
|
||||
.send(MetastoreRequest::User(UserRequest::UpdatePasswordHash {
|
||||
user_id,
|
||||
password_hash: password_hash.to_owned(),
|
||||
password_hash: password_hash.as_str().to_owned(),
|
||||
tx,
|
||||
}))?;
|
||||
recv(rx).await
|
||||
@@ -4571,13 +4575,13 @@ impl<S: StorageIO + 'static> tranquil_db_traits::UserRepository for MetastoreCli
|
||||
async fn reset_password_with_sessions(
|
||||
&self,
|
||||
user_id: Uuid,
|
||||
password_hash: &str,
|
||||
password_hash: &PasswordHash,
|
||||
) -> Result<PasswordResetResult, DbError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool.send(MetastoreRequest::User(
|
||||
UserRequest::ResetPasswordWithSessions {
|
||||
user_id,
|
||||
password_hash: password_hash.to_owned(),
|
||||
password_hash: password_hash.as_str().to_owned(),
|
||||
tx,
|
||||
},
|
||||
))?;
|
||||
@@ -4645,13 +4649,13 @@ impl<S: StorageIO + 'static> tranquil_db_traits::UserRepository for MetastoreCli
|
||||
async fn set_new_user_password(
|
||||
&self,
|
||||
user_id: Uuid,
|
||||
password_hash: &str,
|
||||
password_hash: &PasswordHash,
|
||||
) -> Result<(), DbError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool
|
||||
.send(MetastoreRequest::User(UserRequest::SetNewUserPassword {
|
||||
user_id,
|
||||
password_hash: password_hash.to_owned(),
|
||||
password_hash: password_hash.as_str().to_owned(),
|
||||
tx,
|
||||
}))?;
|
||||
recv(rx).await
|
||||
|
||||
@@ -455,7 +455,7 @@ mod tests {
|
||||
handle: tranquil_types::Handle::new(handle.to_string()).unwrap(),
|
||||
email: None,
|
||||
did: tranquil_types::Did::new(did.to_string()).unwrap(),
|
||||
password_hash: "test-hash".to_string(),
|
||||
password_hash: tranquil_types::PasswordHash::new("test-hash"),
|
||||
preferred_comms_channel: tranquil_db_traits::CommsChannel::Email,
|
||||
discord_username: None,
|
||||
telegram_username: None,
|
||||
|
||||
@@ -24,7 +24,7 @@ use tranquil_db_traits::{
|
||||
RefreshGraceReplay, RefreshSessionResult, SessionForRefresh, SessionId, SessionListItem,
|
||||
SessionMfaStatus, SessionRefreshData, SessionToken, SessionTokenCreate,
|
||||
};
|
||||
use tranquil_types::Did;
|
||||
use tranquil_types::{Did, Jti, PasswordHash};
|
||||
|
||||
pub struct SessionOps {
|
||||
db: Database,
|
||||
@@ -112,7 +112,7 @@ impl SessionOps {
|
||||
id: v.id,
|
||||
user_id: v.user_id,
|
||||
name: v.name.clone(),
|
||||
password_hash: v.password_hash.clone(),
|
||||
password_hash: PasswordHash::new(v.password_hash.clone()),
|
||||
created_at: DateTime::from_timestamp_millis(v.created_at_ms).unwrap_or_default(),
|
||||
privilege: u8_to_privilege(v.privilege)
|
||||
.unwrap_or(tranquil_db_traits::AppPasswordPrivilege::Standard),
|
||||
@@ -620,7 +620,7 @@ impl SessionOps {
|
||||
id,
|
||||
user_id: data.user_id,
|
||||
name: data.name.clone(),
|
||||
password_hash: data.password_hash.clone(),
|
||||
password_hash: data.password_hash.as_str().to_owned(),
|
||||
created_at_ms: now_ms,
|
||||
privilege: privilege_to_u8(data.privilege),
|
||||
scopes: data.scopes.clone(),
|
||||
|
||||
@@ -38,7 +38,7 @@ use tranquil_db_traits::{
|
||||
UserResetCodeInfo, UserRow, UserSessionInfo, UserStatus, UserVerificationInfo, UserWithKey,
|
||||
WebauthnChallengeType,
|
||||
};
|
||||
use tranquil_types::{CidLink, Did, Handle};
|
||||
use tranquil_types::{CidLink, Did, Handle, Jti, PasswordHash};
|
||||
|
||||
pub struct UserOps {
|
||||
db: Database,
|
||||
@@ -440,7 +440,7 @@ impl UserOps {
|
||||
id: v.id,
|
||||
did: Did::new(v.did.clone())
|
||||
.map_err(|_| MetastoreError::CorruptData("invalid user did"))?,
|
||||
password_hash: v.password_hash.clone(),
|
||||
password_hash: v.password_hash.clone().map(PasswordHash::new),
|
||||
deactivated_at: v
|
||||
.deactivated_at_ms
|
||||
.and_then(DateTime::from_timestamp_millis),
|
||||
@@ -476,7 +476,7 @@ impl UserOps {
|
||||
Ok(UserLoginCheck {
|
||||
did: Did::new(v.did.clone())
|
||||
.map_err(|_| MetastoreError::CorruptData("invalid user did"))?,
|
||||
password_hash: v.password_hash.clone(),
|
||||
password_hash: v.password_hash.clone().map(PasswordHash::new),
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
@@ -493,7 +493,7 @@ impl UserOps {
|
||||
did: Did::new(v.did.clone())
|
||||
.map_err(|_| MetastoreError::CorruptData("invalid user did"))?,
|
||||
email: v.email.clone(),
|
||||
password_hash: v.password_hash.clone(),
|
||||
password_hash: v.password_hash.clone().map(PasswordHash::new),
|
||||
password_required: v.password_required,
|
||||
two_factor_enabled: v.two_factor_enabled,
|
||||
preferred_comms_channel: Self::comms_channel(&v),
|
||||
@@ -625,7 +625,7 @@ impl UserOps {
|
||||
.map(|v| {
|
||||
Ok(UserForDeletion {
|
||||
id: v.id,
|
||||
password_hash: v.password_hash.clone(),
|
||||
password_hash: v.password_hash.clone().map(PasswordHash::new),
|
||||
handle: Handle::new(v.handle.clone())
|
||||
.map_err(|_| MetastoreError::CorruptData("invalid user handle"))?,
|
||||
})
|
||||
@@ -1956,7 +1956,7 @@ impl UserOps {
|
||||
.map_err(|_| MetastoreError::CorruptData("invalid user did"))?,
|
||||
handle: Handle::new(v.handle.clone())
|
||||
.map_err(|_| MetastoreError::CorruptData("invalid user handle"))?,
|
||||
password_hash: v.password_hash.clone(),
|
||||
password_hash: v.password_hash.clone().map(PasswordHash::new),
|
||||
email: v.email.clone(),
|
||||
deactivated_at: v
|
||||
.deactivated_at_ms
|
||||
@@ -2160,7 +2160,7 @@ impl UserOps {
|
||||
.map(|(id, ph)| {
|
||||
Ok(UserIdAndPasswordHash {
|
||||
id,
|
||||
password_hash: ph,
|
||||
password_hash: PasswordHash::new(ph),
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
@@ -2279,7 +2279,7 @@ impl UserOps {
|
||||
.load_user_by_did(did.as_str())?
|
||||
.map(|v| UserPasswordInfo {
|
||||
id: v.id,
|
||||
password_hash: v.password_hash.clone(),
|
||||
password_hash: v.password_hash.clone().map(PasswordHash::new),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -2854,7 +2854,7 @@ impl UserOps {
|
||||
&input.did,
|
||||
&input.handle,
|
||||
input.email.as_deref(),
|
||||
Some(&input.password_hash),
|
||||
Some(input.password_hash.as_str()),
|
||||
input.preferred_comms_channel,
|
||||
input.discord_username.as_deref(),
|
||||
input.telegram_username.as_deref(),
|
||||
@@ -3135,7 +3135,7 @@ impl UserOps {
|
||||
let user_hash = self.resolve_hash(input.did.as_str());
|
||||
|
||||
self.mutate_user(user_hash, |u| {
|
||||
u.password_hash = Some(input.app_password_hash.clone());
|
||||
u.password_hash = Some(input.app_password_hash.as_str().to_owned());
|
||||
u.password_required = false;
|
||||
})?;
|
||||
|
||||
@@ -3173,7 +3173,7 @@ impl UserOps {
|
||||
batch.remove(&self.users, recovery_token_key(user_hash).as_slice());
|
||||
|
||||
if let Some(mut user) = self.load_user(user_hash)? {
|
||||
user.password_hash = Some(input.password_hash.clone());
|
||||
user.password_hash = Some(input.password_hash.as_str().to_owned());
|
||||
user.password_required = true;
|
||||
batch.insert(
|
||||
&self.users,
|
||||
|
||||
Reference in New Issue
Block a user