mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2026-08-30 23:28:29 +00:00
Switch to Edition 2024, more clippy lints, and less macro calls (#7200)
* Update to Rust 2024 Edition Updated to the Rust 2024 Edition and added and fixed several lint checks. This is a large change which, because of the extra lints, added some possible fixes for issues. Signed-off-by: BlackDex <black.dex@gmail.com> * Reorder and merge imports Signed-off-by: BlackDex <black.dex@gmail.com> * Remove "db_run!" macro calls where possible Signed-off-by: BlackDex <black.dex@gmail.com> --------- Signed-off-by: BlackDex <black.dex@gmail.com>
This commit is contained in:
+54
-57
@@ -1,24 +1,28 @@
|
||||
//
|
||||
// Web Headers and caching
|
||||
//
|
||||
use std::{collections::HashMap, io::Cursor, path::Path};
|
||||
use std::{collections::HashMap, env, fmt, io::Cursor, path::Path, str::FromStr};
|
||||
|
||||
use chrono::{DateTime, Local, NaiveDateTime, TimeZone};
|
||||
use num_traits::ToPrimitive;
|
||||
use tokio::{
|
||||
runtime::Handle,
|
||||
time::{Duration, sleep},
|
||||
};
|
||||
|
||||
use serde::de::{self, DeserializeOwned, Deserializer, MapAccess, SeqAccess, Visitor};
|
||||
use serde_json::Value;
|
||||
|
||||
use rocket::{
|
||||
Data, Orbit, Request, Response, Rocket,
|
||||
fairing::{Fairing, Info, Kind},
|
||||
http::{ContentType, Header, HeaderMap, Method, Status},
|
||||
response::{self, Responder},
|
||||
Data, Orbit, Request, Response, Rocket,
|
||||
};
|
||||
|
||||
use tokio::{
|
||||
runtime::Handle,
|
||||
time::{sleep, Duration},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
config::{PathType, SUPPORTED_FEATURE_FLAGS},
|
||||
CONFIG,
|
||||
config::{PathType, SUPPORTED_FEATURE_FLAGS},
|
||||
};
|
||||
|
||||
pub struct AppHeaders();
|
||||
@@ -75,11 +79,16 @@ impl Fairing for AppHeaders {
|
||||
// Do not send the Content-Security-Policy (CSP) Header and X-Frame-Options for the *-connector.html files.
|
||||
// This can cause issues when some MFA requests needs to open a popup or page within the clients like WebAuthn, or Duo.
|
||||
// This is the same behavior as upstream Bitwarden.
|
||||
if !req_uri_path.ends_with("connector.html") {
|
||||
if req_uri_path.ends_with("connector.html") {
|
||||
// It looks like this header get's set somewhere else also, make sure this is not sent for these files, it will cause MFA issues.
|
||||
res.remove_header("X-Frame-Options");
|
||||
} else {
|
||||
let csp = if is_image {
|
||||
// Prevent scripts, frames, objects, etc., from loading with images, mainly for SVG images, since these could contain JavaScript and other unsafe items.
|
||||
// Even though we sanitize SVG images before storing and viewing them, it's better to prevent allowing these elements.
|
||||
String::from("default-src 'none'; img-src 'self' data:; style-src 'unsafe-inline'; script-src 'none'; frame-src 'none'; object-src 'none")
|
||||
String::from(
|
||||
"default-src 'none'; img-src 'self' data:; style-src 'unsafe-inline'; script-src 'none'; frame-src 'none'; object-src 'none",
|
||||
)
|
||||
} else {
|
||||
// # Frame Ancestors:
|
||||
// Chrome Web Store: https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb
|
||||
@@ -129,9 +138,6 @@ impl Fairing for AppHeaders {
|
||||
|
||||
res.set_raw_header("Content-Security-Policy", csp);
|
||||
res.set_raw_header("X-Frame-Options", "SAMEORIGIN");
|
||||
} else {
|
||||
// It looks like this header get's set somewhere else also, make sure this is not sent for these files, it will cause MFA issues.
|
||||
res.remove_header("X-Frame-Options");
|
||||
}
|
||||
|
||||
// Disable cache unless otherwise specified
|
||||
@@ -146,7 +152,7 @@ pub struct Cors();
|
||||
impl Cors {
|
||||
fn get_header(headers: &HeaderMap<'_>, name: &str) -> String {
|
||||
match headers.get_one(name) {
|
||||
Some(h) => h.to_string(),
|
||||
Some(h) => h.to_owned(),
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
@@ -212,7 +218,7 @@ impl<R> Cached<R> {
|
||||
Self {
|
||||
response,
|
||||
is_immutable,
|
||||
ttl: 604800, // 7 days
|
||||
ttl: 604_800, // 7 days
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +292,7 @@ impl Fairing for BetterLogging {
|
||||
} else {
|
||||
"http"
|
||||
};
|
||||
let addr = format!("{scheme}://{}:{}", &config.address, &config.port);
|
||||
let addr = format!("{scheme}://{}:{}", config.address, config.port);
|
||||
info!(target: "start", "Rocket has launched from {addr}");
|
||||
}
|
||||
|
||||
@@ -303,7 +309,7 @@ impl Fairing for BetterLogging {
|
||||
match uri.query() {
|
||||
Some(q) => info!(target: "request", "{method} {uri_path_str}?{}", &q[..q.len().min(30)]),
|
||||
None => info!(target: "request", "{method} {uri_path_str}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,10 +322,10 @@ impl Fairing for BetterLogging {
|
||||
let uri_subpath = uri_path_str.strip_prefix(&CONFIG.domain_path()).unwrap_or(&uri_path_str);
|
||||
if self.0 || LOGGED_ROUTES.iter().any(|r| uri_subpath.starts_with(r)) {
|
||||
let status = response.status();
|
||||
if let Some(ref route) = request.route() {
|
||||
info!(target: "response", "{route} => {status}")
|
||||
if let Some(route) = request.route() {
|
||||
info!(target: "response", "{route} => {status}");
|
||||
} else {
|
||||
info!(target: "response", "{status}")
|
||||
info!(target: "response", "{status}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,9 +360,6 @@ pub fn get_uuid() -> String {
|
||||
//
|
||||
// String util methods
|
||||
//
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
#[inline]
|
||||
pub fn upcase_first(s: &str) -> String {
|
||||
let mut c = s.chars();
|
||||
@@ -390,9 +393,6 @@ where
|
||||
//
|
||||
// Env methods
|
||||
//
|
||||
|
||||
use std::env;
|
||||
|
||||
pub fn get_env_str_value(key: &str) -> Option<String> {
|
||||
let key_file = format!("{key}_FILE");
|
||||
let value_from_env = env::var(key);
|
||||
@@ -402,7 +402,7 @@ pub fn get_env_str_value(key: &str) -> Option<String> {
|
||||
(Ok(_), Ok(_)) => panic!("You should not define both {key} and {key_file}!"),
|
||||
(Ok(v_env), Err(_)) => Some(v_env),
|
||||
(Err(_), Ok(v_file)) => match std::fs::read_to_string(v_file) {
|
||||
Ok(content) => Some(content.trim().to_string()),
|
||||
Ok(content) => Some(content.trim().to_owned()),
|
||||
Err(e) => panic!("Failed to load {key}: {e:?}"),
|
||||
},
|
||||
_ => None,
|
||||
@@ -431,8 +431,6 @@ pub fn get_env_bool(key: &str) -> Option<bool> {
|
||||
// Date util methods
|
||||
//
|
||||
|
||||
use chrono::{DateTime, Local, NaiveDateTime, TimeZone};
|
||||
|
||||
/// Formats a UTC-offset `NaiveDateTime` in the format used by Bitwarden API
|
||||
/// responses with "date" fields (`CreationDate`, `RevisionDate`, etc.).
|
||||
pub fn format_date(dt: &NaiveDateTime) -> String {
|
||||
@@ -457,10 +455,10 @@ pub fn validate_and_format_date(dt: &str) -> String {
|
||||
pub fn format_datetime_local(dt: &DateTime<Local>, fmt: &str) -> String {
|
||||
// Try parsing the `TZ` environment variable to enable formatting `%Z` as
|
||||
// a time zone abbreviation.
|
||||
if let Ok(tz) = env::var("TZ") {
|
||||
if let Ok(tz) = tz.parse::<chrono_tz::Tz>() {
|
||||
return dt.with_timezone(&tz).format(fmt).to_string();
|
||||
}
|
||||
if let Ok(tz) = env::var("TZ")
|
||||
&& let Ok(tz) = tz.parse::<chrono_tz::Tz>()
|
||||
{
|
||||
return dt.with_timezone(&tz).format(fmt).to_string();
|
||||
}
|
||||
|
||||
// Otherwise, fall back to formatting `%Z` as a UTC offset.
|
||||
@@ -512,6 +510,7 @@ pub fn is_valid_email(email: &str) -> bool {
|
||||
//
|
||||
|
||||
/// Returns true if the program is running in Docker, Podman or Kubernetes.
|
||||
#[must_use]
|
||||
pub fn is_running_in_container() -> bool {
|
||||
Path::new("/.dockerenv").exists()
|
||||
|| Path::new("/run/.containerenv").exists()
|
||||
@@ -543,10 +542,10 @@ pub fn get_active_web_release() -> String {
|
||||
];
|
||||
|
||||
for version_file in version_files {
|
||||
if let Ok(version_str) = std::fs::read_to_string(&version_file) {
|
||||
if let Ok(version) = serde_json::from_str::<WebVaultVersion>(&version_str) {
|
||||
return String::from(version.version.trim_start_matches('v'));
|
||||
}
|
||||
if let Ok(version_str) = std::fs::read_to_string(&version_file)
|
||||
&& let Ok(version) = serde_json::from_str::<WebVaultVersion>(&version_str)
|
||||
{
|
||||
return String::from(version.version.trim_start_matches('v'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,12 +555,6 @@ pub fn get_active_web_release() -> String {
|
||||
//
|
||||
// Deserialization methods
|
||||
//
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use serde::de::{self, DeserializeOwned, Deserializer, MapAccess, SeqAccess, Visitor};
|
||||
use serde_json::Value;
|
||||
|
||||
pub type JsonMap = serde_json::Map<String, Value>;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -605,7 +598,7 @@ impl<'de> Visitor<'de> for LowerCaseVisitor {
|
||||
let mut result_map = JsonMap::new();
|
||||
|
||||
while let Some((key, value)) = map.next_entry()? {
|
||||
result_map.insert(_process_key(key), convert_json_key_lcase_first(value));
|
||||
result_map.insert(process_json_key(key), convert_json_key_lcase_first(value));
|
||||
}
|
||||
|
||||
Ok(Value::Object(result_map))
|
||||
@@ -627,7 +620,7 @@ impl<'de> Visitor<'de> for LowerCaseVisitor {
|
||||
|
||||
// Inner function to handle a special case for the 'ssn' key.
|
||||
// This key is part of the Identity Cipher (Social Security Number)
|
||||
fn _process_key(key: &str) -> String {
|
||||
fn process_json_key(key: &str) -> String {
|
||||
match key.to_lowercase().as_ref() {
|
||||
"ssn" => "ssn".into(),
|
||||
_ => lcase_first(key),
|
||||
@@ -664,21 +657,24 @@ impl NumberOrString {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
#[expect(clippy::wrong_self_convention)]
|
||||
pub fn into_i32(&self) -> Result<i32, crate::Error> {
|
||||
use std::num::ParseIntError as PIE;
|
||||
match self {
|
||||
NumberOrString::Number(n) => match n.to_i32() {
|
||||
Some(n) => Ok(n),
|
||||
None => err!("Number does not fit in i32"),
|
||||
},
|
||||
NumberOrString::Number(n) => {
|
||||
if let Some(n) = n.to_i32() {
|
||||
Ok(n)
|
||||
} else {
|
||||
err!("Number does not fit in i32")
|
||||
}
|
||||
}
|
||||
NumberOrString::String(s) => {
|
||||
s.parse().map_err(|e: PIE| crate::Error::new("Can't convert to number", e.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
#[expect(clippy::wrong_self_convention)]
|
||||
pub fn into_i64(&self) -> Result<i64, crate::Error> {
|
||||
use std::num::ParseIntError as PIE;
|
||||
match self {
|
||||
@@ -753,11 +749,11 @@ pub fn convert_json_key_lcase_first(src_json: Value) -> Value {
|
||||
|
||||
Value::Object(obj) => {
|
||||
let mut json_map = JsonMap::new();
|
||||
for (key, value) in obj.into_iter() {
|
||||
for (key, value) in obj {
|
||||
match (key, value) {
|
||||
(key, Value::Object(elm)) => {
|
||||
let inner_value = convert_json_key_lcase_first(Value::Object(elm));
|
||||
json_map.insert(_process_key(&key), inner_value);
|
||||
json_map.insert(process_json_key(&key), inner_value);
|
||||
}
|
||||
|
||||
(key, Value::Array(elm)) => {
|
||||
@@ -767,11 +763,11 @@ pub fn convert_json_key_lcase_first(src_json: Value) -> Value {
|
||||
inner_array.push(convert_json_key_lcase_first(inner_obj));
|
||||
}
|
||||
|
||||
json_map.insert(_process_key(&key), Value::Array(inner_array));
|
||||
json_map.insert(process_json_key(&key), Value::Array(inner_array));
|
||||
}
|
||||
|
||||
(key, value) => {
|
||||
json_map.insert(_process_key(&key), value);
|
||||
json_map.insert(process_json_key(&key), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -793,7 +789,7 @@ pub enum FeatureFlagFilter {
|
||||
/// Parses the experimental client feature flags string into a HashMap.
|
||||
pub fn parse_experimental_client_feature_flags(
|
||||
experimental_client_feature_flags: &str,
|
||||
filter_mode: FeatureFlagFilter,
|
||||
filter_mode: &FeatureFlagFilter,
|
||||
) -> HashMap<String, bool> {
|
||||
experimental_client_feature_flags
|
||||
.split(',')
|
||||
@@ -811,7 +807,8 @@ pub fn parse_experimental_client_feature_flags(
|
||||
/// TODO: This is extracted from IpAddr::is_global, which is unstable:
|
||||
/// https://doc.rust-lang.org/nightly/std/net/enum.IpAddr.html#method.is_global
|
||||
/// Remove once https://github.com/rust-lang/rust/issues/27709 is merged
|
||||
#[allow(clippy::nonminimal_bool)]
|
||||
// #[expect(clippy::nonminimal_bool, reason = "Mostly copy/paste from std, keep as-is")]
|
||||
#[expect(clippy::decimal_bitwise_operands, reason = "Mostly copy/paste from std, keep as-is")]
|
||||
#[cfg(any(not(feature = "unstable"), test))]
|
||||
pub fn is_global_hardcoded(ip: std::net::IpAddr) -> bool {
|
||||
match ip {
|
||||
|
||||
Reference in New Issue
Block a user