mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-05-28 14:33:56 +00:00
Update login API code
- Updated jsonwebtoken to latest version - Trim `username` received from the login form ( Fixes #2348 ) - Make uuid and user_uuid a combined primary key for the devices table ( Fixes #2295 ) - Updated crates including regex which contains a CVE ( https://blog.rust-lang.org/2022/03/08/cve-2022-24713.html )
This commit is contained in:
parent
06f8e69c70
commit
c4d565b15b
14 changed files with 126 additions and 132 deletions
|
@ -98,7 +98,7 @@ async fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> Json
|
|||
crate::ratelimit::check_limit_login(&ip.ip)?;
|
||||
|
||||
// Get the user
|
||||
let username = data.username.as_ref().unwrap();
|
||||
let username = data.username.as_ref().unwrap().trim();
|
||||
let user = match User::find_by_mail(username, &conn).await {
|
||||
Some(user) => user,
|
||||
None => err!("Username or password is incorrect. Try again", format!("IP: {}. Username: {}.", ip.ip, username)),
|
||||
|
@ -266,17 +266,8 @@ async fn get_device(data: &ConnectData, conn: &DbConn, user: &User) -> (Device,
|
|||
|
||||
let mut new_device = false;
|
||||
// Find device or create new
|
||||
let device = match Device::find_by_uuid(&device_id, conn).await {
|
||||
Some(device) => {
|
||||
// Check if owned device, and recreate if not
|
||||
if device.user_uuid != user.uuid {
|
||||
info!("Device exists but is owned by another user. The old device will be discarded");
|
||||
new_device = true;
|
||||
Device::new(device_id, user.uuid.clone(), device_name, device_type)
|
||||
} else {
|
||||
device
|
||||
}
|
||||
}
|
||||
let device = match Device::find_by_uuid_and_user(&device_id, &user.uuid, conn).await {
|
||||
Some(device) => device,
|
||||
None => {
|
||||
new_device = true;
|
||||
Device::new(device_id, user.uuid.clone(), device_name, device_type)
|
||||
|
@ -328,7 +319,7 @@ async fn twofactor_auth(
|
|||
}
|
||||
Some(TwoFactorType::YubiKey) => _tf::yubikey::validate_yubikey_login(twofactor_code, &selected_data?)?,
|
||||
Some(TwoFactorType::Duo) => {
|
||||
_tf::duo::validate_duo_login(data.username.as_ref().unwrap(), twofactor_code, conn).await?
|
||||
_tf::duo::validate_duo_login(data.username.as_ref().unwrap().trim(), twofactor_code, conn).await?
|
||||
}
|
||||
Some(TwoFactorType::Email) => {
|
||||
_tf::email::validate_email_code_str(user_uuid, twofactor_code, &selected_data?, conn).await?
|
||||
|
|
18
src/auth.rs
18
src/auth.rs
|
@ -38,7 +38,7 @@ static PRIVATE_RSA_KEY: Lazy<EncodingKey> = Lazy::new(|| {
|
|||
static PUBLIC_RSA_KEY_VEC: Lazy<Vec<u8>> = Lazy::new(|| {
|
||||
read_file(&CONFIG.public_rsa_key()).unwrap_or_else(|e| panic!("Error loading public RSA Key.\n{}", e))
|
||||
});
|
||||
static PUBLIC_RSA_KEY: Lazy<DecodingKey<'_>> = Lazy::new(|| {
|
||||
static PUBLIC_RSA_KEY: Lazy<DecodingKey> = Lazy::new(|| {
|
||||
DecodingKey::from_rsa_pem(&PUBLIC_RSA_KEY_VEC).unwrap_or_else(|e| panic!("Error decoding public RSA Key.\n{}", e))
|
||||
});
|
||||
|
||||
|
@ -55,15 +55,11 @@ pub fn encode_jwt<T: Serialize>(claims: &T) -> String {
|
|||
}
|
||||
|
||||
fn decode_jwt<T: DeserializeOwned>(token: &str, issuer: String) -> Result<T, Error> {
|
||||
let validation = jsonwebtoken::Validation {
|
||||
leeway: 30, // 30 seconds
|
||||
validate_exp: true,
|
||||
validate_nbf: true,
|
||||
aud: None,
|
||||
iss: Some(issuer),
|
||||
sub: None,
|
||||
algorithms: vec![JWT_ALGORITHM],
|
||||
};
|
||||
let mut validation = jsonwebtoken::Validation::new(JWT_ALGORITHM);
|
||||
validation.leeway = 30; // 30 seconds
|
||||
validation.validate_exp = true;
|
||||
validation.validate_nbf = true;
|
||||
validation.set_issuer(&[issuer]);
|
||||
|
||||
let token = token.replace(char::is_whitespace, "");
|
||||
jsonwebtoken::decode(&token, &PUBLIC_RSA_KEY, &validation).map(|d| d.claims).map_res("Error decoding JWT")
|
||||
|
@ -350,7 +346,7 @@ impl<'r> FromRequest<'r> for Headers {
|
|||
_ => err_handler!("Error getting DB"),
|
||||
};
|
||||
|
||||
let device = match Device::find_by_uuid(&device_uuid, &conn).await {
|
||||
let device = match Device::find_by_uuid_and_user(&device_uuid, &user_uuid, &conn).await {
|
||||
Some(device) => device,
|
||||
None => err_handler!("Invalid device id"),
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@ db_object! {
|
|||
#[table_name = "devices"]
|
||||
#[changeset_options(treat_none_as_null="true")]
|
||||
#[belongs_to(User, foreign_key = "user_uuid")]
|
||||
#[primary_key(uuid)]
|
||||
#[primary_key(uuid, user_uuid)]
|
||||
pub struct Device {
|
||||
pub uuid: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
|
@ -131,32 +131,26 @@ impl Device {
|
|||
postgresql {
|
||||
let value = DeviceDb::to_db(self);
|
||||
crate::util::retry(
|
||||
|| diesel::insert_into(devices::table).values(&value).on_conflict(devices::uuid).do_update().set(&value).execute(conn),
|
||||
|| diesel::insert_into(devices::table).values(&value).on_conflict((devices::uuid, devices::user_uuid)).do_update().set(&value).execute(conn),
|
||||
10,
|
||||
).map_res("Error saving device")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||
pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||
db_run! { conn: {
|
||||
diesel::delete(devices::table.filter(devices::uuid.eq(self.uuid)))
|
||||
diesel::delete(devices::table.filter(devices::user_uuid.eq(user_uuid)))
|
||||
.execute(conn)
|
||||
.map_res("Error removing device")
|
||||
.map_res("Error removing devices for user")
|
||||
}}
|
||||
}
|
||||
|
||||
pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||
for device in Self::find_by_user(user_uuid, conn).await {
|
||||
device.delete(conn).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
|
||||
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Self> {
|
||||
db_run! { conn: {
|
||||
devices::table
|
||||
.filter(devices::uuid.eq(uuid))
|
||||
.filter(devices::user_uuid.eq(user_uuid))
|
||||
.first::<DeviceDb>(conn)
|
||||
.ok()
|
||||
.from_db()
|
||||
|
@ -173,16 +167,6 @@ impl Device {
|
|||
}}
|
||||
}
|
||||
|
||||
pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
||||
db_run! { conn: {
|
||||
devices::table
|
||||
.filter(devices::user_uuid.eq(user_uuid))
|
||||
.load::<DeviceDb>(conn)
|
||||
.expect("Error loading devices")
|
||||
.from_db()
|
||||
}}
|
||||
}
|
||||
|
||||
pub async fn find_latest_active_by_user(user_uuid: &str, conn: &DbConn) -> Option<Self> {
|
||||
db_run! { conn: {
|
||||
devices::table
|
||||
|
|
|
@ -42,7 +42,7 @@ table! {
|
|||
}
|
||||
|
||||
table! {
|
||||
devices (uuid) {
|
||||
devices (uuid, user_uuid) {
|
||||
uuid -> Text,
|
||||
created_at -> Datetime,
|
||||
updated_at -> Datetime,
|
||||
|
|
|
@ -42,7 +42,7 @@ table! {
|
|||
}
|
||||
|
||||
table! {
|
||||
devices (uuid) {
|
||||
devices (uuid, user_uuid) {
|
||||
uuid -> Text,
|
||||
created_at -> Timestamp,
|
||||
updated_at -> Timestamp,
|
||||
|
|
|
@ -42,7 +42,7 @@ table! {
|
|||
}
|
||||
|
||||
table! {
|
||||
devices (uuid) {
|
||||
devices (uuid, user_uuid) {
|
||||
uuid -> Text,
|
||||
created_at -> Timestamp,
|
||||
updated_at -> Timestamp,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue