Browse Source

TwoFactor.find_by_user_and_type take enum parameter not i32

pull/7563/head
Timshel 1 week ago
parent
commit
4845b071bb
  1. 10
      src/api/core/two_factor/authenticator.rs
  2. 6
      src/api/core/two_factor/duo.rs
  3. 24
      src/api/core/two_factor/email.rs
  4. 5
      src/api/core/two_factor/protected_actions.rs
  5. 17
      src/api/core/two_factor/webauthn.rs
  6. 7
      src/api/core/two_factor/yubikey.rs
  7. 4
      src/api/identity.rs
  8. 4
      src/db/models/two_factor.rs

10
src/api/core/two_factor/authenticator.rs

@ -24,8 +24,7 @@ async fn generate_authenticator(data: Json<PasswordOrOtpData>, headers: Headers,
data.validate(&user, false, &conn).await?; data.validate(&user, false, &conn).await?;
let type_ = TwoFactorType::Authenticator as i32; let twofactor = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Authenticator, &conn).await;
let twofactor = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await;
let (enabled, key) = match twofactor { let (enabled, key) = match twofactor {
Some(tf) => (true, tf.data), Some(tf) => (true, tf.data),
@ -117,8 +116,7 @@ pub async fn validate_totp_code(
err!("Invalid TOTP secret") err!("Invalid TOTP secret")
}; };
let mut twofactor = match TwoFactor::find_by_user_and_type(user_id, TwoFactorType::Authenticator as i32, conn).await let mut twofactor = match TwoFactor::find_by_user_and_type(user_id, TwoFactorType::Authenticator, conn).await {
{
Some(tf) => tf, Some(tf) => tf,
_ => TwoFactor::new(user_id.clone(), TwoFactorType::Authenticator, secret.to_owned()), _ => TwoFactor::new(user_id.clone(), TwoFactorType::Authenticator, secret.to_owned()),
}; };
@ -185,9 +183,7 @@ async fn disable_authenticator(data: Json<DisableAuthenticatorData>, headers: He
two_factor::validate_authenticator(&data.user_verification_token, &user.uuid, &data.key, true)?; two_factor::validate_authenticator(&data.user_verification_token, &user.uuid, &data.key, true)?;
if let Some(twofactor) = if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Authenticator, &conn).await {
TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Authenticator as i32, &conn).await
{
if twofactor.data == data.key { if twofactor.data == data.key {
twofactor.delete(&conn).await?; twofactor.delete(&conn).await?;
log_user_event(EventType::UserDisabled2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &conn) log_user_event(EventType::UserDisabled2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &conn)

6
src/api/core/two_factor/duo.rs

@ -149,7 +149,7 @@ async fn activate_duo_put(data: Json<EnableDuoData>, headers: Headers, conn: DbC
async fn disable_duo(data: Json<VerificationTokenData>, headers: Headers, conn: DbConn) -> EmptyResult { async fn disable_duo(data: Json<VerificationTokenData>, headers: Headers, conn: DbConn) -> EmptyResult {
let user = headers.user; let user = headers.user;
if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Duo as i32, &conn).await { if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Duo, &conn).await {
// Apply the same transformation than in `get_duo` to check we are disabling the correct one // Apply the same transformation than in `get_duo` to check we are disabling the correct one
let duo = match to_user_duo_data(&twofactor) { let duo = match to_user_duo_data(&twofactor) {
DuoStatus::Global(_) => Some(DuoData::secret()), DuoStatus::Global(_) => Some(DuoData::secret()),
@ -203,10 +203,8 @@ const DUO_PREFIX: &str = "TX";
const APP_PREFIX: &str = "APP"; const APP_PREFIX: &str = "APP";
async fn get_user_duo_data(user_id: &UserId, conn: &DbConn) -> DuoStatus { async fn get_user_duo_data(user_id: &UserId, conn: &DbConn) -> DuoStatus {
let type_ = TwoFactorType::Duo as i32;
// If the user doesn't have an entry, disabled // If the user doesn't have an entry, disabled
let Some(twofactor) = TwoFactor::find_by_user_and_type(user_id, type_, conn).await else { let Some(twofactor) = TwoFactor::find_by_user_and_type(user_id, TwoFactorType::Duo, conn).await else {
return DuoStatus::Disabled(DuoData::global().is_some()); return DuoStatus::Disabled(DuoData::global().is_some());
}; };

24
src/api/core/two_factor/email.rs

@ -110,8 +110,8 @@ async fn send_email_login(data: Json<SendEmailLoginData>, client_headers: Client
/// Generate the token, save the data for later verification and send email to user /// Generate the token, save the data for later verification and send email to user
pub async fn send_token(user_id: &UserId, conn: &DbConn) -> EmptyResult { pub async fn send_token(user_id: &UserId, conn: &DbConn) -> EmptyResult {
let type_ = TwoFactorType::Email as i32; let mut twofactor =
let mut twofactor = TwoFactor::find_by_user_and_type(user_id, type_, conn).await.map_res("Two factor not found")?; TwoFactor::find_by_user_and_type(user_id, TwoFactorType::Email, conn).await.map_res("Two factor not found")?;
let generated_token = crypto::generate_email_token(CONFIG.email_token_size()); let generated_token = crypto::generate_email_token(CONFIG.email_token_size());
@ -134,7 +134,7 @@ async fn get_email(data: Json<PasswordOrOtpData>, headers: Headers, conn: DbConn
data.validate(&user, false, &conn).await?; data.validate(&user, false, &conn).await?;
let (enabled, mfa_email) = let (enabled, mfa_email) =
if let Some(x) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Email as i32, &conn).await { if let Some(x) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Email, &conn).await {
let twofactor_data = EmailTokenData::from_json(&x.data)?; let twofactor_data = EmailTokenData::from_json(&x.data)?;
(true, Some(twofactor_data.email)) (true, Some(twofactor_data.email))
} else { } else {
@ -170,9 +170,7 @@ async fn send_email(data: Json<SendEmailData>, headers: Headers, conn: DbConn) -
err!("Email 2FA is disabled") err!("Email 2FA is disabled")
} }
let type_ = TwoFactorType::Email as i32; if let Some(tf) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Email, &conn).await {
if let Some(tf) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await {
tf.delete(&conn).await?; tf.delete(&conn).await?;
} }
@ -205,10 +203,9 @@ async fn email(data: Json<EmailData>, headers: Headers, conn: DbConn) -> JsonRes
two_factor::validate_email(&data.user_verification_token, &user.uuid, data.email, false)?; two_factor::validate_email(&data.user_verification_token, &user.uuid, data.email, false)?;
let mut twofactor = let mut twofactor = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::EmailVerificationChallenge, &conn)
TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::EmailVerificationChallenge as i32, &conn) .await
.await .map_res("Two factor not found")?;
.map_res("Two factor not found")?;
let mut email_data = EmailTokenData::from_json(&twofactor.data)?; let mut email_data = EmailTokenData::from_json(&twofactor.data)?;
@ -236,7 +233,7 @@ async fn email(data: Json<EmailData>, headers: Headers, conn: DbConn) -> JsonRes
async fn disable_email(data: Json<VerificationTokenData>, headers: Headers, conn: DbConn) -> EmptyResult { async fn disable_email(data: Json<VerificationTokenData>, headers: Headers, conn: DbConn) -> EmptyResult {
let user = headers.user; let user = headers.user;
if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Email as i32, &conn).await { if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Email, &conn).await {
let twofactor_data = EmailTokenData::from_json(&twofactor.data)?; let twofactor_data = EmailTokenData::from_json(&twofactor.data)?;
two_factor::validate_email(&data.user_verification_token, &user.uuid, twofactor_data.email, true)?; two_factor::validate_email(&data.user_verification_token, &user.uuid, twofactor_data.email, true)?;
@ -261,9 +258,8 @@ pub async fn validate_email_code_str(
conn: &DbConn, conn: &DbConn,
) -> EmptyResult { ) -> EmptyResult {
let mut email_data = EmailTokenData::from_json(data)?; let mut email_data = EmailTokenData::from_json(data)?;
let mut twofactor = TwoFactor::find_by_user_and_type(user_id, TwoFactorType::Email as i32, conn) let mut twofactor =
.await TwoFactor::find_by_user_and_type(user_id, TwoFactorType::Email, conn).await.map_res("Two factor not found")?;
.map_res("Two factor not found")?;
let Some(issued_token) = &email_data.last_token else { let Some(issued_token) = &email_data.last_token else {
err!( err!(
format!("No token available! IP: {ip}"), format!("No token available! IP: {ip}"),

5
src/api/core/two_factor/protected_actions.rs

@ -72,8 +72,7 @@ async fn request_otp(headers: Headers, conn: DbConn) -> EmptyResult {
let user = headers.user; let user = headers.user;
// Only one Protected Action per user is allowed to take place, delete the previous one // Only one Protected Action per user is allowed to take place, delete the previous one
if let Some(pa) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::ProtectedActions as i32, &conn).await if let Some(pa) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::ProtectedActions, &conn).await {
{
let pa_data = ProtectedActionData::from_json(&pa.data)?; let pa_data = ProtectedActionData::from_json(&pa.data)?;
let elapsed = pa_data.time_since_sent().num_seconds(); let elapsed = pa_data.time_since_sent().num_seconds();
let delay = 30; let delay = 30;
@ -125,7 +124,7 @@ pub async fn validate_protected_action_otp(
delete_if_valid: bool, delete_if_valid: bool,
conn: &DbConn, conn: &DbConn,
) -> EmptyResult { ) -> EmptyResult {
let mut pa = TwoFactor::find_by_user_and_type(user_id, TwoFactorType::ProtectedActions as i32, conn) let mut pa = TwoFactor::find_by_user_and_type(user_id, TwoFactorType::ProtectedActions, conn)
.await .await
.map_res("Protected action token not found, try sending the code again or restart the process")?; .map_res("Protected action token not found, try sending the code again or restart the process")?;
let mut pa_data = ProtectedActionData::from_json(&pa.data)?; let mut pa_data = ProtectedActionData::from_json(&pa.data)?;

17
src/api/core/two_factor/webauthn.rs

@ -269,8 +269,9 @@ async fn activate_webauthn(data: Json<EnableWebauthnData>, headers: Headers, con
two_factor::validate_webauthn(&data.user_verification_token, &user.uuid, &keys, false)?; two_factor::validate_webauthn(&data.user_verification_token, &user.uuid, &keys, false)?;
// Retrieve and delete the saved challenge state // Retrieve and delete the saved challenge state
let type_ = TwoFactorType::WebauthnRegisterChallenge as i32; let state = if let Some(tf) =
let state = if let Some(tf) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await { TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::WebauthnRegisterChallenge, &conn).await
{
let state: PasskeyRegistration = serde_json::from_str(&tf.data)?; let state: PasskeyRegistration = serde_json::from_str(&tf.data)?;
tf.delete(&conn).await?; tf.delete(&conn).await?;
state state
@ -317,7 +318,7 @@ async fn activate_webauthn_put(data: Json<EnableWebauthnData>, headers: Headers,
async fn delete_webauthn(data: Json<VerificationTokenData>, headers: Headers, conn: DbConn) -> EmptyResult { async fn delete_webauthn(data: Json<VerificationTokenData>, headers: Headers, conn: DbConn) -> EmptyResult {
let user = headers.user; let user = headers.user;
let Some(tf) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Webauthn as i32, &conn).await else { let Some(tf) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Webauthn, &conn).await else {
err!("Webauthn data not found!") err!("Webauthn data not found!")
}; };
@ -332,7 +333,7 @@ async fn delete_webauthn(data: Json<VerificationTokenData>, headers: Headers, co
let migrated: Vec<WebauthnRegistration> = removed.into_iter().filter(|r| r.migrated).collect(); let migrated: Vec<WebauthnRegistration> = removed.into_iter().filter(|r| r.migrated).collect();
// If entry is migrated from u2f, delete the u2f entry as well // If entry is migrated from u2f, delete the u2f entry as well
if !migrated.is_empty() if !migrated.is_empty()
&& let Some(mut u2f) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::U2f as i32, &conn).await && let Some(mut u2f) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::U2f, &conn).await
{ {
let Ok(mut data) = serde_json::from_str::<Vec<U2FRegistration>>(&u2f.data) else { let Ok(mut data) = serde_json::from_str::<Vec<U2FRegistration>>(&u2f.data) else {
err!("Error parsing U2F data") err!("Error parsing U2F data")
@ -360,8 +361,7 @@ pub async fn get_webauthn_registrations(
user_id: &UserId, user_id: &UserId,
conn: &DbConn, conn: &DbConn,
) -> Result<(bool, Vec<WebauthnRegistration>), Error> { ) -> Result<(bool, Vec<WebauthnRegistration>), Error> {
let type_ = TwoFactorType::Webauthn as i32; match TwoFactor::find_by_user_and_type(user_id, TwoFactorType::Webauthn, conn).await {
match TwoFactor::find_by_user_and_type(user_id, type_, conn).await {
Some(tf) => Ok((tf.enabled, serde_json::from_str(&tf.data)?)), Some(tf) => Ok((tf.enabled, serde_json::from_str(&tf.data)?)),
None => Ok((false, Vec::new())), // If no data, return empty list None => Ok((false, Vec::new())), // If no data, return empty list
} }
@ -408,8 +408,9 @@ pub async fn generate_webauthn_login(user_id: &UserId, conn: &DbConn) -> JsonRes
} }
pub async fn validate_webauthn_login(user_id: &UserId, response: &str, conn: &DbConn) -> EmptyResult { pub async fn validate_webauthn_login(user_id: &UserId, response: &str, conn: &DbConn) -> EmptyResult {
let type_ = TwoFactorType::WebauthnLoginChallenge as i32; let mut state = if let Some(tf) =
let mut state = if let Some(tf) = TwoFactor::find_by_user_and_type(user_id, type_, conn).await { TwoFactor::find_by_user_and_type(user_id, TwoFactorType::WebauthnLoginChallenge, conn).await
{
let state: PasskeyAuthentication = serde_json::from_str(&tf.data)?; let state: PasskeyAuthentication = serde_json::from_str(&tf.data)?;
tf.delete(conn).await?; tf.delete(conn).await?;
state state

7
src/api/core/two_factor/yubikey.rs

@ -127,10 +127,9 @@ async fn generate_yubikey(data: Json<PasswordOrOtpData>, headers: Headers, conn:
data.validate(&user, false, &conn).await?; data.validate(&user, false, &conn).await?;
let user_id = &user.uuid; let user_id = &user.uuid;
let yubikey_type = TwoFactorType::YubiKey as i32;
let (enabled, keys, yubikey_json) = let (enabled, keys, yubikey_json) =
if let Some(r) = TwoFactor::find_by_user_and_type(user_id, yubikey_type, &conn).await { if let Some(r) = TwoFactor::find_by_user_and_type(user_id, TwoFactorType::YubiKey, &conn).await {
let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?; let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?;
let enabled = !yubikey_metadata.keys.is_empty(); let enabled = !yubikey_metadata.keys.is_empty();
let mut result = jsonify_yubikeys(yubikey_metadata.keys.clone()); let mut result = jsonify_yubikeys(yubikey_metadata.keys.clone());
@ -160,7 +159,7 @@ async fn activate_yubikey(data: Json<EnableYubikeyData>, headers: Headers, conn:
// Check if we already have some data // Check if we already have some data
let mut yubikey_data = let mut yubikey_data =
if let Some(yd) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::YubiKey as i32, &conn).await { if let Some(yd) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::YubiKey, &conn).await {
let ym: YubikeyMetadata = serde_json::from_str(&yd.data)?; let ym: YubikeyMetadata = serde_json::from_str(&yd.data)?;
two_factor::validate_yubikey(&data.user_verification_token, &user.uuid, &ym.keys, !ym.keys.is_empty())?; two_factor::validate_yubikey(&data.user_verification_token, &user.uuid, &ym.keys, !ym.keys.is_empty())?;
yd yd
@ -207,7 +206,7 @@ async fn activate_yubikey_put(data: Json<EnableYubikeyData>, headers: Headers, c
async fn delete_yubikeys(data: Json<VerificationTokenData>, headers: Headers, conn: DbConn) -> EmptyResult { async fn delete_yubikeys(data: Json<VerificationTokenData>, headers: Headers, conn: DbConn) -> EmptyResult {
let user = headers.user; let user = headers.user;
if let Some(r) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::YubiKey as i32, &conn).await { if let Some(r) = TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::YubiKey, &conn).await {
let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?; let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?;
two_factor::validate_yubikey(&data.user_verification_token, &user.uuid, &yubikey_metadata.keys, true)?; two_factor::validate_yubikey(&data.user_verification_token, &user.uuid, &yubikey_metadata.keys, true)?;

4
src/api/identity.rs

@ -993,7 +993,7 @@ async fn json_err_twofactor(
} }
Some(tf_type @ TwoFactorType::YubiKey) => { Some(tf_type @ TwoFactorType::YubiKey) => {
let Some(twofactor) = TwoFactor::find_by_user_and_type(user_id, tf_type as i32, conn).await else { let Some(twofactor) = TwoFactor::find_by_user_and_type(user_id, tf_type, conn).await else {
err!("No YubiKey devices registered") err!("No YubiKey devices registered")
}; };
@ -1005,7 +1005,7 @@ async fn json_err_twofactor(
} }
Some(tf_type @ TwoFactorType::Email) => { Some(tf_type @ TwoFactorType::Email) => {
let Some(twofactor) = TwoFactor::find_by_user_and_type(user_id, tf_type as i32, conn).await else { let Some(twofactor) = TwoFactor::find_by_user_and_type(user_id, tf_type, conn).await else {
err!("No twofactor email registered") err!("No twofactor email registered")
}; };

4
src/db/models/two_factor.rs

@ -137,11 +137,11 @@ impl TwoFactor {
.await .await
} }
pub async fn find_by_user_and_type(user_uuid: &UserId, atype: i32, conn: &DbConn) -> Option<Self> { pub async fn find_by_user_and_type(user_uuid: &UserId, atype: TwoFactorType, conn: &DbConn) -> Option<Self> {
conn.run(move |conn| { conn.run(move |conn| {
twofactor::table twofactor::table
.filter(twofactor::user_uuid.eq(user_uuid)) .filter(twofactor::user_uuid.eq(user_uuid))
.filter(twofactor::atype.eq(atype)) .filter(twofactor::atype.eq(atype as i32))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}) })

Loading…
Cancel
Save