mirror of
https://github.com/dani-garcia/vaultwarden.wiki.git
synced 2026-11-05 06:08:36 +02:00
* Fix several scheduled job issues Several jobs could cause a panic, or stop without running through all items because of this panic. This could cause missed items until that panic somehow resolved it self, and probably would never work fully again. This could cause emergency access for example to never work again. Besides this, emergency access was also able to be panic if the wait-time was out-of-bounds and date/time calculation might fail. This is also fixed now by using `checked` functions and continue upon those issues so all awaiting items will be handled. Signed-off-by: BlackDex <black.dex@gmail.com> * Fix several possible panics and overflows Adjust several integer types to be a lower or unsigned type. Most types were too large, only because the function which used this value needed that specific type. But, this allowed too large values which either caused panics or might cause a DoS. This does however might possible cause configurations which have larger values, which are probably way too large anyway to be not loaded or use their defaults. During the config loading, either via env or json, it will show error logs regarding these items. Also fixed an issue with protected actions via OTP, it increased the attempt before it validated if the attempt limit was reached. This could cause an issue when set to `1`, that way it was never allowed, and `3` times would actually be `2`. Also added several config validation checks which otherwise would have prevent Vaultwarden to start, or login, so these will probably not cause issues, else users would already have issues. Signed-off-by: BlackDex <black.dex@gmail.com> * Show config errors on diagnostics page With the previous commits it is now possible to add a warning if their rare issues with loading of the config, either environment variables which we were not able to parse correctly, or if loading the `config.json` failed instead of just showing it wasn't used, while it does exists, which might be confusing. Signed-off-by: BlackDex <black.dex@gmail.com> --------- Signed-off-by: BlackDex <black.dex@gmail.com>
123 lines
4.3 KiB
Rust
123 lines
4.3 KiB
Rust
use chrono::{NaiveDateTime, Utc};
|
|
use diesel::prelude::*;
|
|
|
|
use crate::{
|
|
CONFIG,
|
|
api::EmptyResult,
|
|
auth::ClientIp,
|
|
db::{
|
|
DbConn,
|
|
models::{DeviceId, UserId},
|
|
schema::twofactor_incomplete,
|
|
},
|
|
error::MapResult,
|
|
};
|
|
|
|
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
|
|
#[diesel(table_name = twofactor_incomplete)]
|
|
#[diesel(primary_key(user_uuid, device_uuid))]
|
|
pub struct TwoFactorIncomplete {
|
|
pub user_uuid: UserId,
|
|
// This device UUID is simply what's claimed by the device. It doesn't
|
|
// necessarily correspond to any UUID in the devices table, since a device
|
|
// must complete 2FA login before being added into the devices table.
|
|
pub device_uuid: DeviceId,
|
|
pub device_name: String,
|
|
pub device_type: i32,
|
|
pub login_time: NaiveDateTime,
|
|
pub ip_address: String,
|
|
}
|
|
|
|
impl TwoFactorIncomplete {
|
|
pub async fn mark_incomplete(
|
|
user_uuid: &UserId,
|
|
device_uuid: &DeviceId,
|
|
device_name: &str,
|
|
device_type: i32,
|
|
ip: &ClientIp,
|
|
conn: &DbConn,
|
|
) -> EmptyResult {
|
|
if CONFIG.incomplete_2fa_time_limit() == 0 || !CONFIG.mail_enabled() {
|
|
return Ok(());
|
|
}
|
|
|
|
// Don't update the data for an existing user/device pair, since that
|
|
// would allow an attacker to arbitrarily delay notifications by
|
|
// sending repeated 2FA attempts to reset the timer.
|
|
let existing = Self::find_by_user_and_device(user_uuid, device_uuid, conn).await;
|
|
if existing.is_some() {
|
|
return Ok(());
|
|
}
|
|
|
|
conn.run(move |conn| {
|
|
diesel::insert_into(twofactor_incomplete::table)
|
|
.values((
|
|
twofactor_incomplete::user_uuid.eq(user_uuid),
|
|
twofactor_incomplete::device_uuid.eq(device_uuid),
|
|
twofactor_incomplete::device_name.eq(device_name),
|
|
twofactor_incomplete::device_type.eq(device_type),
|
|
twofactor_incomplete::login_time.eq(Utc::now().naive_utc()),
|
|
twofactor_incomplete::ip_address.eq(ip.ip.to_string()),
|
|
))
|
|
.execute(conn)
|
|
.map_res("Error adding twofactor_incomplete record")
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn mark_complete(user_uuid: &UserId, device_uuid: &DeviceId, conn: &DbConn) -> EmptyResult {
|
|
if CONFIG.incomplete_2fa_time_limit() == 0 || !CONFIG.mail_enabled() {
|
|
return Ok(());
|
|
}
|
|
|
|
Self::delete_by_user_and_device(user_uuid, device_uuid, conn).await
|
|
}
|
|
|
|
pub async fn find_by_user_and_device(user_uuid: &UserId, device_uuid: &DeviceId, conn: &DbConn) -> Option<Self> {
|
|
conn.run(move |conn| {
|
|
twofactor_incomplete::table
|
|
.filter(twofactor_incomplete::user_uuid.eq(user_uuid))
|
|
.filter(twofactor_incomplete::device_uuid.eq(device_uuid))
|
|
.first::<Self>(conn)
|
|
.ok()
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn find_logins_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec<Self> {
|
|
conn.run(move |conn| {
|
|
twofactor_incomplete::table
|
|
.filter(twofactor_incomplete::login_time.lt(dt))
|
|
.load::<Self>(conn)
|
|
.expect("Error loading twofactor_incomplete")
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn delete(self, conn: &DbConn) -> EmptyResult {
|
|
Self::delete_by_user_and_device(&self.user_uuid, &self.device_uuid, conn).await
|
|
}
|
|
|
|
pub async fn delete_by_user_and_device(user_uuid: &UserId, device_uuid: &DeviceId, conn: &DbConn) -> EmptyResult {
|
|
conn.run(move |conn| {
|
|
diesel::delete(
|
|
twofactor_incomplete::table
|
|
.filter(twofactor_incomplete::user_uuid.eq(user_uuid))
|
|
.filter(twofactor_incomplete::device_uuid.eq(device_uuid)),
|
|
)
|
|
.execute(conn)
|
|
.map_res("Error in twofactor_incomplete::delete_by_user_and_device()")
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult {
|
|
conn.run(move |conn| {
|
|
diesel::delete(twofactor_incomplete::table.filter(twofactor_incomplete::user_uuid.eq(user_uuid)))
|
|
.execute(conn)
|
|
.map_res("Error in twofactor_incomplete::delete_all_by_user()")
|
|
})
|
|
.await
|
|
}
|
|
}
|