vw_small

Hardened fork of Vaultwarden (https://github.com/dani-garcia/vaultwarden) with fewer features.
git clone https://git.philomathiclife.com/repos/vw_small
Log | Files | Refs | README

commit 8d6e4cb9584ab04d4e19aae669c57d48c5cf739b
parent cd856fed66dec4fd6305804e06044ed926bf4809
Author: Zack Newman <zack@philomathiclife.com>
Date:   Sun,  7 Jan 2024 21:05:21 -0700

remove log in with device

Diffstat:
MCargo.toml | 1-
Msrc/api/core/accounts.rs | 207+++++++++++--------------------------------------------------------------------
Msrc/api/identity.rs | 23++++++-----------------
Msrc/api/mod.rs | 2+-
Msrc/api/notifications.rs | 77-----------------------------------------------------------------------------
Msrc/api/web.rs | 18+++---------------
Msrc/auth.rs | 42------------------------------------------
Msrc/config.rs | 6+-----
Msrc/db/mod.rs | 7+++----
Dsrc/db/models/auth_request.rs | 106-------------------------------------------------------------------------------
Msrc/db/models/device.rs | 87-------------------------------------------------------------------------------
Msrc/db/models/mod.rs | 4+---
Msrc/db/models/two_factor.rs | 27++++++++-------------------
Asrc/db/schema.rs | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/db/schemas/sqlite/schema.rs | 215-------------------------------------------------------------------------------
Msrc/error.rs | 4----
Msrc/main.rs | 11+----------
17 files changed, 247 insertions(+), 785 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -27,7 +27,6 @@ libsqlite3-sys = { version = "0.27.0", default-features = false, features = ["bu openssl = { version = "0.10.62", default-features = false } paste = { version = "1.0.14", default-features = false } rand = { version = "0.8.5", default-features = false, features = ["small_rng"] } -regex = { version = "1.10.2", default-features = false, features = ["std"] } ring = { version = "0.17.7", default-features = false } rmpv = { version = "1.0.1", default-features = false } rocket = { version = "0.5.0", default-features = false, features = ["json", "tls"] } diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs @@ -1,17 +1,14 @@ use crate::{ api::{ - AnonymousNotify, EmptyResult, JsonResult, JsonUpcase, Notify, NumberOrString, - PasswordOrOtpData, UpdateType, + EmptyResult, JsonResult, JsonUpcase, Notify, NumberOrString, PasswordOrOtpData, UpdateType, }, - auth::{decode_delete, decode_verify_email, ClientHeaders, Headers}, - config, + auth::{decode_delete, ClientHeaders, Headers}, db::{ - models::{AuthRequest, Cipher, Device, DeviceType, Folder, User, UserKdfType}, + models::{Cipher, Device, Folder, User, UserKdfType}, DbConn, }, error::Error, }; -use chrono::Utc; use rocket::serde::json::Json; use rocket::{ http::Status, @@ -440,31 +437,17 @@ fn post_verify_email(_headers: Headers) -> Error { } #[derive(Deserialize)] -#[allow(non_snake_case)] +#[allow(non_snake_case, dead_code)] struct VerifyEmailTokenData { UserId: String, Token: String, } +#[allow(unused_variables, clippy::needless_pass_by_value)] #[post("/accounts/verify-email-token", data = "<data>")] -async fn post_verify_email_token( - data: JsonUpcase<VerifyEmailTokenData>, - conn: DbConn, -) -> EmptyResult { - let token_data: VerifyEmailTokenData = data.into_inner().data; - let Some(mut user) = User::find_by_uuid(&token_data.UserId, &conn).await else { - err!("User doesn't exist") - }; - let Ok(claims) = decode_verify_email(&token_data.Token) else { - err!("Invalid claim") - }; - if claims.sub != user.uuid { - err!("Invalid claim"); - } - user.verified_at = Some(Utc::now().naive_utc()); - user.last_verifying_at = None; - user.set_login_verify_count(0); - user.save(&conn).await +fn post_verify_email_token(data: JsonUpcase<VerifyEmailTokenData>) -> Error { + const MSG: &str = "E-mail is disabled."; + Error::new(MSG, MSG) } #[derive(Deserialize)] @@ -688,7 +671,7 @@ const fn put_clear_device_token(uuid: &str) {} const fn post_clear_device_token(uuid: &str) {} #[derive(Deserialize)] -#[allow(non_snake_case)] +#[allow(non_snake_case, dead_code)] struct AuthRequestRequest { accessCode: String, deviceIdentifier: String, @@ -697,71 +680,21 @@ struct AuthRequestRequest { #[serde(alias = "type")] _type: i32, } - +const AUTH_DISABLED_MSG: &str = "Auth requests and log in via device are disabled."; +#[allow(unused_variables, clippy::needless_pass_by_value)] #[post("/auth-requests", data = "<data>")] -async fn post_auth_request( - data: Json<AuthRequestRequest>, - headers: ClientHeaders, - conn: DbConn, - nt: Notify<'_>, -) -> JsonResult { - let inner_data = data.into_inner(); - let Some(user) = User::find_by_mail(&inner_data.email, &conn).await else { - err!("AuthRequest doesn't exist") - }; - let mut auth_request = AuthRequest::new( - user.uuid.clone(), - inner_data.deviceIdentifier.clone(), - headers.device_type, - headers.ip.ip.to_string(), - inner_data.accessCode, - inner_data.publicKey, - ); - auth_request.save(&conn).await?; - nt.send_auth_request(&user.uuid, &auth_request.uuid, &inner_data.deviceIdentifier) - .await; - Ok(Json(json!({ - "id": auth_request.uuid, - "publicKey": auth_request.public_key, - "requestDeviceType": DeviceType::from_i32(auth_request.device_type).to_string(), - "requestIpAddress": auth_request.request_ip, - "key": null, - "masterPasswordHash": null, - "creationDate": auth_request.creation_date.and_utc(), - "responseDate": null, - "requestApproved": false, - "origin": config::get_config().domain_origin(), - "object": "auth-request" - }))) +fn post_auth_request(data: Json<AuthRequestRequest>, _headers: ClientHeaders) -> Error { + Error::new(AUTH_DISABLED_MSG, AUTH_DISABLED_MSG) } +#[allow(unused_variables)] #[get("/auth-requests/<uuid>")] -async fn get_auth_request(uuid: &str, conn: DbConn) -> JsonResult { - let Some(auth_request) = AuthRequest::find_by_uuid(uuid, &conn).await else { - err!("AuthRequest doesn't exist") - }; - let response_date_utc = auth_request - .response_date - .map(|response_date| response_date.and_utc()); - Ok(Json(json!( - { - "id": uuid, - "publicKey": auth_request.public_key, - "requestDeviceType": DeviceType::from_i32(auth_request.device_type).to_string(), - "requestIpAddress": auth_request.request_ip, - "key": auth_request.enc_key, - "masterPasswordHash": auth_request.master_password_hash, - "creationDate": auth_request.creation_date.and_utc(), - "responseDate": response_date_utc, - "requestApproved": auth_request.approved, - "origin": config::get_config().domain_origin(), - "object":"auth-request" - } - ))) +fn get_auth_request(uuid: &str) -> Error { + Error::new(AUTH_DISABLED_MSG, AUTH_DISABLED_MSG) } #[derive(Deserialize)] -#[allow(non_snake_case)] +#[allow(non_snake_case, dead_code)] struct AuthResponseRequest { deviceIdentifier: String, key: String, @@ -769,108 +702,24 @@ struct AuthResponseRequest { requestApproved: bool, } +#[allow(unused_variables, clippy::needless_pass_by_value)] #[put("/auth-requests/<uuid>", data = "<data>")] -async fn put_auth_request( - uuid: &str, - data: Json<AuthResponseRequest>, - conn: DbConn, - ant: AnonymousNotify<'_>, - nt: Notify<'_>, -) -> JsonResult { - let inner_data = data.into_inner(); - let mut auth_request: AuthRequest = match AuthRequest::find_by_uuid(uuid, &conn).await { - Some(auth_request) => auth_request, - None => { - err!("AuthRequest doesn't exist") - } - }; - auth_request.approved = Some(inner_data.requestApproved); - auth_request.enc_key = Some(inner_data.key); - auth_request.master_password_hash = inner_data.masterPasswordHash; - auth_request.response_device_id = Some(inner_data.deviceIdentifier.clone()); - auth_request.save(&conn).await?; - if auth_request.approved.unwrap_or(false) { - ant.send_auth_response(&auth_request.user_uuid, &auth_request.uuid) - .await; - nt.send_auth_response( - &auth_request.user_uuid, - &auth_request.uuid, - inner_data.deviceIdentifier, - ) - .await; - } - let response_date_utc = auth_request - .response_date - .map(|response_date| response_date.and_utc()); - Ok(Json(json!( - { - "id": uuid, - "publicKey": auth_request.public_key, - "requestDeviceType": DeviceType::from_i32(auth_request.device_type).to_string(), - "requestIpAddress": auth_request.request_ip, - "key": auth_request.enc_key, - "masterPasswordHash": auth_request.master_password_hash, - "creationDate": auth_request.creation_date.and_utc(), - "responseDate": response_date_utc, - "requestApproved": auth_request.approved, - "origin": config::get_config().domain_origin(), - "object":"auth-request" - } - ))) +fn put_auth_request(uuid: &str, data: Json<AuthResponseRequest>) -> Error { + Error::new(AUTH_DISABLED_MSG, AUTH_DISABLED_MSG) } +#[allow(unused_variables)] #[get("/auth-requests/<uuid>/response?<code>")] -async fn get_auth_request_response(uuid: &str, code: &str, conn: DbConn) -> JsonResult { - let Some(auth_request) = AuthRequest::find_by_uuid(uuid, &conn).await else { - err!("AuthRequest doesn't exist") - }; - if !auth_request.check_access_code(code) { - err!("Access code invalid doesn't exist") - } - let response_date_utc = auth_request - .response_date - .map(|response_date| response_date.and_utc()); - Ok(Json(json!( - { - "id": uuid, - "publicKey": auth_request.public_key, - "requestDeviceType": DeviceType::from_i32(auth_request.device_type).to_string(), - "requestIpAddress": auth_request.request_ip, - "key": auth_request.enc_key, - "masterPasswordHash": auth_request.master_password_hash, - "creationDate": auth_request.creation_date.and_utc(), - "responseDate": response_date_utc, - "requestApproved": auth_request.approved, - "origin": config::get_config().domain_origin(), - "object":"auth-request" - } - ))) +fn get_auth_request_response(uuid: &str, code: &str) -> Error { + Error::new(AUTH_DISABLED_MSG, AUTH_DISABLED_MSG) } +#[allow(unused_variables, clippy::needless_pass_by_value)] #[get("/auth-requests")] -async fn get_auth_requests(headers: Headers, conn: DbConn) -> JsonResult { - let auth_requests = AuthRequest::find_by_user(&headers.user.uuid, &conn).await; - Ok(Json(json!({ - "data": auth_requests - .iter() - .filter(|request| request.approved.is_none()) - .map(|request| { - let response_date_utc = request.response_date.map(|response_date| response_date.and_utc()); - json!({ - "id": request.uuid, - "publicKey": request.public_key, - "requestDeviceType": DeviceType::from_i32(request.device_type).to_string(), - "requestIpAddress": request.request_ip, - "key": request.enc_key, - "masterPasswordHash": request.master_password_hash, - "creationDate": request.creation_date.and_utc(), - "responseDate": response_date_utc, - "requestApproved": request.approved, - "origin": config::get_config().domain_origin(), - "object":"auth-request" - }) - }).collect::<Vec<Value>>(), +fn get_auth_requests(headers: Headers) -> Json<Value> { + Json(json!({ + "data": Vec::<Value>::new(), "continuationToken": null, "object": "list" - }))) + })) } diff --git a/src/api/identity.rs b/src/api/identity.rs @@ -6,7 +6,7 @@ use crate::{ auth::{ClientHeaders, ClientIp}, config, db::{ - models::{AuthRequest, Device, TwoFactorType, User}, + models::{Device, TwoFactorType, User}, DbConn, }, error::{Error, MapResult}, @@ -108,22 +108,11 @@ async fn _password_login( *user_uuid = Some(user.uuid.clone()); // Check password let password = data.password.as_ref().unwrap(); - if let Some(auth_request_uuid) = data.auth_request.clone() { - if let Some(auth_request) = - AuthRequest::find_by_uuid(auth_request_uuid.as_str(), conn).await - { - if !auth_request.check_access_code(password) { - err!( - "Username or access code is incorrect. Try again", - format!("IP: {}. Username: {}.", ip.ip, username) - ) - } - } else { - err!( - "Auth request not found. Try again.", - format!("IP: {}. Username: {}.", ip.ip, username) - ) - } + if data.auth_request.is_some() { + err!( + "Auth request not found. Try again.", + format!("IP: {}. Username: {}.", ip.ip, username) + ) } else if !user.check_valid_password(password) { err!( "Username or password is incorrect. Try again", diff --git a/src/api/mod.rs b/src/api/mod.rs @@ -15,7 +15,7 @@ pub use crate::api::{ notifications::routes as notifications_routes, notifications::{ init_ws_anonymous_subscriptions, init_ws_users, start_notification_server, - ws_anonymous_subscriptions, AnonymousNotify, Notify, UpdateType, + ws_anonymous_subscriptions, Notify, UpdateType, }, web::catchers as web_catchers, web::routes as web_routes, diff --git a/src/api/notifications.rs b/src/api/notifications.rs @@ -394,40 +394,6 @@ impl WebSocketUsers { self.send_update(uuid, &data).await; } } - - pub async fn send_auth_request( - &self, - user_uuid: &str, - auth_request_uuid: &str, - acting_device_uuid: &str, - ) { - let data = create_update( - vec![ - ("Id".into(), auth_request_uuid.to_owned().into()), - ("UserId".into(), user_uuid.to_owned().into()), - ], - UpdateType::AuthRequest, - Some(acting_device_uuid.to_owned()), - ); - self.send_update(user_uuid, &data).await; - } - - pub async fn send_auth_response( - &self, - user_uuid: &str, - auth_response_uuid: &str, - approving_device_uuid: String, - ) { - let data = create_update( - vec![ - ("Id".into(), auth_response_uuid.to_owned().into()), - ("UserId".into(), user_uuid.to_owned().into()), - ], - UpdateType::AuthRequestResponse, - approving_device_uuid.clone().into(), - ); - self.send_update(auth_response_uuid, &data).await; - } } #[derive(Clone)] @@ -435,28 +401,6 @@ pub struct AnonymousWebSocketSubscriptions { map: Arc<dashmap::DashMap<String, Sender<Message>>>, } -impl AnonymousWebSocketSubscriptions { - async fn send_update(&self, token: &str, data: &[u8]) { - if let Some(sender) = self.map.get(token).map(|v| v.clone()) { - if let Err(e) = sender.send(Message::binary(data)).await { - error!("Error sending WS update {e}"); - }; - } - } - - pub async fn send_auth_response(&self, user_uuid: &String, auth_response_uuid: &str) { - let data = create_anonymous_update( - vec![ - ("Id".into(), auth_response_uuid.to_owned().into()), - ("UserId".into(), user_uuid.clone().into()), - ], - UpdateType::AuthRequestResponse, - user_uuid.to_string(), - ); - self.send_update(auth_response_uuid, &data).await; - } -} - /* Message Structure [ 1, // MessageType.Invocation @@ -495,26 +439,6 @@ fn create_update( serialize(&value) } -fn create_anonymous_update( - payload: Vec<(Value, Value)>, - ut: UpdateType, - user_id: String, -) -> Vec<u8> { - use rmpv::Value as V; - let value = V::Array(vec![ - 1i32.into(), - V::Map(vec![]), - V::Nil, - "AuthRequestResponseRecieved".into(), - V::Array(vec![V::Map(vec![ - ("Type".into(), (i32::from(ut)).into()), - ("Payload".into(), payload.into()), - ("UserId".into(), user_id.into()), - ])]), - ]); - serialize(&value) -} - fn create_ping() -> Vec<u8> { serialize(&Value::Array(vec![6i32.into()])) } @@ -567,7 +491,6 @@ impl From<UpdateType> for i32 { } pub type Notify<'a> = &'a rocket::State<Arc<WebSocketUsers>>; -pub type AnonymousNotify<'a> = &'a rocket::State<Arc<AnonymousWebSocketSubscriptions>>; pub fn start_notification_server() -> Arc<WebSocketUsers> { Arc::clone(ws_users()) } diff --git a/src/api/web.rs b/src/api/web.rs @@ -1,6 +1,5 @@ use crate::{ api::core::now, - auth::decode_file_download, config::{self, Config}, error::Error, util::{Cached, SafeString}, @@ -92,21 +91,10 @@ async fn web_files(p: PathBuf) -> Cached<Option<NamedFile>> { ) } +#[allow(unused_variables, clippy::needless_pass_by_value)] #[get("/attachments/<uuid>/<file_id>?<token>")] -async fn attachments(uuid: SafeString, file_id: SafeString, token: String) -> Option<NamedFile> { - let Ok(claims) = decode_file_download(&token) else { - return None; - }; - if claims.sub != *uuid || claims.file_id != *file_id { - return None; - } - NamedFile::open( - Path::new(Config::ATTACHMENTS_FOLDER) - .join(uuid) - .join(file_id), - ) - .await - .ok() +fn attachments(uuid: SafeString, file_id: SafeString, token: &str) -> Option<NamedFile> { + None } #[get("/alive")] diff --git a/src/auth.rs b/src/auth.rs @@ -79,40 +79,6 @@ fn get_jwt_delete_issuer() -> &'static str { .expect("JWT_DELETE_ISSUER must be initialized in main") .as_str() } -static JWT_VERIFYEMAIL_ISSUER: OnceLock<String> = OnceLock::new(); -#[inline] -fn init_jwt_verifyemail_issuer() { - JWT_VERIFYEMAIL_ISSUER - .set(format!( - "{}|verifyemail", - config::get_config().domain_origin() - )) - .expect("JWT_VERIFYEMAIL_ISSUER must only be initialized once"); -} -#[inline] -fn get_jwt_verifyemail_issuer() -> &'static str { - JWT_VERIFYEMAIL_ISSUER - .get() - .expect("JWT_VERIFYEMAIL_ISSUER must be initialized in main") - .as_str() -} -static JWT_FILE_DOWNLOAD_ISSUER: OnceLock<String> = OnceLock::new(); -#[inline] -fn init_jwt_file_download_issuer() { - JWT_FILE_DOWNLOAD_ISSUER - .set(format!( - "{}|file_download", - config::get_config().domain_origin() - )) - .expect("JWT_FILE_DOWNLOAD_ISSUER must only be initialized once"); -} -#[inline] -fn get_jwt_file_download_issuer() -> &'static str { - JWT_FILE_DOWNLOAD_ISSUER - .get() - .expect("JWT_FILE_DOWNLOAD_ISSUER must be initialized in main") - .as_str() -} const JWT_ALGORITHM: Algorithm = Algorithm::EdDSA; static ED_KEYS: OnceLock<(EncodingKey, DecodingKey)> = OnceLock::new(); #[allow(clippy::map_err_ignore, clippy::verbose_file_reads)] @@ -172,8 +138,6 @@ pub fn init_values() { init_jwt_login_issuer(); init_jwt_invite_issuer(); init_jwt_delete_issuer(); - init_jwt_verifyemail_issuer(); - init_jwt_file_download_issuer(); init_ed_keys().expect("error creating Ed25519 keys"); } pub fn encode_jwt<T: Serialize>(claims: &T) -> String { @@ -226,12 +190,6 @@ pub fn decode_invite(token: &str) -> Result<InviteJwtClaims, Error> { pub fn decode_delete(token: &str) -> Result<BasicJwtClaims, Error> { decode_jwt(token, get_jwt_delete_issuer().to_owned()) } -pub fn decode_verify_email(token: &str) -> Result<BasicJwtClaims, Error> { - decode_jwt(token, get_jwt_verifyemail_issuer().to_owned()) -} -pub fn decode_file_download(token: &str) -> Result<FileDownloadClaims, Error> { - decode_jwt(token, get_jwt_file_download_issuer().to_owned()) -} #[derive(Serialize, Deserialize)] pub struct LoginJwtClaims { diff --git a/src/config.rs b/src/config.rs @@ -125,7 +125,7 @@ impl Config { .limit("file", 525i32.megabytes()), log_level: LogLevel::Off, port: config_file.port, - temp_dir: Self::TMP_FOLDER.into(), + temp_dir: "data/tmp".into(), tls: Some(tls), ..Default::default() }; @@ -172,13 +172,9 @@ impl Config { } } impl Config { - pub const ATTACHMENTS_FOLDER: &'static str = "data/attachments"; pub const DATA_FOLDER: &'static str = "data"; pub const DATABASE_URL: &'static str = "data/db.sqlite3"; - pub const ICON_CACHE_FOLDER: &'static str = "data/icon_cache"; pub const PRIVATE_ED25519_KEY: &'static str = "data/ed25519_key.pem"; - pub const SENDS_FOLDER: &'static str = "data/sends"; - pub const TMP_FOLDER: &'static str = "data/tmp"; pub const WEB_VAULT_FOLDER: &'static str = "web-vault/"; #[inline] pub fn domain_origin(&self) -> String { diff --git a/src/db/mod.rs b/src/db/mod.rs @@ -19,8 +19,7 @@ use tokio::{ task, time::timeout, }; -#[path = "schemas/sqlite/schema.rs"] -mod __sqlite_schema; +mod schema; // These changes are based on Rocket 0.5-rc wrapper of Diesel: https://github.com/SergioBenitez/Rocket/blob/v0.5-rc/contrib/sync_db_pools // A wrapper around spawn_blocking that propagates panics to the calling code. @@ -131,7 +130,7 @@ macro_rules! db_run { use $crate::db::FromDb; let mut con = $conn.conn.clone().lock_owned().await; paste::paste! { - #[allow(unused)] use $crate::db::__sqlite_schema::{self as schema, *}; + #[allow(unused)] use $crate::db::schema::{self, *}; #[allow(unused)] use __sqlite_model::*; } task::block_in_place(move || { @@ -187,7 +186,7 @@ macro_rules! db_object { paste::paste! { #[allow(unused)] use super::*; #[allow(unused)] use diesel::prelude::*; - #[allow(unused)] use $crate::db::__sqlite_schema::*; + #[allow(unused)] use $crate::db::schema::*; $( #[$attr] )* pub struct [<$name Db>] { $( diff --git a/src/db/models/auth_request.rs b/src/db/models/auth_request.rs @@ -1,106 +0,0 @@ -use crate::crypto::ct_eq; -use crate::util; -use chrono::{NaiveDateTime, Utc}; -use diesel::result::{self, DatabaseErrorKind}; - -db_object! { - #[derive(AsChangeset, Insertable, Queryable)] - #[diesel(table_name = auth_requests)] - #[diesel(treat_none_as_null = true)] - pub struct AuthRequest { - pub uuid: String, - pub user_uuid: String, - organization_uuid: Option<String>, - request_device_identifier: String, - pub device_type: i32, // https://github.com/bitwarden/server/blob/master/src/Core/Enums/DeviceType.cs - pub request_ip: String, - pub response_device_id: Option<String>, - access_code: String, - pub public_key: String, - pub enc_key: Option<String>, - pub master_password_hash: Option<String>, - pub approved: Option<bool>, - pub creation_date: NaiveDateTime, - pub response_date: Option<NaiveDateTime>, - authentication_date: Option<NaiveDateTime>, - } -} - -impl AuthRequest { - pub fn new( - user_uuid: String, - request_device_identifier: String, - device_type: i32, - request_ip: String, - access_code: String, - public_key: String, - ) -> Self { - let now = Utc::now().naive_utc(); - Self { - uuid: util::get_uuid(), - user_uuid, - organization_uuid: None, - request_device_identifier, - device_type, - request_ip, - response_device_id: None, - access_code, - public_key, - enc_key: None, - master_password_hash: None, - approved: None, - creation_date: now, - response_date: None, - authentication_date: None, - } - } -} - -use crate::api::EmptyResult; -use crate::db::DbConn; -use crate::error::MapResult; - -impl AuthRequest { - pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { - db_run! { conn: - { - match diesel::replace_into(auth_requests::table) - .values(AuthRequestDb::to_db(self)) - .execute(conn) - { - Ok(_) => Ok(()), - // Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first. - Err(result::Error::DatabaseError(DatabaseErrorKind::ForeignKeyViolation, _)) => { - diesel::update(auth_requests::table) - .filter(auth_requests::uuid.eq(&self.uuid)) - .set(AuthRequestDb::to_db(self)) - .execute(conn) - .map_res("Error auth_request") - } - Err(e) => Err(e.into()), - }.map_res("Error auth_request") - } - } - } - - pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> { - db_run! {conn: { - auth_requests::table - .filter(auth_requests::uuid.eq(uuid)) - .first::<AuthRequestDb>(conn) - .ok() - .from_db() - }} - } - - pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> { - db_run! {conn: { - auth_requests::table - .filter(auth_requests::user_uuid.eq(user_uuid)) - .load::<AuthRequestDb>(conn).expect("Error loading auth_requests").from_db() - }} - } - pub fn check_access_code(&self, access_code: &str) -> bool { - ct_eq(&self.access_code, access_code) - } -} diff --git a/src/db/models/device.rs b/src/db/models/device.rs @@ -1,7 +1,6 @@ use crate::crypto; use crate::util; use chrono::{NaiveDateTime, Utc}; -use core::fmt; db_object! { #[derive(Insertable, Queryable)] @@ -157,89 +156,3 @@ impl Device { }} } } - -pub enum DeviceType { - Android = 0, - Ios = 1, - ChromeExtension = 2, - FirefoxExtension = 3, - OperaExtension = 4, - EdgeExtension = 5, - WindowsDesktop = 6, - MacOsDesktop = 7, - LinuxDesktop = 8, - ChromeBrowser = 9, - FirefoxBrowser = 10, - OperaBrowser = 11, - EdgeBrowser = 12, - IEBrowser = 13, - UnknownBrowser = 14, - AndroidAmazon = 15, - Uwp = 16, - SafariBrowser = 17, - VivaldiBrowser = 18, - VivaldiExtension = 19, - SafariExtension = 20, - Sdk = 21, - Server = 22, -} - -impl fmt::Display for DeviceType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Self::Android => write!(f, "Android"), - Self::Ios => write!(f, "iOS"), - Self::ChromeExtension => write!(f, "Chrome Extension"), - Self::FirefoxExtension => write!(f, "Firefox Extension"), - Self::OperaExtension => write!(f, "Opera Extension"), - Self::EdgeExtension => write!(f, "Edge Extension"), - Self::WindowsDesktop => write!(f, "Windows Desktop"), - Self::MacOsDesktop => write!(f, "MacOS Desktop"), - Self::LinuxDesktop => write!(f, "Linux Desktop"), - Self::ChromeBrowser => write!(f, "Chrome Browser"), - Self::FirefoxBrowser => write!(f, "Firefox Browser"), - Self::OperaBrowser => write!(f, "Opera Browser"), - Self::EdgeBrowser => write!(f, "Edge Browser"), - Self::IEBrowser => write!(f, "Internet Explorer"), - Self::UnknownBrowser => write!(f, "Unknown Browser"), - Self::AndroidAmazon => write!(f, "Android Amazon"), - Self::Uwp => write!(f, "UWP"), - Self::SafariBrowser => write!(f, "Safari Browser"), - Self::VivaldiBrowser => write!(f, "Vivaldi Browser"), - Self::VivaldiExtension => write!(f, "Vivaldi Extension"), - Self::SafariExtension => write!(f, "Safari Extension"), - Self::Sdk => write!(f, "SDK"), - Self::Server => write!(f, "Server"), - } - } -} - -impl DeviceType { - pub const fn from_i32(value: i32) -> Self { - match value { - 0i32 => Self::Android, - 1i32 => Self::Ios, - 2i32 => Self::ChromeExtension, - 3i32 => Self::FirefoxExtension, - 4i32 => Self::OperaExtension, - 5i32 => Self::EdgeExtension, - 6i32 => Self::WindowsDesktop, - 7i32 => Self::MacOsDesktop, - 8i32 => Self::LinuxDesktop, - 9i32 => Self::ChromeBrowser, - 10i32 => Self::FirefoxBrowser, - 11i32 => Self::OperaBrowser, - 12i32 => Self::EdgeBrowser, - 13i32 => Self::IEBrowser, - 15i32 => Self::AndroidAmazon, - 16i32 => Self::Uwp, - 17i32 => Self::SafariBrowser, - 18i32 => Self::VivaldiBrowser, - 19i32 => Self::VivaldiExtension, - 20i32 => Self::SafariExtension, - 21i32 => Self::Sdk, - 22i32 => Self::Server, - _ => Self::UnknownBrowser, - } - } -} diff --git a/src/db/models/mod.rs b/src/db/models/mod.rs @@ -1,4 +1,3 @@ -mod auth_request; mod cipher; mod collection; mod device; @@ -8,10 +7,9 @@ mod org_policy; mod organization; mod two_factor; mod user; -pub use self::auth_request::AuthRequest; pub use self::cipher::Cipher; pub use self::collection::{Collection, CollectionCipher, CollectionUser}; -pub use self::device::{Device, DeviceType}; +pub use self::device::Device; pub use self::favorite::Favorite; pub use self::folder::{Folder, FolderCipher}; pub use self::org_policy::{OrgPolicy, OrgPolicyErr, OrgPolicyType}; diff --git a/src/db/models/two_factor.rs b/src/db/models/two_factor.rs @@ -1,4 +1,11 @@ -use crate::{api::EmptyResult, db::DbConn, error::Error}; +use crate::{ + api::EmptyResult, + db::{ + schema::{totp, webauthn, webauthn_auth, webauthn_reg}, + DbConn, FromDb, + }, + error::Error, +}; use serde::ser::{Serialize, SerializeStruct, Serializer}; use serde_json::de; use tokio::task; @@ -168,7 +175,6 @@ impl Totp { impl TwoFactorType { #[allow(clippy::clone_on_ref_ptr, clippy::shadow_unrelated)] pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { - use crate::db::__sqlite_schema::{totp, webauthn}; use diesel::prelude::{Connection, ExpressionMethods, RunQueryDsl}; use diesel::result; let mut con_res = conn.conn.clone().lock_owned().await; @@ -190,7 +196,6 @@ impl TwoFactorType { } #[allow(clippy::clone_on_ref_ptr)] pub async fn delete_by_user(self, user_uuid: &str, conn: &DbConn) -> EmptyResult { - use crate::db::__sqlite_schema::{totp, webauthn}; use diesel::prelude::{ExpressionMethods, RunQueryDsl}; use diesel::result; let mut con_res = conn.conn.clone().lock_owned().await; @@ -210,7 +215,6 @@ impl TwoFactorType { } #[allow(clippy::clone_on_ref_ptr, clippy::shadow_unrelated)] pub async fn has_twofactor(user_uuid: &str, conn: &DbConn) -> Result<bool, Error> { - use crate::db::__sqlite_schema::{totp, webauthn}; use diesel::prelude::{Connection, ExpressionMethods, QueryDsl, RunQueryDsl}; use diesel::result; let mut con_res = conn.conn.clone().lock_owned().await; @@ -244,7 +248,6 @@ impl TwoFactorType { user_uuid: &str, conn: &DbConn, ) -> Result<(bool, Option<String>), Error> { - use crate::db::__sqlite_schema::{totp, webauthn}; use diesel::prelude::{Connection, ExpressionMethods, QueryDsl, RunQueryDsl}; use diesel::result; use diesel::OptionalExtension; @@ -273,7 +276,6 @@ impl TwoFactorType { impl WebAuthnInfo { #[allow(clippy::clone_on_ref_ptr)] pub async fn get_all_by_user(user_uuid: &str, conn: &DbConn) -> Result<Vec<Self>, Error> { - use crate::db::__sqlite_schema::webauthn; use diesel::prelude::{ExpressionMethods, QueryDsl, RunQueryDsl}; use diesel::result; let mut con_res = conn.conn.clone().lock_owned().await; @@ -294,7 +296,6 @@ impl WebAuthn { user_uuid: &str, conn: &DbConn, ) -> Result<Vec<SecurityKey>, Error> { - use crate::db::__sqlite_schema::webauthn; use diesel::prelude::{ExpressionMethods, QueryDsl, RunQueryDsl}; use diesel::result; let mut con_res = conn.conn.clone().lock_owned().await; @@ -320,7 +321,6 @@ impl WebAuthn { } #[allow(clippy::clone_on_ref_ptr)] pub async fn insert(self, conn: &DbConn) -> EmptyResult { - use crate::db::__sqlite_schema::webauthn; use __sqlite_model::WebAuthnDb; use diesel::prelude::RunQueryDsl; use diesel::result; @@ -344,7 +344,6 @@ impl WebAuthn { } #[allow(clippy::clone_on_ref_ptr)] pub async fn update(self, conn: &DbConn) -> EmptyResult { - use crate::db::__sqlite_schema::webauthn; use diesel::prelude::{ExpressionMethods, RunQueryDsl}; use diesel::result; let mut con_res = conn.conn.clone().lock_owned().await; @@ -371,7 +370,6 @@ impl WebAuthn { user_uuid: &str, conn: &DbConn, ) -> Result<Vec<CredentialID>, Error> { - use crate::db::__sqlite_schema::webauthn; use diesel::prelude::{ExpressionMethods, QueryDsl, RunQueryDsl}; use diesel::result; let mut con_res = conn.conn.clone().lock_owned().await; @@ -398,7 +396,6 @@ impl WebAuthn { } #[allow(clippy::clone_on_ref_ptr)] pub async fn get_by_cred_id(credential_id: &str, conn: &DbConn) -> Result<Option<Self>, Error> { - use crate::db::{FromDb, __sqlite_schema::webauthn}; use __sqlite_model::WebAuthnDb; use diesel::prelude::{ExpressionMethods, QueryDsl, RunQueryDsl}; use diesel::result; @@ -420,7 +417,6 @@ impl WebAuthn { id: i64, conn: &DbConn, ) -> EmptyResult { - use crate::db::__sqlite_schema::webauthn; use diesel::prelude::{ExpressionMethods, RunQueryDsl}; use diesel::result; let mut con_res = conn.conn.clone().lock_owned().await; @@ -447,7 +443,6 @@ impl WebAuthn { impl WebAuthnAuth { #[allow(clippy::clone_on_ref_ptr)] pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Result<Option<Self>, Error> { - use crate::db::{FromDb, __sqlite_schema::webauthn_auth}; use __sqlite_model::WebAuthnAuthDb; use diesel::prelude::{ExpressionMethods, QueryDsl, RunQueryDsl}; use diesel::result; @@ -468,7 +463,6 @@ impl WebAuthnAuth { impl WebAuthnReg { #[allow(clippy::clone_on_ref_ptr)] pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Result<Option<Self>, Error> { - use crate::db::{FromDb, __sqlite_schema::webauthn_reg}; use __sqlite_model::WebAuthnRegDb; use diesel::prelude::{ExpressionMethods, QueryDsl, RunQueryDsl}; use diesel::result; @@ -489,7 +483,6 @@ impl WebAuthnReg { impl WebAuthnChallenge { #[allow(clippy::clone_on_ref_ptr, clippy::shadow_unrelated)] pub async fn delete_all(user_uuid: &str, conn: &DbConn) -> EmptyResult { - use crate::db::__sqlite_schema::{webauthn_auth, webauthn_reg}; use diesel::prelude::{Connection, ExpressionMethods, RunQueryDsl}; use diesel::result; let mut con_res = conn.conn.clone().lock_owned().await; @@ -511,7 +504,6 @@ impl WebAuthnChallenge { } #[allow(clippy::clone_on_ref_ptr)] pub async fn delete(self, conn: &DbConn) -> EmptyResult { - use crate::db::__sqlite_schema::{webauthn_auth, webauthn_reg}; use diesel::prelude::{ExpressionMethods, RunQueryDsl}; use diesel::result; let mut con_res = conn.conn.clone().lock_owned().await; @@ -539,7 +531,6 @@ impl WebAuthnChallenge { } #[allow(clippy::clone_on_ref_ptr, clippy::shadow_unrelated)] pub async fn replace(&self, conn: &DbConn) -> EmptyResult { - use crate::db::__sqlite_schema::{webauthn_auth, webauthn_reg}; use __sqlite_model::{WebAuthnAuthDb, WebAuthnRegDb}; use diesel::prelude::{Connection, ExpressionMethods, RunQueryDsl}; use diesel::result; @@ -590,7 +581,6 @@ impl WebAuthnChallenge { impl Totp { #[allow(clippy::clone_on_ref_ptr, clippy::shadow_unrelated)] pub async fn replace(self, conn: &DbConn) -> EmptyResult { - use crate::db::__sqlite_schema::totp; use __sqlite_model::TotpDb; use diesel::prelude::{ExpressionMethods, RunQueryDsl}; use diesel::result; @@ -625,7 +615,6 @@ impl Totp { } #[allow(clippy::clone_on_ref_ptr)] pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Result<Option<Self>, Error> { - use crate::db::{FromDb, __sqlite_schema::totp}; use __sqlite_model::TotpDb; use diesel::prelude::{ExpressionMethods, QueryDsl, RunQueryDsl}; use diesel::result; diff --git a/src/db/schema.rs b/src/db/schema.rs @@ -0,0 +1,195 @@ +table! { + ciphers (uuid) { + uuid -> Text, + created_at -> Timestamp, + updated_at -> Timestamp, + user_uuid -> Nullable<Text>, + organization_uuid -> Nullable<Text>, + key -> Nullable<Text>, + atype -> Integer, + name -> Text, + notes -> Nullable<Text>, + fields -> Nullable<Text>, + data -> Text, + password_history -> Nullable<Text>, + deleted_at -> Nullable<Timestamp>, + reprompt -> Nullable<Integer>, + } +} +table! { + ciphers_collections (cipher_uuid, collection_uuid) { + cipher_uuid -> Text, + collection_uuid -> Text, + } +} + +table! { + collections (uuid) { + uuid -> Text, + org_uuid -> Text, + name -> Text, + external_id -> Nullable<Text>, + } +} +table! { + devices (uuid, user_uuid) { + uuid -> Text, + created_at -> Timestamp, + updated_at -> Timestamp, + user_uuid -> Text, + name -> Text, + atype -> Integer, + push_uuid -> Nullable<Text>, + push_token -> Nullable<Text>, + refresh_token -> Text, + twofactor_remember -> Nullable<Text>, + } +} + +table! { + favorites (user_uuid, cipher_uuid) { + user_uuid -> Text, + cipher_uuid -> Text, + } +} + +table! { + folders (uuid) { + uuid -> Text, + created_at -> Timestamp, + updated_at -> Timestamp, + user_uuid -> Text, + name -> Text, + } +} + +table! { + folders_ciphers (cipher_uuid, folder_uuid) { + cipher_uuid -> Text, + folder_uuid -> Text, + } +} + +table! { + org_policies (uuid) { + uuid -> Text, + org_uuid -> Text, + atype -> Integer, + enabled -> Bool, + data -> Text, + } +} + +table! { + organizations (uuid) { + uuid -> Text, + name -> Text, + billing_email -> Text, + private_key -> Nullable<Text>, + public_key -> Nullable<Text>, + } +} + +table! { + totp (user_uuid) { + user_uuid -> Text, + token -> Text, + last_used -> BigInt, + } +} + +table! { + users (uuid) { + uuid -> Text, + enabled -> Bool, + created_at -> Timestamp, + updated_at -> Timestamp, + verified_at -> Nullable<Timestamp>, + last_verifying_at -> Nullable<Timestamp>, + login_verify_count -> Integer, + email -> Text, + email_new -> Nullable<Text>, + email_new_token -> Nullable<Text>, + name -> Text, + password_hash -> Binary, + salt -> Binary, + password_iterations -> Integer, + password_hint -> Nullable<Text>, + akey -> Text, + private_key -> Nullable<Text>, + public_key -> Nullable<Text>, + totp_secret -> Nullable<Text>, + totp_recover -> Nullable<Text>, + security_stamp -> Text, + stamp_exception -> Nullable<Text>, + equivalent_domains -> Text, + excluded_globals -> Text, + client_kdf_type -> Integer, + client_kdf_iter -> Integer, + client_kdf_memory -> Nullable<Integer>, + client_kdf_parallelism -> Nullable<Integer>, + api_key -> Nullable<Text>, + avatar_color -> Nullable<Text>, + external_id -> Nullable<Text>, + } +} + +table! { + users_collections (user_uuid, collection_uuid) { + user_uuid -> Text, + collection_uuid -> Text, + read_only -> Bool, + hide_passwords -> Bool, + } +} + +table! { + users_organizations (uuid) { + uuid -> Text, + user_uuid -> Text, + org_uuid -> Text, + access_all -> Bool, + akey -> Text, + status -> Integer, + atype -> Integer, + reset_password_key -> Nullable<Text>, + external_id -> Nullable<Text>, + } +} + +table! { + webauthn (credential_id) { + credential_id -> Text, + user_uuid -> Text, + id -> BigInt, + name -> Text, + security_key -> Text, + } +} + +table! { + webauthn_auth (user_uuid) { + user_uuid -> Text, + data -> Text, + } +} + +table! { + webauthn_reg (user_uuid) { + user_uuid -> Text, + data -> Text, + } +} + +joinable!(folders_ciphers -> ciphers (cipher_uuid)); +joinable!(folders_ciphers -> folders (folder_uuid)); +allow_tables_to_appear_in_same_query!( + ciphers, + ciphers_collections, + collections, + folders, + folders_ciphers, + org_policies, + users_collections, + users_organizations, +); diff --git a/src/db/schemas/sqlite/schema.rs b/src/db/schemas/sqlite/schema.rs @@ -1,215 +0,0 @@ -table! { - auth_requests (uuid) { - uuid -> Text, - user_uuid -> Text, - organization_uuid -> Nullable<Text>, - request_device_identifier -> Text, - device_type -> Integer, - request_ip -> Text, - response_device_id -> Nullable<Text>, - access_code -> Text, - public_key -> Text, - enc_key -> Nullable<Text>, - master_password_hash -> Nullable<Text>, - approved -> Nullable<Bool>, - creation_date -> Timestamp, - response_date -> Nullable<Timestamp>, - authentication_date -> Nullable<Timestamp>, - } -} - -table! { - ciphers (uuid) { - uuid -> Text, - created_at -> Timestamp, - updated_at -> Timestamp, - user_uuid -> Nullable<Text>, - organization_uuid -> Nullable<Text>, - key -> Nullable<Text>, - atype -> Integer, - name -> Text, - notes -> Nullable<Text>, - fields -> Nullable<Text>, - data -> Text, - password_history -> Nullable<Text>, - deleted_at -> Nullable<Timestamp>, - reprompt -> Nullable<Integer>, - } -} -table! { - ciphers_collections (cipher_uuid, collection_uuid) { - cipher_uuid -> Text, - collection_uuid -> Text, - } -} - -table! { - collections (uuid) { - uuid -> Text, - org_uuid -> Text, - name -> Text, - external_id -> Nullable<Text>, - } -} -table! { - devices (uuid, user_uuid) { - uuid -> Text, - created_at -> Timestamp, - updated_at -> Timestamp, - user_uuid -> Text, - name -> Text, - atype -> Integer, - push_uuid -> Nullable<Text>, - push_token -> Nullable<Text>, - refresh_token -> Text, - twofactor_remember -> Nullable<Text>, - } -} - -table! { - favorites (user_uuid, cipher_uuid) { - user_uuid -> Text, - cipher_uuid -> Text, - } -} - -table! { - folders (uuid) { - uuid -> Text, - created_at -> Timestamp, - updated_at -> Timestamp, - user_uuid -> Text, - name -> Text, - } -} - -table! { - folders_ciphers (cipher_uuid, folder_uuid) { - cipher_uuid -> Text, - folder_uuid -> Text, - } -} - -table! { - org_policies (uuid) { - uuid -> Text, - org_uuid -> Text, - atype -> Integer, - enabled -> Bool, - data -> Text, - } -} - -table! { - organizations (uuid) { - uuid -> Text, - name -> Text, - billing_email -> Text, - private_key -> Nullable<Text>, - public_key -> Nullable<Text>, - } -} - -table! { - totp (user_uuid) { - user_uuid -> Text, - token -> Text, - last_used -> BigInt, - } -} - -table! { - users (uuid) { - uuid -> Text, - enabled -> Bool, - created_at -> Timestamp, - updated_at -> Timestamp, - verified_at -> Nullable<Timestamp>, - last_verifying_at -> Nullable<Timestamp>, - login_verify_count -> Integer, - email -> Text, - email_new -> Nullable<Text>, - email_new_token -> Nullable<Text>, - name -> Text, - password_hash -> Binary, - salt -> Binary, - password_iterations -> Integer, - password_hint -> Nullable<Text>, - akey -> Text, - private_key -> Nullable<Text>, - public_key -> Nullable<Text>, - totp_secret -> Nullable<Text>, - totp_recover -> Nullable<Text>, - security_stamp -> Text, - stamp_exception -> Nullable<Text>, - equivalent_domains -> Text, - excluded_globals -> Text, - client_kdf_type -> Integer, - client_kdf_iter -> Integer, - client_kdf_memory -> Nullable<Integer>, - client_kdf_parallelism -> Nullable<Integer>, - api_key -> Nullable<Text>, - avatar_color -> Nullable<Text>, - external_id -> Nullable<Text>, - } -} - -table! { - users_collections (user_uuid, collection_uuid) { - user_uuid -> Text, - collection_uuid -> Text, - read_only -> Bool, - hide_passwords -> Bool, - } -} - -table! { - users_organizations (uuid) { - uuid -> Text, - user_uuid -> Text, - org_uuid -> Text, - access_all -> Bool, - akey -> Text, - status -> Integer, - atype -> Integer, - reset_password_key -> Nullable<Text>, - external_id -> Nullable<Text>, - } -} - -table! { - webauthn (credential_id) { - credential_id -> Text, - user_uuid -> Text, - id -> BigInt, - name -> Text, - security_key -> Text, - } -} - -table! { - webauthn_auth (user_uuid) { - user_uuid -> Text, - data -> Text, - } -} - -table! { - webauthn_reg (user_uuid) { - user_uuid -> Text, - data -> Text, - } -} - -joinable!(folders_ciphers -> ciphers (cipher_uuid)); -joinable!(folders_ciphers -> folders (folder_uuid)); -allow_tables_to_appear_in_same_query!( - ciphers, - ciphers_collections, - collections, - folders, - folders_ciphers, - org_policies, - users_collections, - users_organizations, -); diff --git a/src/error.rs b/src/error.rs @@ -43,7 +43,6 @@ use jsonwebtoken::errors::Error as JwtErr; use openssl::error::ErrorStack as SSLErr; #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] use priv_sep::UnveilErr; -use regex::Error as RegexErr; use rocket::error::Error as RocketErr; use serde_json::{Error as SerdeErr, Value}; use std::io::Error as IoErr; @@ -74,7 +73,6 @@ make_error! { Io(IoErr): _has_source, _api_error, Time(TimeErr): _has_source, _api_error, - Regex(RegexErr): _has_source, _api_error, OpenSSL(SSLErr): _has_source, _api_error, Rocket(RocketErr): _has_source, _api_error, @@ -103,7 +101,6 @@ make_error! { Io(IoErr): _has_source, _api_error, Time(TimeErr): _has_source, _api_error, - Regex(RegexErr): _has_source, _api_error, OpenSSL(SSLErr): _has_source, _api_error, Rocket(RocketErr): _has_source, _api_error, @@ -142,7 +139,6 @@ impl Debug for Error { | ErrorKind::JWt(_) | ErrorKind::Io(_) | ErrorKind::Time(_) - | ErrorKind::Regex(_) | ErrorKind::OpenSSL(_) | ErrorKind::Rocket(_) | ErrorKind::DieselCon(_) diff --git a/src/main.rs b/src/main.rs @@ -79,7 +79,7 @@ use alloc::sync::Arc; use config::Config; pub use error::{Error, MapResult}; use std::env; -use std::{fs, path::Path, process}; +use std::{path::Path, process}; use tokio::runtime::Builder; use tokio::signal; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -90,10 +90,6 @@ fn main() -> Result<(), Error> { static_init(); check_data_folder(); check_web_vault(); - create_dir(Config::ATTACHMENTS_FOLDER, "attachments folder"); - create_dir(Config::ICON_CACHE_FOLDER, "icon cache"); - create_dir(Config::SENDS_FOLDER, "sends folder"); - create_dir(Config::TMP_FOLDER, "tmp folder"); Builder::new_multi_thread() .enable_all() .build() @@ -132,11 +128,6 @@ fn static_init() { api::init_ws_anonymous_subscriptions(); } -fn create_dir(path: &str, description: &str) { - fs::create_dir_all(path) - .unwrap_or_else(|_| panic!("Error creating {description} directory '{path}'")); -} - #[allow(clippy::exit)] fn check_data_folder() { if !Path::new(Config::DATA_FOLDER).is_dir() {