diff --git a/Cargo.lock b/Cargo.lock index 46325e0..9a40d63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -181,7 +181,7 @@ dependencies = [ "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -503,12 +503,12 @@ dependencies = [ "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "hyper" -version = "0.11.18" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -546,7 +546,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.18 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.19 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -587,7 +587,7 @@ dependencies = [ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -866,7 +866,7 @@ dependencies = [ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -943,12 +943,12 @@ dependencies = [ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.25 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "openssl-sys" -version = "0.9.25" +version = "0.9.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1153,7 +1153,7 @@ dependencies = [ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.18 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.19 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libflate 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1165,7 +1165,7 @@ dependencies = [ "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1202,7 +1202,7 @@ dependencies = [ "state 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "yansi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1347,7 +1347,7 @@ dependencies = [ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1650,7 +1650,7 @@ dependencies = [ [[package]] name = "url" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1829,7 +1829,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum hmac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb5aa9647ba4711e9d6968dc1c810cd23989ed435443ca962e1bf6d8b8b83ff" "checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37" "checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2" -"checksum hyper 0.11.18 (registry+https://github.com/rust-lang/crates.io-index)" = "c4f9b276c87e3fc1902a8bdfcce264c3f7c8a1c35e5e0c946062739f55026664" +"checksum hyper 0.11.19 (registry+https://github.com/rust-lang/crates.io-index)" = "47659bb1cb7ef3cd7b4f9bd2a11349b8d92097d34f9597a3c09e9bcefaf92b61" "checksum hyper-sync-rustls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c6df6f419a9f116cc93b5f39a5ded1161e088a2c8424c8fcd1d4049193424a4" "checksum hyper-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c81fa95203e2a6087242c38691a0210f23e9f3f8f944350bd676522132e2985" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" @@ -1873,7 +1873,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum oath 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ec6405dc6afe8219020d535f9ad888a12b191bbc8ce1c55f7ee663bde5be80ca" "checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985" -"checksum openssl-sys 0.9.25 (registry+https://github.com/rust-lang/crates.io-index)" = "93b3cbfaccf11969aea8c2041bfafc43c81666c1ce673476e19395c92cc77bf4" +"checksum openssl-sys 0.9.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5a41ce2f5f2d939c80decde8fcfcf5837c203ca6c06a553510a2fcb84fa3ef1" "checksum ordermap 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b81cf3b8cb96aa0e73bbedfcdc9708d09fec2854ba8d474be4e6f666d7379e8b" "checksum pear 0.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "b5c2dabd6c1650d9bfac8e46be7b518b31c3885ab4412de1aca330938616c5bd" "checksum pear_codegen 0.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "df863bb78b3ee6b049278324eea8df6b2553a8db9a3504c0e32cfcc17bc8d18c" @@ -1955,7 +1955,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" "checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" "checksum url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)" = "cbaa8377a162d88e7d15db0cf110c8523453edcbc5bc66d2b6fffccffa34a068" -"checksum url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa35e768d4daf1d85733418a49fb42e10d7f633e394fccab4ab7aba897053fe2" +"checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "78c590b5bd79ed10aad8fb75f078a59d8db445af6c743e55c4a53227fc01c13f" "checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22" diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 7ac7468..275d55a 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -1,9 +1,9 @@ -use rocket_contrib::{Json, Value}; +use rocket_contrib::Json; use db::DbConn; use db::models::*; -use api::{JsonResult, EmptyResult}; +use api::{PasswordData, JsonResult, EmptyResult}; use auth::Headers; use CONFIG; @@ -33,15 +33,12 @@ fn register(data: Json, conn: DbConn) -> EmptyResult { if !CONFIG.signups_allowed { err!(format!("Signups not allowed")) } - println!("DEBUG - {:#?}", data); if let Some(_) = User::find_by_mail(&data.email, &conn) { err!("Email already exists") } - let mut user = User::new(data.email, - data.key, - data.masterPasswordHash); + let mut user = User::new(data.email, data.key, data.masterPasswordHash); // Add extra fields if present if let Some(name) = data.name { @@ -81,32 +78,36 @@ fn post_keys(data: Json, headers: Headers, conn: DbConn) -> JsonResult Ok(Json(user.to_json())) } -#[post("/accounts/password", data = "")] -fn post_password(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { - let key = data["key"].as_str().unwrap(); - let password_hash = data["masterPasswordHash"].as_str().unwrap(); - let new_password_hash = data["newMasterPasswordHash"].as_str().unwrap(); +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct ChangePassData { + masterPasswordHash: String, + newMasterPasswordHash: String, + key: String, +} +#[post("/accounts/password", data = "")] +fn post_password(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { + let data: ChangePassData = data.into_inner(); let mut user = headers.user; - if !user.check_valid_password(password_hash) { + if !user.check_valid_password(&data.masterPasswordHash) { err!("Invalid password") } - user.set_password(new_password_hash); - user.key = key.to_string(); + user.set_password(&data.newMasterPasswordHash); + user.key = data.key; user.save(&conn); Ok(()) } #[post("/accounts/security-stamp", data = "")] -fn post_sstamp(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { - let password_hash = data["masterPasswordHash"].as_str().unwrap(); - +fn post_sstamp(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { + let data: PasswordData = data.into_inner(); let mut user = headers.user; - if !user.check_valid_password(password_hash) { + if !user.check_valid_password(&data.masterPasswordHash) { err!("Invalid password") } @@ -116,34 +117,39 @@ fn post_sstamp(data: Json, headers: Headers, conn: DbConn) -> EmptyResult Ok(()) } -#[post("/accounts/email-token", data = "")] -fn post_email(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { - let password_hash = data["masterPasswordHash"].as_str().unwrap(); - let new_email = data["newEmail"].as_str().unwrap(); +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct ChangeEmailData { + masterPasswordHash: String, + newEmail: String, +} + +#[post("/accounts/email-token", data = "")] +fn post_email(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { + let data: ChangeEmailData = data.into_inner(); let mut user = headers.user; - if !user.check_valid_password(password_hash) { + if !user.check_valid_password(&data.masterPasswordHash) { err!("Invalid password") } - if User::find_by_mail(new_email, &conn).is_some() { + if User::find_by_mail(&data.newEmail, &conn).is_some() { err!("Email already in use"); } - user.email = new_email.to_string(); + user.email = data.newEmail; user.save(&conn); Ok(()) } #[post("/accounts/delete", data = "")] -fn delete_account(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { - let password_hash = data["masterPasswordHash"].as_str().unwrap(); - +fn delete_account(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { + let data: PasswordData = data.into_inner(); let user = headers.user; - if !user.check_valid_password(password_hash) { + if !user.check_valid_password(&data.masterPasswordHash) { err!("Invalid password") } diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index c43cb34..9b7368f 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -16,7 +16,7 @@ use db::models::*; use util; use crypto; -use api::{self, JsonResult, EmptyResult}; +use api::{self, PasswordData, JsonResult, EmptyResult}; use auth::Headers; use CONFIG; @@ -74,7 +74,9 @@ fn get_cipher(uuid: String, headers: Headers, conn: DbConn) -> JsonResult { struct CipherData { #[serde(rename = "type")] type_: i32, + // Folder id is not included in import folderId: Option, + // TODO: Some of these might appear all the time, no need for Option organizationId: Option, name: Option, notes: Option, @@ -182,38 +184,62 @@ fn copy_values(from: &Value, to: &mut Value) -> bool { true } +use super::folders::FolderData; + +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct ImportData { + ciphers: Vec, + folders: Vec, + folderRelationships: Vec, +} + +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct RelationsData { + // Cipher id + key: u32, + // Folder id + value: u32, +} + + #[post("/ciphers/import", data = "")] -fn post_ciphers_import(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { - let data: Value = data.into_inner(); - let folders_value = data["folders"].as_array().unwrap(); - let ciphers_value = data["ciphers"].as_array().unwrap(); - let relations_value = data["folderRelationships"].as_array().unwrap(); +fn post_ciphers_import(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { + let data: ImportData = data.into_inner(); // Read and create the folders - let folders: Vec<_> = folders_value.iter().map(|f| { - let name = f["name"].as_str().unwrap().to_string(); - let mut folder = Folder::new(headers.user.uuid.clone(), name); + let folders: Vec<_> = data.folders.iter().map(|folder| { + let mut folder = Folder::new(headers.user.uuid.clone(), folder.name.clone()); folder.save(&conn); folder }).collect(); // Read the relations between folders and ciphers - let relations = relations_value.iter().map(|r| r["value"].as_u64().unwrap() as usize); + use std::collections::HashMap; + let mut relations_map = HashMap::new(); + + for relation in data.folderRelationships { + relations_map.insert(relation.key, relation.value); + } // Read and create the ciphers - use serde::Deserialize; - ciphers_value.iter().zip(
relations).map(|(c, fp)| { - let folder_uuid = folders[fp].uuid.clone(); - let data = CipherData::deserialize(c.clone()).unwrap(); + let mut index = 0; + for cipher_data in data.ciphers { + let folder_uuid = relations_map.get(&index) + .map(|i| folders[*i as usize].uuid.clone()); let user_uuid = headers.user.uuid.clone(); - let favorite = data.favorite.unwrap_or(false); - let mut cipher = Cipher::new(user_uuid, data.type_, favorite); + let favorite = cipher_data.favorite.unwrap_or(false); + let mut cipher = Cipher::new(user_uuid, cipher_data.type_, favorite); - if update_cipher_from_data(&mut cipher, data, &headers, &conn).is_err() { return; } + if update_cipher_from_data(&mut cipher, cipher_data, &headers, &conn).is_err() { err!("Error creating cipher") } + + cipher.folder_uuid = folder_uuid; cipher.save(&conn); - }); + index += 1; + } Ok(()) } @@ -279,7 +305,7 @@ fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers let attachment = Attachment::new(file_name, cipher.uuid.clone(), name, size); println!("Attachment: {:#?}", attachment); attachment.save(&conn); - }); + }).expect("Error processing multipart data"); Ok(Json(cipher.to_json(&headers.host, &conn))) } @@ -340,13 +366,14 @@ fn delete_cipher(uuid: String, headers: Headers, conn: DbConn) -> EmptyResult { Ok(()) } -#[post("/ciphers/delete", data = "")] -fn delete_all(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { - let password_hash = data["masterPasswordHash"].as_str().unwrap(); +#[post("/ciphers/purge", data = "")] +fn delete_all(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { + let data: PasswordData = data.into_inner(); + let password_hash = data.masterPasswordHash; let user = headers.user; - if !user.check_valid_password(password_hash) { + if !user.check_valid_password(&password_hash) { err!("Invalid password") } diff --git a/src/api/core/folders.rs b/src/api/core/folders.rs index fb83733..d5f6c2a 100644 --- a/src/api/core/folders.rs +++ b/src/api/core/folders.rs @@ -32,15 +32,16 @@ fn get_folder(uuid: String, headers: Headers, conn: DbConn) -> JsonResult { Ok(Json(folder.to_json())) } +#[derive(Deserialize)] +pub struct FolderData { + pub name: String +} + #[post("/folders", data = "")] -fn post_folders(data: Json, headers: Headers, conn: DbConn) -> JsonResult { - let name = &data["name"].as_str(); +fn post_folders(data: Json, headers: Headers, conn: DbConn) -> JsonResult { + let data: FolderData = data.into_inner(); - if name.is_none() { - err!("Invalid name") - } - - let mut folder = Folder::new(headers.user.uuid.clone(), name.unwrap().into()); + let mut folder = Folder::new(headers.user.uuid.clone(), data.name); folder.save(&conn); @@ -48,12 +49,14 @@ fn post_folders(data: Json, headers: Headers, conn: DbConn) -> JsonResult } #[post("/folders/", data = "")] -fn post_folder(uuid: String, data: Json, headers: Headers, conn: DbConn) -> JsonResult { +fn post_folder(uuid: String, data: Json, headers: Headers, conn: DbConn) -> JsonResult { put_folder(uuid, data, headers, conn) } #[put("/folders/", data = "")] -fn put_folder(uuid: String, data: Json, headers: Headers, conn: DbConn) -> JsonResult { +fn put_folder(uuid: String, data: Json, headers: Headers, conn: DbConn) -> JsonResult { + let data: FolderData = data.into_inner(); + let mut folder = match Folder::find_by_uuid(&uuid, &conn) { Some(folder) => folder, _ => err!("Invalid folder") @@ -63,13 +66,7 @@ fn put_folder(uuid: String, data: Json, headers: Headers, conn: DbConn) - err!("Folder belongs to another user") } - let name = &data["name"].as_str(); - - if name.is_none() { - err!("Invalid name") - } - - folder.name = name.unwrap().into(); + folder.name = data.name; folder.save(&conn); diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 033bab3..f6ae3be 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -67,7 +67,7 @@ pub fn routes() -> Vec { use rocket::Route; -use rocket_contrib::{Json, Value}; +use rocket_contrib::Json; use db::DbConn; @@ -84,14 +84,6 @@ fn put_device_token(uuid: String, conn: DbConn) -> JsonResult { err!("Not implemented") } - -#[derive(Deserialize, Debug)] -#[allow(non_snake_case)] -struct EquivDomainData { - ExcludedGlobalEquivalentDomains: Option>, - EquivalentDomains: Option>>, -} - #[derive(Serialize, Deserialize, Debug)] #[allow(non_snake_case)] struct GlobalDomain { @@ -123,6 +115,14 @@ fn get_eq_domains(headers: Headers) -> JsonResult { }))) } + +#[derive(Deserialize, Debug)] +#[allow(non_snake_case)] +struct EquivDomainData { + ExcludedGlobalEquivalentDomains: Option>, + EquivalentDomains: Option>>, +} + #[post("/settings/domains", data = "")] fn post_eq_domains(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { let data: EquivDomainData = data.into_inner(); diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 97b93e2..6bd5d07 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -1,3 +1,5 @@ +#![allow(unused_imports)] + use rocket_contrib::{Json, Value}; use db::DbConn; diff --git a/src/api/core/two_factor.rs b/src/api/core/two_factor.rs index 90320e9..7ebdaed 100644 --- a/src/api/core/two_factor.rs +++ b/src/api/core/two_factor.rs @@ -4,13 +4,11 @@ use data_encoding::BASE32; use db::DbConn; -use util; use crypto; -use api::{JsonResult, EmptyResult}; +use api::{PasswordData, JsonResult}; use auth::Headers; - #[get("/two-factor")] fn get_twofactor(headers: Headers) -> JsonResult { let data = if headers.user.totp_secret.is_none() { @@ -30,10 +28,10 @@ fn get_twofactor(headers: Headers) -> JsonResult { } #[post("/two-factor/get-recover", data = "")] -fn get_recover(data: Json, headers: Headers) -> JsonResult { - let password_hash = data["masterPasswordHash"].as_str().unwrap(); +fn get_recover(data: Json, headers: Headers) -> JsonResult { + let data: PasswordData = data.into_inner(); - if !headers.user.check_valid_password(password_hash) { + if !headers.user.check_valid_password(&data.masterPasswordHash) { err!("Invalid password"); } @@ -43,29 +41,33 @@ fn get_recover(data: Json, headers: Headers) -> JsonResult { }))) } +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct RecoverTwoFactor { + masterPasswordHash: String, + email: String, + recoveryCode: String, +} + #[post("/two-factor/recover", data = "")] -fn recover(data: Json, conn: DbConn) -> JsonResult { - println!("{:#?}", data); +fn recover(data: Json, conn: DbConn) -> JsonResult { + let data: RecoverTwoFactor = data.into_inner(); use db::models::User; // Get the user - let username = data["email"].as_str().unwrap(); - let mut user = match User::find_by_mail(username, &conn) { + let mut user = match User::find_by_mail(&data.email, &conn) { Some(user) => user, None => err!("Username or password is incorrect. Try again.") }; // Check password - let password = data["masterPasswordHash"].as_str().unwrap(); - if !user.check_valid_password(password) { + if !user.check_valid_password(&data.masterPasswordHash) { err!("Username or password is incorrect. Try again.") } // Check if recovery code is correct - let recovery_code = data["recoveryCode"].as_str().unwrap(); - - if !user.check_valid_recovery_code(recovery_code) { + if !user.check_valid_recovery_code(&data.recoveryCode) { err!("Recovery code is incorrect. Try again.") } @@ -77,10 +79,10 @@ fn recover(data: Json, conn: DbConn) -> JsonResult { } #[post("/two-factor/get-authenticator", data = "")] -fn generate_authenticator(data: Json, headers: Headers) -> JsonResult { - let password_hash = data["masterPasswordHash"].as_str().unwrap(); +fn generate_authenticator(data: Json, headers: Headers) -> JsonResult { + let data: PasswordData = data.into_inner(); - if !headers.user.check_valid_password(password_hash) { + if !headers.user.check_valid_password(&data.masterPasswordHash) { err!("Invalid password"); } @@ -96,15 +98,24 @@ fn generate_authenticator(data: Json, headers: Headers) -> JsonResult { }))) } -#[post("/two-factor/authenticator", data = "")] -fn activate_authenticator(data: Json, headers: Headers, conn: DbConn) -> JsonResult { - let password_hash = data["masterPasswordHash"].as_str().unwrap(); +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct EnableTwoFactorData { + masterPasswordHash: String, + key: String, + token: u64, +} - if !headers.user.check_valid_password(password_hash) { +#[post("/two-factor/authenticator", data = "")] +fn activate_authenticator(data: Json, headers: Headers, conn: DbConn) -> JsonResult { + let data: EnableTwoFactorData = data.into_inner(); + let password_hash = data.masterPasswordHash; + let key = data.key; + let token = data.token; + + if !headers.user.check_valid_password(&password_hash) { err!("Invalid password"); } - let token = data["token"].as_str(); - let key = data["key"].as_str().unwrap(); // Validate key as base32 and 20 bytes length let decoded_key: Vec = match BASE32.decode(key.as_bytes()) { @@ -121,7 +132,7 @@ fn activate_authenticator(data: Json, headers: Headers, conn: DbConn) -> user.totp_secret = Some(key.to_uppercase()); // Validate the token provided with the key - if !user.check_totp_code(util::parse_option_string(token)) { + if !user.check_totp_code(Some(token)) { err!("Invalid totp code") } @@ -138,12 +149,20 @@ fn activate_authenticator(data: Json, headers: Headers, conn: DbConn) -> }))) } -#[post("/two-factor/disable", data = "")] -fn disable_authenticator(data: Json, headers: Headers, conn: DbConn) -> JsonResult { - let _type = &data["type"]; - let password_hash = data["masterPasswordHash"].as_str().unwrap(); +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct DisableTwoFactorData { + masterPasswordHash: String, + #[serde(rename = "type")] + type_: u32, +} - if !headers.user.check_valid_password(password_hash) { +#[post("/two-factor/disable", data = "")] +fn disable_authenticator(data: Json, headers: Headers, conn: DbConn) -> JsonResult { + let data: DisableTwoFactorData = data.into_inner(); + let password_hash = data.masterPasswordHash; + + if !headers.user.check_valid_password(&password_hash) { err!("Invalid password"); } diff --git a/src/api/identity.rs b/src/api/identity.rs index 11d868c..e6a7d9b 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -10,7 +10,7 @@ use db::models::*; use util; -use api::{JsonResult, EmptyResult}; +use api::JsonResult; pub fn routes() -> Vec { routes![ login] diff --git a/src/api/mod.rs b/src/api/mod.rs index db33fce..09d7265 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -11,5 +11,13 @@ pub use self::web::routes as web_routes; use rocket::response::status::BadRequest; use rocket_contrib::Json; +// Type aliases for API methods results type JsonResult = Result>; type EmptyResult = Result<(), BadRequest>; + +// Common structs representing JSON data received +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct PasswordData { + masterPasswordHash: String +} diff --git a/src/auth.rs b/src/auth.rs index a160727..d5fc4b2 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -124,7 +124,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Headers { // Check JWT token is valid and get device and user from it let claims: JWTClaims = match decode_jwt(access_token) { Ok(claims) => claims, - Err(msg) => err_handler!("Invalid claim") + Err(_) => err_handler!("Invalid claim") }; let device_uuid = claims.device; diff --git a/src/crypto.rs b/src/crypto.rs index 645ee90..2698c0e 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -30,7 +30,7 @@ pub fn get_random_64() -> Vec { pub fn get_random(mut array: Vec) -> Vec { use ring::rand::{SecureRandom, SystemRandom}; - SystemRandom::new().fill(&mut array); + SystemRandom::new().fill(&mut array).expect("Error generating random values"); array } diff --git a/src/main.rs b/src/main.rs index a60d4b4..79bd6d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -#![allow(unused)] +#![allow(unused_variables, dead_code)] #![feature(plugin, custom_derive)] #![cfg_attr(test, plugin(stainless))] @@ -31,9 +31,7 @@ extern crate lazy_static; use std::{io, env}; - -use rocket::{Data, Request, Rocket}; -use rocket::fairing::{Fairing, Info, Kind}; +use rocket::Rocket; #[macro_use] mod util; diff --git a/src/util.rs b/src/util.rs index f656382..e240843 100644 --- a/src/util.rs +++ b/src/util.rs @@ -61,7 +61,9 @@ pub fn delete_file(path: &str) -> bool { let res = fs::remove_file(path).is_ok(); if let Some(parent) = Path::new(path).parent() { - fs::remove_dir(parent); // Only removes if the directory is empty + // If the directory isn't empty, this returns an error, which we ignore + // We only want to delete the folder if it's empty + fs::remove_dir(parent).ok(); } res