mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-05-12 16:01:54 +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:
142
Cargo.lock
generated
142
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
14
Cargo.toml
@ -34,14 +34,14 @@ syslog = "4.0.1" # Needs to be v4 until fern is updated
|
|||||||
# Logging
|
# Logging
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
fern = { version = "0.6.0", features = ["syslog-4"] }
|
fern = { version = "0.6.0", features = ["syslog-4"] }
|
||||||
tracing = { version = "0.1.31", features = ["log"] } # Needed to have lettre and webauthn-rs trace logging to work
|
tracing = { version = "0.1.32", features = ["log"] } # Needed to have lettre and webauthn-rs trace logging to work
|
||||||
backtrace = "0.3.64" # Logging panics to logfile instead stderr only
|
backtrace = "0.3.64" # Logging panics to logfile instead stderr only
|
||||||
|
|
||||||
# A `dotenv` implementation for Rust
|
# A `dotenv` implementation for Rust
|
||||||
dotenv = { version = "0.15.0", default-features = false }
|
dotenv = { version = "0.15.0", default-features = false }
|
||||||
|
|
||||||
# Lazy initialization
|
# Lazy initialization
|
||||||
once_cell = "1.9.0"
|
once_cell = "1.10.0"
|
||||||
|
|
||||||
# Numerical libraries
|
# Numerical libraries
|
||||||
num-traits = "0.2.14"
|
num-traits = "0.2.14"
|
||||||
@ -89,7 +89,7 @@ job_scheduler = "1.2.1"
|
|||||||
data-encoding = "2.3.2"
|
data-encoding = "2.3.2"
|
||||||
|
|
||||||
# JWT library
|
# JWT library
|
||||||
jsonwebtoken = "7.2.0"
|
jsonwebtoken = "8.0.1"
|
||||||
|
|
||||||
# TOTP library
|
# TOTP library
|
||||||
totp-lite = "1.0.3"
|
totp-lite = "1.0.3"
|
||||||
@ -110,17 +110,17 @@ idna = "0.2.3" # Punycode conversion
|
|||||||
percent-encoding = "2.1.0" # URL encoding library used for URL's in the emails
|
percent-encoding = "2.1.0" # URL encoding library used for URL's in the emails
|
||||||
|
|
||||||
# Template library
|
# Template library
|
||||||
handlebars = { version = "4.2.1", features = ["dir_source"] }
|
handlebars = { version = "4.2.2", features = ["dir_source"] }
|
||||||
|
|
||||||
# HTTP client
|
# HTTP client
|
||||||
reqwest = { version = "0.11.9", features = ["stream", "json", "gzip", "brotli", "socks", "cookies", "trust-dns"] }
|
reqwest = { version = "0.11.9", features = ["stream", "json", "gzip", "brotli", "socks", "cookies", "trust-dns"] }
|
||||||
|
|
||||||
# For favicon extraction from main website
|
# For favicon extraction from main website
|
||||||
html5gum = "0.4.0"
|
html5gum = "0.4.0"
|
||||||
regex = { version = "1.5.4", features = ["std", "perf", "unicode-perl"], default-features = false }
|
regex = { version = "1.5.5", features = ["std", "perf", "unicode-perl"], default-features = false }
|
||||||
data-url = "0.1.1"
|
data-url = "0.1.1"
|
||||||
bytes = "1.1.0"
|
bytes = "1.1.0"
|
||||||
cached = "0.30.0"
|
cached = "0.34.0"
|
||||||
|
|
||||||
# Used for custom short lived cookie jar during favicon extraction
|
# Used for custom short lived cookie jar during favicon extraction
|
||||||
cookie = "0.15.1"
|
cookie = "0.15.1"
|
||||||
@ -140,7 +140,7 @@ governor = "0.4.2"
|
|||||||
ctrlc = { version = "3.2.1", features = ["termination"] }
|
ctrlc = { version = "3.2.1", features = ["termination"] }
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
rocket = { git = 'https://github.com/SergioBenitez/Rocket', rev = '91e3b4397a1637d0f55f23db712cf7bda0c7f891' }
|
rocket = { git = 'https://github.com/SergioBenitez/Rocket', rev = 'ae0ccf43f11be5c00bb9cd49996c8bb06a7e1651' }
|
||||||
|
|
||||||
# The maintainer of the `job_scheduler` crate doesn't seem to have responded
|
# The maintainer of the `job_scheduler` crate doesn't seem to have responded
|
||||||
# to any issues or PRs for almost a year (as of April 2021). This hopefully
|
# to any issues or PRs for almost a year (as of April 2021). This hopefully
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
-- First remove the previous primary key
|
||||||
|
ALTER TABLE devices DROP PRIMARY KEY;
|
||||||
|
-- Add a new combined one
|
||||||
|
ALTER TABLE devices ADD PRIMARY KEY (uuid, user_uuid);
|
@ -0,0 +1,4 @@
|
|||||||
|
-- First remove the previous primary key
|
||||||
|
ALTER TABLE devices DROP CONSTRAINT devices_pkey;
|
||||||
|
-- Add a new combined one
|
||||||
|
ALTER TABLE devices ADD PRIMARY KEY (uuid, user_uuid);
|
@ -0,0 +1,23 @@
|
|||||||
|
-- Create new devices table with primary keys on both uuid and user_uuid
|
||||||
|
CREATE TABLE devices_new (
|
||||||
|
uuid TEXT NOT NULL,
|
||||||
|
created_at DATETIME NOT NULL,
|
||||||
|
updated_at DATETIME NOT NULL,
|
||||||
|
user_uuid TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
atype INTEGER NOT NULL,
|
||||||
|
push_token TEXT,
|
||||||
|
refresh_token TEXT NOT NULL,
|
||||||
|
twofactor_remember TEXT,
|
||||||
|
PRIMARY KEY(uuid, user_uuid),
|
||||||
|
FOREIGN KEY(user_uuid) REFERENCES users(uuid)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Transfer current data to new table
|
||||||
|
INSERT INTO devices_new SELECT * FROM devices;
|
||||||
|
|
||||||
|
-- Drop the old table
|
||||||
|
DROP TABLE devices;
|
||||||
|
|
||||||
|
-- Rename the new table to the original name
|
||||||
|
ALTER TABLE devices_new RENAME TO devices;
|
@ -98,7 +98,7 @@ async fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> Json
|
|||||||
crate::ratelimit::check_limit_login(&ip.ip)?;
|
crate::ratelimit::check_limit_login(&ip.ip)?;
|
||||||
|
|
||||||
// Get the user
|
// 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 {
|
let user = match User::find_by_mail(username, &conn).await {
|
||||||
Some(user) => user,
|
Some(user) => user,
|
||||||
None => err!("Username or password is incorrect. Try again", format!("IP: {}. Username: {}.", ip.ip, username)),
|
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;
|
let mut new_device = false;
|
||||||
// Find device or create new
|
// Find device or create new
|
||||||
let device = match Device::find_by_uuid(&device_id, conn).await {
|
let device = match Device::find_by_uuid_and_user(&device_id, &user.uuid, conn).await {
|
||||||
Some(device) => {
|
Some(device) => 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
None => {
|
||||||
new_device = true;
|
new_device = true;
|
||||||
Device::new(device_id, user.uuid.clone(), device_name, device_type)
|
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::YubiKey) => _tf::yubikey::validate_yubikey_login(twofactor_code, &selected_data?)?,
|
||||||
Some(TwoFactorType::Duo) => {
|
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) => {
|
Some(TwoFactorType::Email) => {
|
||||||
_tf::email::validate_email_code_str(user_uuid, twofactor_code, &selected_data?, conn).await?
|
_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(|| {
|
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))
|
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))
|
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> {
|
fn decode_jwt<T: DeserializeOwned>(token: &str, issuer: String) -> Result<T, Error> {
|
||||||
let validation = jsonwebtoken::Validation {
|
let mut validation = jsonwebtoken::Validation::new(JWT_ALGORITHM);
|
||||||
leeway: 30, // 30 seconds
|
validation.leeway = 30; // 30 seconds
|
||||||
validate_exp: true,
|
validation.validate_exp = true;
|
||||||
validate_nbf: true,
|
validation.validate_nbf = true;
|
||||||
aud: None,
|
validation.set_issuer(&[issuer]);
|
||||||
iss: Some(issuer),
|
|
||||||
sub: None,
|
|
||||||
algorithms: vec![JWT_ALGORITHM],
|
|
||||||
};
|
|
||||||
|
|
||||||
let token = token.replace(char::is_whitespace, "");
|
let token = token.replace(char::is_whitespace, "");
|
||||||
jsonwebtoken::decode(&token, &PUBLIC_RSA_KEY, &validation).map(|d| d.claims).map_res("Error decoding JWT")
|
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"),
|
_ => 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,
|
Some(device) => device,
|
||||||
None => err_handler!("Invalid device id"),
|
None => err_handler!("Invalid device id"),
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,7 @@ db_object! {
|
|||||||
#[table_name = "devices"]
|
#[table_name = "devices"]
|
||||||
#[changeset_options(treat_none_as_null="true")]
|
#[changeset_options(treat_none_as_null="true")]
|
||||||
#[belongs_to(User, foreign_key = "user_uuid")]
|
#[belongs_to(User, foreign_key = "user_uuid")]
|
||||||
#[primary_key(uuid)]
|
#[primary_key(uuid, user_uuid)]
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
pub uuid: String,
|
pub uuid: String,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
@ -131,32 +131,26 @@ impl Device {
|
|||||||
postgresql {
|
postgresql {
|
||||||
let value = DeviceDb::to_db(self);
|
let value = DeviceDb::to_db(self);
|
||||||
crate::util::retry(
|
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,
|
10,
|
||||||
).map_res("Error saving device")
|
).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: {
|
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)
|
.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 {
|
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Self> {
|
||||||
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> {
|
|
||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
devices::table
|
devices::table
|
||||||
.filter(devices::uuid.eq(uuid))
|
.filter(devices::uuid.eq(uuid))
|
||||||
|
.filter(devices::user_uuid.eq(user_uuid))
|
||||||
.first::<DeviceDb>(conn)
|
.first::<DeviceDb>(conn)
|
||||||
.ok()
|
.ok()
|
||||||
.from_db()
|
.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> {
|
pub async fn find_latest_active_by_user(user_uuid: &str, conn: &DbConn) -> Option<Self> {
|
||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
devices::table
|
devices::table
|
||||||
|
@ -42,7 +42,7 @@ table! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
devices (uuid) {
|
devices (uuid, user_uuid) {
|
||||||
uuid -> Text,
|
uuid -> Text,
|
||||||
created_at -> Datetime,
|
created_at -> Datetime,
|
||||||
updated_at -> Datetime,
|
updated_at -> Datetime,
|
||||||
|
@ -42,7 +42,7 @@ table! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
devices (uuid) {
|
devices (uuid, user_uuid) {
|
||||||
uuid -> Text,
|
uuid -> Text,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
updated_at -> Timestamp,
|
updated_at -> Timestamp,
|
||||||
|
@ -42,7 +42,7 @@ table! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
devices (uuid) {
|
devices (uuid, user_uuid) {
|
||||||
uuid -> Text,
|
uuid -> Text,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
updated_at -> Timestamp,
|
updated_at -> Timestamp,
|
||||||
|
Reference in New Issue
Block a user