use alibi_core::entity::{AuthAccount, AuthUser};
use alibi_core::verification::{VerificationConsumption, VerificationSnapshot};
use alibi_core::{AuthContext, AuthResult};
use std::sync::Arc;
pub async fn get_credential_account<S: alibi_core::AuthSchema>(
ctx: &AuthContext<S>,
user_id: impl AsRef<str>,
) -> AuthResult<Option<S::Account>> {
Ok(ctx
.database
.get_credential_account_record(user_id.as_ref())
.await?
.map(alibi_core::AdapterRecord::into_stored))
}
pub async fn get_credential_password_hash(
ctx: &AuthContext<impl alibi_core::AuthSchema>,
user: &impl AuthUser,
) -> AuthResult<Option<String>> {
Ok(get_credential_account(ctx, user.id())
.await?
.and_then(|account| account.password().map(str::to_owned)))
}
pub async fn user_has_password(
ctx: &AuthContext<impl alibi_core::AuthSchema>,
user: &impl AuthUser,
) -> AuthResult<bool> {
Ok(get_credential_password_hash(ctx, user).await?.is_some())
}
pub(crate) type ResetProofCheck =
Box<dyn Fn(Option<VerificationSnapshot>) -> AuthResult<()> + Send + Sync>;
pub(crate) enum ResetProof {
Spent,
InTransaction {
consumption: VerificationConsumption,
check: ResetProofCheck,
},
}
pub(crate) async fn reset_credential_password<S: alibi_core::AuthSchema>(
ctx: &AuthContext<S>,
user_id: &str,
password_hash: String,
revoke_sessions: bool,
proof: ResetProof,
) -> AuthResult<()> {
let account_id = get_credential_account(ctx, user_id)
.await?
.map(|account| account.id().as_ref().to_owned());
let user_id = user_id.to_owned();
let (consumption, check) = match proof {
ResetProof::Spent => (None, None),
ResetProof::InTransaction { consumption, check } => {
(Some(Arc::new(consumption)), Some(check))
}
};
let spent = consumption.clone();
alibi_core::store::write_transaction(ctx.database.as_ref(), move |tx| {
Box::pin(async move {
if let (Some(consumption), Some(check)) = (&spent, &check) {
check(consumption.consume_in(tx).await?)?;
}
if revoke_sessions {
tx.delete_user_sessions(&user_id).await?;
}
if let Some(account_id) = account_id {
_ = tx
.update_account_record(
&account_id,
alibi_core::UpdateAccount {
password: Some(password_hash),
..Default::default()
},
)
.await?;
} else {
_ = tx
.create_account_record(alibi_core::CreateAccount {
additional_fields: alibi_core::field_policy::FieldValues::default(),
user_id: user_id.clone(),
account_id: user_id,
provider_id: "credential".to_owned(),
access_token: None,
refresh_token: None,
id_token: None,
access_token_expires_at: None,
refresh_token_expires_at: None,
scope: None,
password: Some(password_hash),
})
.await?;
}
Ok(())
})
})
.await?;
if let Some(consumption) = consumption {
consumption.evict().await?;
}
Ok(())
}