Registry testing - part 5
This commit is contained in:
@@ -16,7 +16,9 @@ use utils::sanitize_email;
|
||||
impl LdapDirectory {
|
||||
pub async fn authenticate(&self, credentials: &Credentials) -> trc::Result<Account> {
|
||||
let (username, secret) = match credentials {
|
||||
Credentials::Basic { username, secret } => (username, secret),
|
||||
Credentials::Basic {
|
||||
username, secret, ..
|
||||
} => (username, secret),
|
||||
Credentials::Bearer { token, .. } => (token, token),
|
||||
};
|
||||
let mut conn = self.pool.get().await.map_err(|err| err.into_error())?;
|
||||
|
||||
@@ -13,7 +13,9 @@ use utils::sanitize_email;
|
||||
impl SqlDirectory {
|
||||
pub async fn authenticate(&self, credentials: &Credentials) -> trc::Result<Account> {
|
||||
let (username, secret) = match credentials {
|
||||
Credentials::Basic { username, secret } => (username, secret),
|
||||
Credentials::Basic {
|
||||
username, secret, ..
|
||||
} => (username, secret),
|
||||
Credentials::Bearer { .. } => {
|
||||
return Err(trc::AuthEvent::Error
|
||||
.into_err()
|
||||
|
||||
@@ -33,4 +33,9 @@ impl Directory {
|
||||
pub fn can_lookup_recipients(&self) -> bool {
|
||||
!matches!(self, Directory::OpenId(_))
|
||||
}
|
||||
|
||||
pub fn oidc_authorization_endpoint(&self) -> Option<String> {
|
||||
let todo = "implement";
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,11 @@ impl Credentials {
|
||||
|
||||
match (String::from_utf8(username), String::from_utf8(secret)) {
|
||||
(Ok(username), Ok(secret)) if !username.is_empty() && !secret.is_empty() => {
|
||||
Some(Credentials::Basic { username, secret })
|
||||
Some(Credentials::Basic {
|
||||
username,
|
||||
secret,
|
||||
mfa_token: None,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
||||
@@ -22,27 +22,35 @@ use sha2::Sha512;
|
||||
use tokio::sync::oneshot;
|
||||
use totp_rs::TOTP;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SecretVerificationResult {
|
||||
Valid,
|
||||
Invalid,
|
||||
MissingMfaToken,
|
||||
}
|
||||
|
||||
pub async fn verify_mfa_secret_hash(
|
||||
otp_auth: Option<&str>,
|
||||
totp_uri: Option<&str>,
|
||||
totp_token: Option<&str>,
|
||||
hashed_secret: &str,
|
||||
secret: &str,
|
||||
) -> trc::Result<bool> {
|
||||
if let Some(otp_auth) = otp_auth {
|
||||
if let Some((code, totp_token)) = secret.rsplit_once('$').filter(|(c, t)| {
|
||||
!c.is_empty()
|
||||
&& (6..=8).contains(&t.len())
|
||||
&& t.as_bytes().iter().all(|b| b.is_ascii_digit())
|
||||
}) {
|
||||
let result = verify_secret_hash(hashed_secret, code.as_bytes()).await?
|
||||
&& TOTP::from_url(otp_auth)
|
||||
) -> trc::Result<SecretVerificationResult> {
|
||||
if let Some(totp_uri) = totp_uri {
|
||||
if let Some(totp_token) = totp_token {
|
||||
let result = verify_secret_hash(hashed_secret, secret.as_bytes()).await?
|
||||
&& TOTP::from_url(totp_uri)
|
||||
.map_err(|err| {
|
||||
trc::AuthEvent::Error
|
||||
.reason(err)
|
||||
.details(otp_auth.to_string())
|
||||
.details(totp_uri.to_string())
|
||||
})?
|
||||
.check_current(totp_token)
|
||||
.unwrap_or(false);
|
||||
Ok(result)
|
||||
Ok(if result {
|
||||
SecretVerificationResult::Valid
|
||||
} else {
|
||||
SecretVerificationResult::Invalid
|
||||
})
|
||||
} else if !hashed_secret.is_empty()
|
||||
&& !secret.is_empty()
|
||||
&& verify_secret_hash(hashed_secret, secret.as_bytes()).await?
|
||||
@@ -50,18 +58,22 @@ pub async fn verify_mfa_secret_hash(
|
||||
// Only let the client know if the TOTP code is missing
|
||||
// if the password is correct
|
||||
|
||||
Err(trc::AuthEvent::MissingTotp.into_err())
|
||||
Ok(SecretVerificationResult::MissingMfaToken)
|
||||
} else {
|
||||
Ok(false)
|
||||
Ok(SecretVerificationResult::Invalid)
|
||||
}
|
||||
} else if !hashed_secret.is_empty() && !secret.is_empty() {
|
||||
verify_secret_hash(hashed_secret, secret.as_bytes()).await
|
||||
if verify_secret_hash(hashed_secret, secret.as_bytes()).await? {
|
||||
Ok(SecretVerificationResult::Valid)
|
||||
} else {
|
||||
Ok(SecretVerificationResult::Invalid)
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
Ok(SecretVerificationResult::Invalid)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_otp_auth(otp_auth: Option<&str>, otp_code: Option<&str>) -> trc::Result<bool> {
|
||||
/*pub fn verify_otp_auth(otp_auth: Option<&str>, otp_code: Option<&str>) -> trc::Result<bool> {
|
||||
if let Some(otp_auth) = otp_auth {
|
||||
if let Some(otp_code) = otp_code {
|
||||
TOTP::from_url(otp_auth)
|
||||
@@ -82,7 +94,7 @@ pub fn verify_otp_auth(otp_auth: Option<&str>, otp_code: Option<&str>) -> trc::R
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
async fn verify_hash_prefix(hashed_secret: &str, secret: &[u8]) -> trc::Result<bool> {
|
||||
if hashed_secret.starts_with("$argon2")
|
||||
@@ -247,7 +259,7 @@ pub async fn verify_secret_hash(hashed_secret: &str, secret: &[u8]) -> trc::Resu
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn hash_secret(algorithm: PasswordHashAlgorithm, secret: String) -> trc::Result<String> {
|
||||
pub async fn hash_secret(algorithm: PasswordHashAlgorithm, secret: Vec<u8>) -> trc::Result<String> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
@@ -257,12 +269,12 @@ pub async fn hash_secret(algorithm: PasswordHashAlgorithm, secret: String) -> tr
|
||||
PasswordHashAlgorithm::Argon2id => {
|
||||
let hasher = Argon2::default();
|
||||
hasher
|
||||
.hash_password(secret.as_bytes(), &salt)
|
||||
.hash_password(secret.as_slice(), &salt)
|
||||
.map(|h| h.to_string())
|
||||
}
|
||||
PasswordHashAlgorithm::Bcrypt => {
|
||||
return tx
|
||||
.send(bcrypt::hash(secret.as_bytes()).map_err(|err| {
|
||||
.send(bcrypt::hash(secret.as_slice()).map_err(|err| {
|
||||
trc::AuthEvent::Error
|
||||
.reason(err)
|
||||
.details("Bcrypt hash failed")
|
||||
@@ -271,10 +283,10 @@ pub async fn hash_secret(algorithm: PasswordHashAlgorithm, secret: String) -> tr
|
||||
.unwrap_or(());
|
||||
}
|
||||
PasswordHashAlgorithm::Scrypt => Scrypt
|
||||
.hash_password(secret.as_bytes(), &salt)
|
||||
.hash_password(secret.as_slice(), &salt)
|
||||
.map(|h| h.to_string()),
|
||||
PasswordHashAlgorithm::Pbkdf2 => Pbkdf2
|
||||
.hash_password(secret.as_bytes(), &salt)
|
||||
.hash_password(secret.as_slice(), &salt)
|
||||
.map(|h| h.to_string()),
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ pub enum Credentials {
|
||||
Basic {
|
||||
username: String,
|
||||
secret: String,
|
||||
mfa_token: Option<String>,
|
||||
},
|
||||
Bearer {
|
||||
username: Option<String>,
|
||||
|
||||
Reference in New Issue
Block a user