Spiegel von
https://github.com/dani-garcia/vaultwarden.git
synchronisiert 2024-11-29 06:20:29 +01:00
Implemented proper error handling, now we can do user.save($conn)?;
and it works.
In the future, maybe we can do the same with the `find_by_id` methods that return an Option.
Dieser Commit ist enthalten in:
Ursprung
172f1770cf
Commit
6a99849a1e
22 geänderte Dateien mit 472 neuen und 487 gelöschten Zeilen
|
@ -36,7 +36,17 @@ fn invite_user(data: JsonUpcase<InviteData>, _token: AdminToken, conn: DbConn) -
|
||||||
err!("User already exists")
|
err!("User already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
err!("Unimplemented")
|
if !CONFIG.invitations_allowed {
|
||||||
|
err!("Invitations are not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut invitation = Invitation::new(data.Email.clone());
|
||||||
|
let mut user = User::new(data.Email);
|
||||||
|
|
||||||
|
invitation.save(&conn)?;
|
||||||
|
user.save(&conn)?;
|
||||||
|
|
||||||
|
Ok(Json(json!({})))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/users/<uuid>/delete")]
|
#[post("/users/<uuid>/delete")]
|
||||||
|
@ -46,10 +56,8 @@ fn delete_user(uuid: String, _token: AdminToken, conn: DbConn) -> JsonResult {
|
||||||
None => err!("User doesn't exist"),
|
None => err!("User doesn't exist"),
|
||||||
};
|
};
|
||||||
|
|
||||||
match user.delete(&conn) {
|
user.delete(&conn)?;
|
||||||
Ok(_) => Ok(Json(json!({}))),
|
Ok(Json(json!({})))
|
||||||
Err(e) => err!("Error deleting user", e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AdminToken {}
|
pub struct AdminToken {}
|
||||||
|
|
|
@ -65,9 +65,7 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
|
||||||
if CONFIG.mail.is_none() {
|
if CONFIG.mail.is_none() {
|
||||||
for mut user_org in UserOrganization::find_invited_by_user(&user.uuid, &conn).iter_mut() {
|
for mut user_org in UserOrganization::find_invited_by_user(&user.uuid, &conn).iter_mut() {
|
||||||
user_org.status = UserOrgStatus::Accepted as i32;
|
user_org.status = UserOrgStatus::Accepted as i32;
|
||||||
if user_org.save(&conn).is_err() {
|
user_org.save(&conn)?;
|
||||||
err!("Failed to accept user to organization")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !Invitation::take(&data.Email, &conn) {
|
if !Invitation::take(&data.Email, &conn) {
|
||||||
err!("Error accepting invitation")
|
err!("Error accepting invitation")
|
||||||
|
@ -128,10 +126,7 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
|
||||||
user.public_key = Some(keys.PublicKey);
|
user.public_key = Some(keys.PublicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
match user.save(&conn) {
|
user.save(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed to save user"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/accounts/profile")]
|
#[get("/accounts/profile")]
|
||||||
|
@ -164,10 +159,8 @@ fn post_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbConn) -
|
||||||
Some(ref h) if h.is_empty() => None,
|
Some(ref h) if h.is_empty() => None,
|
||||||
_ => data.MasterPasswordHint,
|
_ => data.MasterPasswordHint,
|
||||||
};
|
};
|
||||||
match user.save(&conn) {
|
user.save(&conn)?;
|
||||||
Ok(()) => Ok(Json(user.to_json(&conn))),
|
Ok(Json(user.to_json(&conn)))
|
||||||
Err(_) => err!("Failed to save user profile"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/users/<uuid>/public-key")]
|
#[get("/users/<uuid>/public-key")]
|
||||||
|
@ -193,10 +186,8 @@ fn post_keys(data: JsonUpcase<KeysData>, headers: Headers, conn: DbConn) -> Json
|
||||||
user.private_key = Some(data.EncryptedPrivateKey);
|
user.private_key = Some(data.EncryptedPrivateKey);
|
||||||
user.public_key = Some(data.PublicKey);
|
user.public_key = Some(data.PublicKey);
|
||||||
|
|
||||||
match user.save(&conn) {
|
user.save(&conn)?;
|
||||||
Ok(()) => Ok(Json(user.to_json(&conn))),
|
Ok(Json(user.to_json(&conn)))
|
||||||
Err(_) => err!("Failed to save the user's keys"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -218,10 +209,7 @@ fn post_password(data: JsonUpcase<ChangePassData>, headers: Headers, conn: DbCon
|
||||||
|
|
||||||
user.set_password(&data.NewMasterPasswordHash);
|
user.set_password(&data.NewMasterPasswordHash);
|
||||||
user.key = data.Key;
|
user.key = data.Key;
|
||||||
match user.save(&conn) {
|
user.save(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed to save password"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -248,10 +236,7 @@ fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, conn: DbConn) ->
|
||||||
user.client_kdf_type = data.Kdf;
|
user.client_kdf_type = data.Kdf;
|
||||||
user.set_password(&data.NewMasterPasswordHash);
|
user.set_password(&data.NewMasterPasswordHash);
|
||||||
user.key = data.Key;
|
user.key = data.Key;
|
||||||
match user.save(&conn) {
|
user.save(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed to save password settings"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -295,9 +280,7 @@ fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, ws:
|
||||||
}
|
}
|
||||||
|
|
||||||
saved_folder.name = folder_data.Name;
|
saved_folder.name = folder_data.Name;
|
||||||
if saved_folder.save(&conn).is_err() {
|
saved_folder.save(&conn)?
|
||||||
err!("Failed to save folder")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update cipher data
|
// Update cipher data
|
||||||
|
@ -323,11 +306,7 @@ fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, ws:
|
||||||
user.private_key = Some(data.PrivateKey);
|
user.private_key = Some(data.PrivateKey);
|
||||||
user.reset_security_stamp();
|
user.reset_security_stamp();
|
||||||
|
|
||||||
if user.save(&conn).is_err() {
|
user.save(&conn)
|
||||||
err!("Failed modify user key");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/accounts/security-stamp", data = "<data>")]
|
#[post("/accounts/security-stamp", data = "<data>")]
|
||||||
|
@ -340,10 +319,7 @@ fn post_sstamp(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -
|
||||||
}
|
}
|
||||||
|
|
||||||
user.reset_security_stamp();
|
user.reset_security_stamp();
|
||||||
match user.save(&conn) {
|
user.save(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed to reset security stamp"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -398,10 +374,7 @@ fn post_email(data: JsonUpcase<ChangeEmailData>, headers: Headers, conn: DbConn)
|
||||||
user.set_password(&data.NewMasterPasswordHash);
|
user.set_password(&data.NewMasterPasswordHash);
|
||||||
user.key = data.Key;
|
user.key = data.Key;
|
||||||
|
|
||||||
match user.save(&conn) {
|
user.save(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed to save email address"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/accounts/delete", data = "<data>")]
|
#[post("/accounts/delete", data = "<data>")]
|
||||||
|
@ -418,10 +391,7 @@ fn delete_account(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn
|
||||||
err!("Invalid password")
|
err!("Invalid password")
|
||||||
}
|
}
|
||||||
|
|
||||||
match user.delete(&conn) {
|
user.delete(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed deleting user account, are you the only owner of some organization?"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/accounts/revision-date")]
|
#[get("/accounts/revision-date")]
|
||||||
|
@ -446,9 +416,7 @@ fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResul
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(ref mail_config) = CONFIG.mail {
|
if let Some(ref mail_config) = CONFIG.mail {
|
||||||
if let Err(e) = mail::send_password_hint(&data.Email, hint, mail_config) {
|
mail::send_password_hint(&data.Email, hint, mail_config)?;
|
||||||
err!(format!("There have been a problem sending the email: {}", e));
|
|
||||||
}
|
|
||||||
} else if CONFIG.show_password_hint {
|
} else if CONFIG.show_password_hint {
|
||||||
if let Some(hint) = hint {
|
if let Some(hint) = hint {
|
||||||
err!(format!("Your password hint is: {}", &hint));
|
err!(format!("Your password hint is: {}", &hint));
|
||||||
|
|
|
@ -182,10 +182,7 @@ fn post_ciphers_admin(data: JsonUpcase<ShareCipherData>, headers: Headers, conn:
|
||||||
|
|
||||||
let mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone());
|
let mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone());
|
||||||
cipher.user_uuid = Some(headers.user.uuid.clone());
|
cipher.user_uuid = Some(headers.user.uuid.clone());
|
||||||
match cipher.save(&conn) {
|
cipher.save(&conn)?;
|
||||||
Ok(()) => (),
|
|
||||||
Err(_) => err!("Failed saving cipher")
|
|
||||||
};
|
|
||||||
|
|
||||||
share_cipher_by_uuid(&cipher.uuid, data, &headers, &conn, &ws)
|
share_cipher_by_uuid(&cipher.uuid, data, &headers, &conn, &ws)
|
||||||
}
|
}
|
||||||
|
@ -248,10 +245,7 @@ pub fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &
|
||||||
saved_att.key = Some(attachment.Key);
|
saved_att.key = Some(attachment.Key);
|
||||||
saved_att.file_name = attachment.FileName;
|
saved_att.file_name = attachment.FileName;
|
||||||
|
|
||||||
match saved_att.save(&conn) {
|
saved_att.save(&conn)?;
|
||||||
Ok(()) => (),
|
|
||||||
Err(_) => err!("Failed to save attachment")
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,17 +278,11 @@ pub fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &
|
||||||
cipher.data = type_data.to_string();
|
cipher.data = type_data.to_string();
|
||||||
cipher.password_history = data.PasswordHistory.map(|f| f.to_string());
|
cipher.password_history = data.PasswordHistory.map(|f| f.to_string());
|
||||||
|
|
||||||
match cipher.save(&conn) {
|
cipher.save(&conn)?;
|
||||||
Ok(()) => (),
|
|
||||||
Err(_) => err!("Failed to save cipher")
|
|
||||||
};
|
|
||||||
ws.send_cipher_update(ut, &cipher, &cipher.update_users_revision(&conn));
|
ws.send_cipher_update(ut, &cipher, &cipher.update_users_revision(&conn));
|
||||||
|
|
||||||
if cipher.move_to_folder(data.FolderId, &headers.user.uuid, &conn).is_err() {
|
cipher.move_to_folder(data.FolderId, &headers.user.uuid, &conn)
|
||||||
err!("Error saving the folder information")
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
use super::folders::FolderData;
|
use super::folders::FolderData;
|
||||||
|
@ -325,11 +313,9 @@ fn post_ciphers_import(data: JsonUpcase<ImportData>, headers: Headers, conn: DbC
|
||||||
let mut folders: Vec<_> = Vec::new();
|
let mut folders: Vec<_> = Vec::new();
|
||||||
for folder in data.Folders.into_iter() {
|
for folder in data.Folders.into_iter() {
|
||||||
let mut new_folder = Folder::new(headers.user.uuid.clone(), folder.Name);
|
let mut new_folder = Folder::new(headers.user.uuid.clone(), folder.Name);
|
||||||
if new_folder.save(&conn).is_err() {
|
new_folder.save(&conn)?;
|
||||||
err!("Failed importing folders")
|
|
||||||
} else {
|
folders.push(new_folder);
|
||||||
folders.push(new_folder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the relations between folders and ciphers
|
// Read the relations between folders and ciphers
|
||||||
|
@ -351,10 +337,7 @@ fn post_ciphers_import(data: JsonUpcase<ImportData>, headers: Headers, conn: DbC
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut user = headers.user;
|
let mut user = headers.user;
|
||||||
match user.update_revision(&conn) {
|
user.update_revision(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed to update the revision, please log out and log back in to finish import.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -429,15 +412,9 @@ fn post_collections_admin(uuid: String, data: JsonUpcase<CollectionsAdminData>,
|
||||||
Some(collection) => {
|
Some(collection) => {
|
||||||
if collection.is_writable_by_user(&headers.user.uuid, &conn) {
|
if collection.is_writable_by_user(&headers.user.uuid, &conn) {
|
||||||
if posted_collections.contains(&collection.uuid) { // Add to collection
|
if posted_collections.contains(&collection.uuid) { // Add to collection
|
||||||
match CollectionCipher::save(&cipher.uuid, &collection.uuid, &conn) {
|
CollectionCipher::save(&cipher.uuid, &collection.uuid, &conn)?;
|
||||||
Ok(()) => (),
|
|
||||||
Err(_) => err!("Failed to add cipher to collection")
|
|
||||||
};
|
|
||||||
} else { // Remove from collection
|
} else { // Remove from collection
|
||||||
match CollectionCipher::delete(&cipher.uuid, &collection.uuid, &conn) {
|
CollectionCipher::delete(&cipher.uuid, &collection.uuid, &conn)?;
|
||||||
Ok(()) => (),
|
|
||||||
Err(_) => err!("Failed to remove cipher from collection")
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err!("No rights to modify the collection")
|
err!("No rights to modify the collection")
|
||||||
|
@ -540,10 +517,7 @@ fn share_cipher_by_uuid(uuid: &str, data: ShareCipherData, headers: &Headers, co
|
||||||
None => err!("Invalid collection ID provided"),
|
None => err!("Invalid collection ID provided"),
|
||||||
Some(collection) => {
|
Some(collection) => {
|
||||||
if collection.is_writable_by_user(&headers.user.uuid, &conn) {
|
if collection.is_writable_by_user(&headers.user.uuid, &conn) {
|
||||||
match CollectionCipher::save(&cipher.uuid.clone(), &collection.uuid, &conn) {
|
CollectionCipher::save(&cipher.uuid.clone(), &collection.uuid, &conn)?;
|
||||||
Ok(()) => (),
|
|
||||||
Err(_) => err!("Failed to add cipher to collection")
|
|
||||||
};
|
|
||||||
shared_to_collection = true;
|
shared_to_collection = true;
|
||||||
} else {
|
} else {
|
||||||
err!("No rights to modify the collection")
|
err!("No rights to modify the collection")
|
||||||
|
@ -614,10 +588,7 @@ fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers
|
||||||
|
|
||||||
let mut attachment = Attachment::new(file_name, cipher.uuid.clone(), name, size);
|
let mut attachment = Attachment::new(file_name, cipher.uuid.clone(), name, size);
|
||||||
attachment.key = attachment_key.clone();
|
attachment.key = attachment_key.clone();
|
||||||
match attachment.save(&conn) {
|
attachment.save(&conn).expect("Error saving attachment");
|
||||||
Ok(()) => (),
|
|
||||||
Err(_) => error!("Failed to save attachment")
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
_ => error!("Invalid multipart name")
|
_ => error!("Invalid multipart name")
|
||||||
}
|
}
|
||||||
|
@ -746,13 +717,9 @@ fn move_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbConn,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move cipher
|
// Move cipher
|
||||||
if cipher.move_to_folder(folder_id.clone(), &headers.user.uuid, &conn).is_err() {
|
cipher.move_to_folder(folder_id.clone(), &headers.user.uuid, &conn)?;
|
||||||
err!("Error saving the folder information")
|
cipher.save(&conn)?;
|
||||||
}
|
|
||||||
match cipher.save(&conn) {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(_) => err!("Failed to save cipher")
|
|
||||||
};
|
|
||||||
ws.send_cipher_update(UpdateType::SyncCipherUpdate, &cipher, &cipher.update_users_revision(&conn));
|
ws.send_cipher_update(UpdateType::SyncCipherUpdate, &cipher, &cipher.update_users_revision(&conn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -777,21 +744,14 @@ fn delete_all(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn, ws
|
||||||
|
|
||||||
// Delete ciphers and their attachments
|
// Delete ciphers and their attachments
|
||||||
for cipher in Cipher::find_owned_by_user(&user.uuid, &conn) {
|
for cipher in Cipher::find_owned_by_user(&user.uuid, &conn) {
|
||||||
if cipher.delete(&conn).is_err() {
|
cipher.delete(&conn)?;
|
||||||
err!("Failed deleting cipher")
|
ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
|
||||||
}
|
|
||||||
else {
|
|
||||||
ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete folders
|
// Delete folders
|
||||||
for f in Folder::find_by_user(&user.uuid, &conn) {
|
for f in Folder::find_by_user(&user.uuid, &conn) {
|
||||||
if f.delete(&conn).is_err() {
|
f.delete(&conn)?;
|
||||||
err!("Failed deleting folder")
|
ws.send_folder_update(UpdateType::SyncFolderCreate, &f);
|
||||||
} else {
|
|
||||||
ws.send_folder_update(UpdateType::SyncFolderCreate, &f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -807,13 +767,9 @@ fn _delete_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, ws: &Sta
|
||||||
err!("Cipher can't be deleted by user")
|
err!("Cipher can't be deleted by user")
|
||||||
}
|
}
|
||||||
|
|
||||||
match cipher.delete(&conn) {
|
cipher.delete(&conn)?;
|
||||||
Ok(()) => {
|
ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
|
||||||
ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
|
Ok(())
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(_) => err!("Failed deleting cipher")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _delete_cipher_attachment_by_id(uuid: &str, attachment_id: &str, headers: &Headers, conn: &DbConn, ws: &State<WebSocketUsers>) -> EmptyResult {
|
fn _delete_cipher_attachment_by_id(uuid: &str, attachment_id: &str, headers: &Headers, conn: &DbConn, ws: &State<WebSocketUsers>) -> EmptyResult {
|
||||||
|
@ -836,11 +792,7 @@ fn _delete_cipher_attachment_by_id(uuid: &str, attachment_id: &str, headers: &He
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete attachment
|
// Delete attachment
|
||||||
match attachment.delete(&conn) {
|
attachment.delete(&conn)?;
|
||||||
Ok(()) => {
|
ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
|
||||||
ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
|
Ok(())
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(_) => err!("Deleting attachment failed")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,9 +62,7 @@ fn post_folders(data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, ws
|
||||||
|
|
||||||
let mut folder = Folder::new(headers.user.uuid.clone(), data.Name);
|
let mut folder = Folder::new(headers.user.uuid.clone(), data.Name);
|
||||||
|
|
||||||
if folder.save(&conn).is_err() {
|
folder.save(&conn)?;
|
||||||
err!("Failed to save folder")
|
|
||||||
}
|
|
||||||
ws.send_folder_update(UpdateType::SyncFolderCreate, &folder);
|
ws.send_folder_update(UpdateType::SyncFolderCreate, &folder);
|
||||||
|
|
||||||
Ok(Json(folder.to_json()))
|
Ok(Json(folder.to_json()))
|
||||||
|
@ -90,9 +88,7 @@ fn put_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn
|
||||||
|
|
||||||
folder.name = data.Name;
|
folder.name = data.Name;
|
||||||
|
|
||||||
if folder.save(&conn).is_err() {
|
folder.save(&conn)?;
|
||||||
err!("Failed to save folder")
|
|
||||||
}
|
|
||||||
ws.send_folder_update(UpdateType::SyncFolderUpdate, &folder);
|
ws.send_folder_update(UpdateType::SyncFolderUpdate, &folder);
|
||||||
|
|
||||||
Ok(Json(folder.to_json()))
|
Ok(Json(folder.to_json()))
|
||||||
|
@ -115,11 +111,8 @@ fn delete_folder(uuid: String, headers: Headers, conn: DbConn, ws: State<WebSock
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the actual folder entry
|
// Delete the actual folder entry
|
||||||
match folder.delete(&conn) {
|
folder.delete(&conn)?;
|
||||||
Ok(()) => {
|
|
||||||
ws.send_folder_update(UpdateType::SyncFolderDelete, &folder);
|
ws.send_folder_update(UpdateType::SyncFolderDelete, &folder);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
|
||||||
Err(_) => err!("Failed deleting folder")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,10 +120,9 @@ fn post_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: Db
|
||||||
user.excluded_globals = to_string(&excluded_globals).unwrap_or("[]".to_string());
|
user.excluded_globals = to_string(&excluded_globals).unwrap_or("[]".to_string());
|
||||||
user.equivalent_domains = to_string(&equivalent_domains).unwrap_or("[]".to_string());
|
user.equivalent_domains = to_string(&equivalent_domains).unwrap_or("[]".to_string());
|
||||||
|
|
||||||
match user.save(&conn) {
|
user.save(&conn)?;
|
||||||
Ok(()) => Ok(Json(json!({}))),
|
|
||||||
Err(_) => err!("Failed to save user"),
|
Ok(Json(json!({})))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[put("/settings/domains", data = "<data>")]
|
#[put("/settings/domains", data = "<data>")]
|
||||||
|
|
|
@ -93,16 +93,9 @@ fn create_organization(headers: Headers, data: JsonUpcase<OrgData>, conn: DbConn
|
||||||
user_org.type_ = UserOrgType::Owner as i32;
|
user_org.type_ = UserOrgType::Owner as i32;
|
||||||
user_org.status = UserOrgStatus::Confirmed as i32;
|
user_org.status = UserOrgStatus::Confirmed as i32;
|
||||||
|
|
||||||
if org.save(&conn).is_err() {
|
org.save(&conn)?;
|
||||||
err!("Failed creating organization")
|
user_org.save(&conn)?;
|
||||||
}
|
collection.save(&conn)?;
|
||||||
if user_org.save(&conn).is_err() {
|
|
||||||
err!("Failed to add user to organization")
|
|
||||||
}
|
|
||||||
|
|
||||||
if collection.save(&conn).is_err() {
|
|
||||||
err!("Failed creating Collection");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Json(org.to_json()))
|
Ok(Json(org.to_json()))
|
||||||
}
|
}
|
||||||
|
@ -118,10 +111,7 @@ fn delete_organization(org_id: String, data: JsonUpcase<PasswordData>, headers:
|
||||||
|
|
||||||
match Organization::find_by_uuid(&org_id, &conn) {
|
match Organization::find_by_uuid(&org_id, &conn) {
|
||||||
None => err!("Organization not found"),
|
None => err!("Organization not found"),
|
||||||
Some(org) => match org.delete(&conn) {
|
Some(org) => org.delete(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed deleting the organization")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,10 +135,7 @@ fn leave_organization(org_id: String, headers: Headers, conn: DbConn) -> EmptyRe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match user_org.delete(&conn) {
|
user_org.delete(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed leaving the organization")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,10 +165,8 @@ fn post_organization(org_id: String, _headers: OwnerHeaders, data: JsonUpcase<Or
|
||||||
org.name = data.Name;
|
org.name = data.Name;
|
||||||
org.billing_email = data.BillingEmail;
|
org.billing_email = data.BillingEmail;
|
||||||
|
|
||||||
match org.save(&conn) {
|
org.save(&conn)?;
|
||||||
Ok(()) => Ok(Json(org.to_json())),
|
Ok(Json(org.to_json()))
|
||||||
Err(_) => err!("Failed to modify organization")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET /api/collections?writeOnly=false
|
// GET /api/collections?writeOnly=false
|
||||||
|
@ -222,10 +207,7 @@ fn post_organization_collections(org_id: String, _headers: AdminHeaders, data: J
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut collection = Collection::new(org.uuid.clone(), data.Name);
|
let mut collection = Collection::new(org.uuid.clone(), data.Name);
|
||||||
|
collection.save(&conn)?;
|
||||||
if collection.save(&conn).is_err() {
|
|
||||||
err!("Failed saving Collection");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Json(collection.to_json()))
|
Ok(Json(collection.to_json()))
|
||||||
}
|
}
|
||||||
|
@ -254,9 +236,7 @@ fn post_organization_collection_update(org_id: String, col_id: String, _headers:
|
||||||
}
|
}
|
||||||
|
|
||||||
collection.name = data.Name.clone();
|
collection.name = data.Name.clone();
|
||||||
if collection.save(&conn).is_err() {
|
collection.save(&conn)?;
|
||||||
err!("Failed updating Collection");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Json(collection.to_json()))
|
Ok(Json(collection.to_json()))
|
||||||
}
|
}
|
||||||
|
@ -279,10 +259,7 @@ fn delete_organization_collection_user(org_id: String, col_id: String, org_user_
|
||||||
match CollectionUser::find_by_collection_and_user(&collection.uuid, &user_org.user_uuid, &conn) {
|
match CollectionUser::find_by_collection_and_user(&collection.uuid, &user_org.user_uuid, &conn) {
|
||||||
None => err!("User not assigned to collection"),
|
None => err!("User not assigned to collection"),
|
||||||
Some(col_user) => {
|
Some(col_user) => {
|
||||||
match col_user.delete(&conn) {
|
col_user.delete(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed removing user from collection")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -299,10 +276,7 @@ fn delete_organization_collection(org_id: String, col_id: String, _headers: Admi
|
||||||
match Collection::find_by_uuid(&col_id, &conn) {
|
match Collection::find_by_uuid(&col_id, &conn) {
|
||||||
None => err!("Collection not found"),
|
None => err!("Collection not found"),
|
||||||
Some(collection) => if collection.org_uuid == org_id {
|
Some(collection) => if collection.org_uuid == org_id {
|
||||||
match collection.delete(&conn) {
|
collection.delete(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed deleting collection")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
err!("Collection and Organization id do not match")
|
err!("Collection and Organization id do not match")
|
||||||
}
|
}
|
||||||
|
@ -435,18 +409,11 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
|
||||||
let user = match User::find_by_mail(&email, &conn) {
|
let user = match User::find_by_mail(&email, &conn) {
|
||||||
None => if CONFIG.invitations_allowed { // Invite user if that's enabled
|
None => if CONFIG.invitations_allowed { // Invite user if that's enabled
|
||||||
let mut invitation = Invitation::new(email.clone());
|
let mut invitation = Invitation::new(email.clone());
|
||||||
match invitation.save(&conn) {
|
invitation.save(&conn)?;
|
||||||
Ok(()) => {
|
let mut user = User::new(email.clone());
|
||||||
let mut user = User::new(email.clone());
|
user.save(&conn)?;
|
||||||
if user.save(&conn).is_err() {
|
user_org_status = UserOrgStatus::Invited as i32;
|
||||||
err!("Failed to create placeholder for invited user")
|
user
|
||||||
} else {
|
|
||||||
user_org_status = UserOrgStatus::Invited as i32;
|
|
||||||
user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => err!(format!("Failed to invite: {}", email))
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
err!(format!("User email does not exist: {}", email))
|
err!(format!("User email does not exist: {}", email))
|
||||||
|
@ -474,17 +441,13 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
|
||||||
match Collection::find_by_uuid_and_org(&col.Id, &org_id, &conn) {
|
match Collection::find_by_uuid_and_org(&col.Id, &org_id, &conn) {
|
||||||
None => err!("Collection not found in Organization"),
|
None => err!("Collection not found in Organization"),
|
||||||
Some(collection) => {
|
Some(collection) => {
|
||||||
if CollectionUser::save(&user.uuid, &collection.uuid, col.ReadOnly, &conn).is_err() {
|
CollectionUser::save(&user.uuid, &collection.uuid, col.ReadOnly, &conn)?;
|
||||||
err!("Failed saving collection access for user")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if new_user.save(&conn).is_err() {
|
new_user.save(&conn)?;
|
||||||
err!("Failed to add user to organization")
|
|
||||||
}
|
|
||||||
org_user_id = Some(new_user.uuid.clone());
|
org_user_id = Some(new_user.uuid.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,10 +590,7 @@ fn confirm_invite(org_id: String, org_user_id: String, data: JsonUpcase<Value>,
|
||||||
None => err!("Invalid key provided")
|
None => err!("Invalid key provided")
|
||||||
};
|
};
|
||||||
|
|
||||||
match user_to_confirm.save(&conn) {
|
user_to_confirm.save(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed to add user to organization")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/organizations/<org_id>/users/<org_user_id>")]
|
#[get("/organizations/<org_id>/users/<org_user_id>")]
|
||||||
|
@ -702,9 +662,7 @@ fn edit_user(org_id: String, org_user_id: String, data: JsonUpcase<EditUserData>
|
||||||
|
|
||||||
// Delete all the odd collections
|
// Delete all the odd collections
|
||||||
for c in CollectionUser::find_by_organization_and_user_uuid(&org_id, &user_to_edit.user_uuid, &conn) {
|
for c in CollectionUser::find_by_organization_and_user_uuid(&org_id, &user_to_edit.user_uuid, &conn) {
|
||||||
if c.delete(&conn).is_err() {
|
c.delete(&conn)?;
|
||||||
err!("Failed deleting old collection assignment")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no accessAll, add the collections received
|
// If no accessAll, add the collections received
|
||||||
|
@ -713,18 +671,13 @@ fn edit_user(org_id: String, org_user_id: String, data: JsonUpcase<EditUserData>
|
||||||
match Collection::find_by_uuid_and_org(&col.Id, &org_id, &conn) {
|
match Collection::find_by_uuid_and_org(&col.Id, &org_id, &conn) {
|
||||||
None => err!("Collection not found in Organization"),
|
None => err!("Collection not found in Organization"),
|
||||||
Some(collection) => {
|
Some(collection) => {
|
||||||
if CollectionUser::save(&user_to_edit.user_uuid, &collection.uuid, col.ReadOnly, &conn).is_err() {
|
CollectionUser::save(&user_to_edit.user_uuid, &collection.uuid, col.ReadOnly, &conn)?;
|
||||||
err!("Failed saving collection access for user")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match user_to_edit.save(&conn) {
|
user_to_edit.save(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed to save user data")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[delete("/organizations/<org_id>/users/<org_user_id>")]
|
#[delete("/organizations/<org_id>/users/<org_user_id>")]
|
||||||
|
@ -736,10 +689,7 @@ fn delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn:
|
||||||
if user_to_delete.uuid == headers.user.uuid {
|
if user_to_delete.uuid == headers.user.uuid {
|
||||||
err!("Delete your account in the account settings")
|
err!("Delete your account in the account settings")
|
||||||
} else {
|
} else {
|
||||||
match user_to_delete.delete(&conn) {
|
user_to_delete.delete(&conn)?;
|
||||||
Ok(()) => return Ok(()),
|
|
||||||
Err(_) => err!("Failed to delete user - likely because it's the only owner of organization")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => err!("User not found")
|
None => err!("User not found")
|
||||||
|
@ -767,10 +717,7 @@ fn delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match user_to_delete.delete(&conn) {
|
user_to_delete.delete(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed deleting user from organization")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/organizations/<org_id>/users/<org_user_id>/delete")]
|
#[post("/organizations/<org_id>/users/<org_user_id>/delete")]
|
||||||
|
@ -844,15 +791,9 @@ fn post_org_import(query: Form<OrgIdData>, data: JsonUpcase<ImportData>, headers
|
||||||
Err(_) => err!("Failed to assign to collection")
|
Err(_) => err!("Failed to assign to collection")
|
||||||
};
|
};
|
||||||
|
|
||||||
match CollectionCipher::save(cipher_id, coll_id, &conn) {
|
CollectionCipher::save(cipher_id, coll_id, &conn)?;
|
||||||
Ok(()) => (),
|
|
||||||
Err(_) => err!("Failed to add cipher to collection")
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut user = headers.user;
|
let mut user = headers.user;
|
||||||
match user.update_revision(&conn) {
|
user.update_revision(&conn)
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => err!("Failed to update the revision, please log out and log back in to finish import.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::db::{
|
||||||
|
|
||||||
use crate::crypto;
|
use crate::crypto;
|
||||||
|
|
||||||
use crate::api::{ApiResult, JsonResult, JsonUpcase, NumberOrString, PasswordData};
|
use crate::api::{ApiResult, EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData};
|
||||||
use crate::auth::Headers;
|
use crate::auth::Headers;
|
||||||
|
|
||||||
use rocket::Route;
|
use rocket::Route;
|
||||||
|
@ -99,10 +99,8 @@ fn recover(data: JsonUpcase<RecoverTwoFactor>, conn: DbConn) -> JsonResult {
|
||||||
|
|
||||||
// Remove the recovery code, not needed without twofactors
|
// Remove the recovery code, not needed without twofactors
|
||||||
user.totp_recover = None;
|
user.totp_recover = None;
|
||||||
match user.save(&conn) {
|
user.save(&conn)?;
|
||||||
Ok(()) => Ok(Json(json!({}))),
|
Ok(Json(json!({})))
|
||||||
Err(_) => err!("Failed to remove the user's two factor recovery code")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -242,9 +240,7 @@ fn _generate_recover_code(user: &mut User, conn: &DbConn) {
|
||||||
if user.totp_recover.is_none() {
|
if user.totp_recover.is_none() {
|
||||||
let totp_recover = BASE32.encode(&crypto::get_random(vec![0u8; 20]));
|
let totp_recover = BASE32.encode(&crypto::get_random(vec![0u8; 20]));
|
||||||
user.totp_recover = Some(totp_recover);
|
user.totp_recover = Some(totp_recover);
|
||||||
if user.save(conn).is_err() {
|
user.save(conn).ok();
|
||||||
error!("Failed to save the user's two factor recovery code")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,15 +345,11 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(tf_challenge) = tf_challenge {
|
if let Some(tf_challenge) = tf_challenge {
|
||||||
let challenge: Challenge = serde_json::from_str(&tf_challenge.data)
|
let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
|
||||||
.expect("Can't parse U2fRegisterChallenge data");
|
|
||||||
|
|
||||||
tf_challenge
|
tf_challenge.delete(&conn)?;
|
||||||
.delete(&conn)
|
|
||||||
.expect("Error deleting U2F register challenge");
|
|
||||||
|
|
||||||
let response_copy: RegisterResponseCopy =
|
let response_copy: RegisterResponseCopy = serde_json::from_str(&data.DeviceResponse)?;
|
||||||
serde_json::from_str(&data.DeviceResponse).expect("Can't parse RegisterResponse data");
|
|
||||||
|
|
||||||
let error_code = response_copy
|
let error_code = response_copy
|
||||||
.error_code
|
.error_code
|
||||||
|
@ -370,40 +362,31 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
|
||||||
|
|
||||||
let response = response_copy.into_response(challenge.challenge.clone());
|
let response = response_copy.into_response(challenge.challenge.clone());
|
||||||
|
|
||||||
match U2F.register_response(challenge.clone(), response) {
|
let registration = U2F.register_response(challenge.clone(), response)?;
|
||||||
Ok(registration) => {
|
// TODO: Allow more than one U2F device
|
||||||
// TODO: Allow more than one U2F device
|
let mut registrations = Vec::new();
|
||||||
let mut registrations = Vec::new();
|
registrations.push(registration);
|
||||||
registrations.push(registration);
|
|
||||||
|
|
||||||
let tf_registration = TwoFactor::new(
|
let tf_registration = TwoFactor::new(
|
||||||
headers.user.uuid.clone(),
|
headers.user.uuid.clone(),
|
||||||
TwoFactorType::U2f,
|
TwoFactorType::U2f,
|
||||||
serde_json::to_string(®istrations).unwrap(),
|
serde_json::to_string(®istrations).unwrap(),
|
||||||
);
|
);
|
||||||
tf_registration
|
tf_registration.save(&conn)?;
|
||||||
.save(&conn)
|
|
||||||
.expect("Error saving U2F registration");
|
|
||||||
|
|
||||||
let mut user = headers.user;
|
let mut user = headers.user;
|
||||||
_generate_recover_code(&mut user, &conn);
|
_generate_recover_code(&mut user, &conn);
|
||||||
|
|
||||||
Ok(Json(json!({
|
Ok(Json(json!({
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"Challenge": {
|
"Challenge": {
|
||||||
"UserId": user.uuid,
|
"UserId": user.uuid,
|
||||||
"AppId": APP_ID.to_string(),
|
"AppId": APP_ID.to_string(),
|
||||||
"Challenge": challenge,
|
"Challenge": challenge,
|
||||||
"Version": U2F_VERSION,
|
"Version": U2F_VERSION,
|
||||||
},
|
},
|
||||||
"Object": "twoFactorU2f"
|
"Object": "twoFactorU2f"
|
||||||
})))
|
})))
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("{:#?}", e);
|
|
||||||
err!("Error activating u2f")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
err!("Can't recover challenge")
|
err!("Can't recover challenge")
|
||||||
}
|
}
|
||||||
|
@ -469,7 +452,7 @@ pub fn generate_u2f_login(user_uuid: &str, conn: &DbConn) -> ApiResult<U2fSignRe
|
||||||
Ok(signed_request)
|
Ok(signed_request)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> ApiResult<()> {
|
pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult {
|
||||||
let challenge_type = TwoFactorType::U2fLoginChallenge as i32;
|
let challenge_type = TwoFactorType::U2fLoginChallenge as i32;
|
||||||
let u2f_type = TwoFactorType::U2f as i32;
|
let u2f_type = TwoFactorType::U2f as i32;
|
||||||
|
|
||||||
|
@ -477,11 +460,8 @@ pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> Api
|
||||||
|
|
||||||
let challenge = match tf_challenge {
|
let challenge = match tf_challenge {
|
||||||
Some(tf_challenge) => {
|
Some(tf_challenge) => {
|
||||||
let challenge: Challenge = serde_json::from_str(&tf_challenge.data)
|
let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
|
||||||
.expect("Can't parse U2fLoginChallenge data");
|
tf_challenge.delete(&conn)?;
|
||||||
tf_challenge
|
|
||||||
.delete(&conn)
|
|
||||||
.expect("Error deleting U2F login challenge");
|
|
||||||
challenge
|
challenge
|
||||||
}
|
}
|
||||||
None => err!("Can't recover login challenge"),
|
None => err!("Can't recover login challenge"),
|
||||||
|
@ -494,8 +474,7 @@ pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> Api
|
||||||
|
|
||||||
let registrations = _parse_registrations(&twofactor.data);
|
let registrations = _parse_registrations(&twofactor.data);
|
||||||
|
|
||||||
let response: SignResponse =
|
let response: SignResponse = serde_json::from_str(response)?;
|
||||||
serde_json::from_str(response).expect("Can't parse SignResponse data");
|
|
||||||
|
|
||||||
let mut _counter: u32 = 0;
|
let mut _counter: u32 = 0;
|
||||||
for registration in registrations {
|
for registration in registrations {
|
||||||
|
@ -614,8 +593,7 @@ fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbCo
|
||||||
let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &conn);
|
let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &conn);
|
||||||
|
|
||||||
if let Some(r) = r {
|
if let Some(r) = r {
|
||||||
let yubikey_metadata: YubikeyMetadata =
|
let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?;
|
||||||
serde_json::from_str(&r.data).expect("Can't parse YubikeyMetadata data");
|
|
||||||
|
|
||||||
let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
|
let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
|
||||||
|
|
||||||
|
@ -648,7 +626,7 @@ fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn:
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(yubikey_data) = yubikey_data {
|
if let Some(yubikey_data) = yubikey_data {
|
||||||
yubikey_data.delete(&conn).expect("Error deleting current Yubikeys");
|
yubikey_data.delete(&conn)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let yubikeys = parse_yubikeys(&data);
|
let yubikeys = parse_yubikeys(&data);
|
||||||
|
@ -686,8 +664,7 @@ fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn:
|
||||||
TwoFactorType::YubiKey,
|
TwoFactorType::YubiKey,
|
||||||
serde_json::to_string(&yubikey_metadata).unwrap(),
|
serde_json::to_string(&yubikey_metadata).unwrap(),
|
||||||
);
|
);
|
||||||
yubikey_registration
|
yubikey_registration.save(&conn)?;
|
||||||
.save(&conn).expect("Failed to save Yubikey info");
|
|
||||||
|
|
||||||
let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
|
let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
|
||||||
|
|
||||||
|
@ -703,7 +680,7 @@ fn activate_yubikey_put(data: JsonUpcase<EnableYubikeyData>, headers: Headers, c
|
||||||
activate_yubikey(data, headers, conn)
|
activate_yubikey(data, headers, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_yubikey_login(user_uuid: &str, response: &str, conn: &DbConn) -> ApiResult<()> {
|
pub fn validate_yubikey_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult {
|
||||||
if response.len() != 44 {
|
if response.len() != 44 {
|
||||||
err!("Invalid Yubikey OTP length");
|
err!("Invalid Yubikey OTP length");
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,17 +61,16 @@ fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult {
|
||||||
let orgs = UserOrganization::find_by_user(&user.uuid, &conn);
|
let orgs = UserOrganization::find_by_user(&user.uuid, &conn);
|
||||||
|
|
||||||
let (access_token, expires_in) = device.refresh_tokens(&user, orgs);
|
let (access_token, expires_in) = device.refresh_tokens(&user, orgs);
|
||||||
match device.save(&conn) {
|
|
||||||
Ok(()) => Ok(Json(json!({
|
device.save(&conn)?;
|
||||||
"access_token": access_token,
|
Ok(Json(json!({
|
||||||
"expires_in": expires_in,
|
"access_token": access_token,
|
||||||
"token_type": "Bearer",
|
"expires_in": expires_in,
|
||||||
"refresh_token": device.refresh_token,
|
"token_type": "Bearer",
|
||||||
"Key": user.key,
|
"refresh_token": device.refresh_token,
|
||||||
"PrivateKey": user.private_key,
|
"Key": user.key,
|
||||||
}))),
|
"PrivateKey": user.private_key,
|
||||||
Err(e) => err!("Failed to add device to user", e),
|
})))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult {
|
fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult {
|
||||||
|
@ -85,19 +84,19 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult
|
||||||
let username = data.username.as_ref().unwrap();
|
let username = data.username.as_ref().unwrap();
|
||||||
let user = match User::find_by_mail(username, &conn) {
|
let user = match User::find_by_mail(username, &conn) {
|
||||||
Some(user) => user,
|
Some(user) => user,
|
||||||
None => err!(format!(
|
None => err!(
|
||||||
"Username or password is incorrect. Try again. IP: {}. Username: {}.",
|
"Username or password is incorrect. Try again",
|
||||||
ip.ip, username
|
format!("IP: {}. Username: {}.", ip.ip, username)
|
||||||
)),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check password
|
// Check password
|
||||||
let password = data.password.as_ref().unwrap();
|
let password = data.password.as_ref().unwrap();
|
||||||
if !user.check_valid_password(password) {
|
if !user.check_valid_password(password) {
|
||||||
err!(format!(
|
err!(
|
||||||
"Username or password is incorrect. Try again. IP: {}. Username: {}.",
|
"Username or password is incorrect. Try again",
|
||||||
ip.ip, username
|
format!("IP: {}. Username: {}.", ip.ip, username)
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// On iOS, device_type sends "iOS", on others it sends a number
|
// On iOS, device_type sends "iOS", on others it sends a number
|
||||||
|
@ -126,9 +125,7 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult
|
||||||
let orgs = UserOrganization::find_by_user(&user.uuid, &conn);
|
let orgs = UserOrganization::find_by_user(&user.uuid, &conn);
|
||||||
|
|
||||||
let (access_token, expires_in) = device.refresh_tokens(&user, orgs);
|
let (access_token, expires_in) = device.refresh_tokens(&user, orgs);
|
||||||
if let Err(e) = device.save(&conn) {
|
device.save(&conn)?;
|
||||||
err!("Failed to add device to user", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut result = json!({
|
let mut result = json!({
|
||||||
"access_token": access_token,
|
"access_token": access_token,
|
||||||
|
|
|
@ -13,14 +13,13 @@ pub use self::web::routes as web_routes;
|
||||||
pub use self::notifications::routes as notifications_routes;
|
pub use self::notifications::routes as notifications_routes;
|
||||||
pub use self::notifications::{start_notification_server, WebSocketUsers, UpdateType};
|
pub use self::notifications::{start_notification_server, WebSocketUsers, UpdateType};
|
||||||
|
|
||||||
use rocket::response::status::BadRequest;
|
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
// Type aliases for API methods results
|
// Type aliases for API methods results
|
||||||
type ApiResult<T> = Result<T, BadRequest<Json<Value>>>;
|
type ApiResult<T> = Result<T, crate::error::Error>;
|
||||||
type JsonResult = ApiResult<Json<Value>>;
|
pub type JsonResult = ApiResult<Json<Value>>;
|
||||||
type EmptyResult = ApiResult<()>;
|
pub type EmptyResult = ApiResult<()>;
|
||||||
|
|
||||||
use crate::util;
|
use crate::util;
|
||||||
type JsonUpcase<T> = Json<util::UpCase<T>>;
|
type JsonUpcase<T> = Json<util::UpCase<T>>;
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub struct Attachment {
|
||||||
pub cipher_uuid: String,
|
pub cipher_uuid: String,
|
||||||
pub file_name: String,
|
pub file_name: String,
|
||||||
pub file_size: i32,
|
pub file_size: i32,
|
||||||
pub key: Option<String>
|
pub key: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Local methods
|
/// Local methods
|
||||||
|
@ -23,7 +23,7 @@ impl Attachment {
|
||||||
cipher_uuid,
|
cipher_uuid,
|
||||||
file_name,
|
file_name,
|
||||||
file_size,
|
file_size,
|
||||||
key: None
|
key: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,29 +54,31 @@ use diesel::prelude::*;
|
||||||
use crate::db::DbConn;
|
use crate::db::DbConn;
|
||||||
use crate::db::schema::attachments;
|
use crate::db::schema::attachments;
|
||||||
|
|
||||||
|
use crate::api::EmptyResult;
|
||||||
|
use crate::error::MapResult;
|
||||||
|
|
||||||
/// Database methods
|
/// Database methods
|
||||||
impl Attachment {
|
impl Attachment {
|
||||||
pub fn save(&self, conn: &DbConn) -> QueryResult<()> {
|
pub fn save(&self, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::replace_into(attachments::table)
|
diesel::replace_into(attachments::table)
|
||||||
.values(self)
|
.values(self)
|
||||||
.execute(&**conn)
|
.execute(&**conn)
|
||||||
.and(Ok(()))
|
.map_res("Error saving attachment")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||||
crate::util::retry(
|
crate::util::retry(
|
||||||
|| {
|
|| diesel::delete(attachments::table.filter(attachments::id.eq(&self.id)))
|
||||||
diesel::delete(attachments::table.filter(attachments::id.eq(&self.id)))
|
.execute(&**conn),
|
||||||
.execute(&**conn)
|
|
||||||
},
|
|
||||||
10,
|
10,
|
||||||
)?;
|
)
|
||||||
|
.map_res("Error deleting attachment")?;
|
||||||
|
|
||||||
crate::util::delete_file(&self.get_file_path());
|
crate::util::delete_file(&self.get_file_path());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
for attachment in Attachment::find_by_cipher(&cipher_uuid, &conn) {
|
for attachment in Attachment::find_by_cipher(&cipher_uuid, &conn) {
|
||||||
attachment.delete(&conn)?;
|
attachment.delete(&conn)?;
|
||||||
}
|
}
|
||||||
|
@ -84,20 +86,20 @@ impl Attachment {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_id(id: &str, conn: &DbConn) -> Option<Self> {
|
pub fn find_by_id(id: &str, conn: &DbConn) -> Option<Self> {
|
||||||
attachments::table
|
attachments::table.filter(attachments::id.eq(id)).first::<Self>(&**conn).ok()
|
||||||
.filter(attachments::id.eq(id))
|
|
||||||
.first::<Self>(&**conn).ok()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_cipher(cipher_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
pub fn find_by_cipher(cipher_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
||||||
attachments::table
|
attachments::table
|
||||||
.filter(attachments::cipher_uuid.eq(cipher_uuid))
|
.filter(attachments::cipher_uuid.eq(cipher_uuid))
|
||||||
.load::<Self>(&**conn).expect("Error loading attachments")
|
.load::<Self>(&**conn)
|
||||||
|
.expect("Error loading attachments")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_ciphers(cipher_uuids: Vec<String>, conn: &DbConn) -> Vec<Self> {
|
pub fn find_by_ciphers(cipher_uuids: Vec<String>, conn: &DbConn) -> Vec<Self> {
|
||||||
attachments::table
|
attachments::table
|
||||||
.filter(attachments::cipher_uuid.eq_any(cipher_uuids))
|
.filter(attachments::cipher_uuid.eq_any(cipher_uuids))
|
||||||
.load::<Self>(&**conn).expect("Error loading attachments")
|
.load::<Self>(&**conn)
|
||||||
|
.expect("Error loading attachments")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use chrono::{NaiveDateTime, Utc};
|
use chrono::{NaiveDateTime, Utc};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use super::{User, Organization, Attachment, FolderCipher, CollectionCipher, UserOrganization, UserOrgType, UserOrgStatus};
|
use super::{Attachment, CollectionCipher, FolderCipher, Organization, User, UserOrgStatus, UserOrgType, UserOrganization};
|
||||||
|
|
||||||
#[derive(Debug, Identifiable, Queryable, Insertable, Associations)]
|
#[derive(Debug, Identifiable, Queryable, Insertable, Associations)]
|
||||||
#[table_name = "ciphers"]
|
#[table_name = "ciphers"]
|
||||||
|
@ -59,17 +59,20 @@ impl Cipher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::db::schema::*;
|
||||||
|
use crate::db::DbConn;
|
||||||
use diesel;
|
use diesel;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use crate::db::DbConn;
|
|
||||||
use crate::db::schema::*;
|
use crate::api::EmptyResult;
|
||||||
|
use crate::error::MapResult;
|
||||||
|
|
||||||
/// Database methods
|
/// Database methods
|
||||||
impl Cipher {
|
impl Cipher {
|
||||||
pub fn to_json(&self, host: &str, user_uuid: &str, conn: &DbConn) -> Value {
|
pub fn to_json(&self, host: &str, user_uuid: &str, conn: &DbConn) -> Value {
|
||||||
use serde_json;
|
|
||||||
use crate::util::format_date;
|
|
||||||
use super::Attachment;
|
use super::Attachment;
|
||||||
|
use crate::util::format_date;
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
let attachments = Attachment::find_by_cipher(&self.uuid, conn);
|
let attachments = Attachment::find_by_cipher(&self.uuid, conn);
|
||||||
let attachments_json: Vec<Value> = attachments.iter().map(|c| c.to_json(host)).collect();
|
let attachments_json: Vec<Value> = attachments.iter().map(|c| c.to_json(host)).collect();
|
||||||
|
@ -149,56 +152,54 @@ impl Cipher {
|
||||||
user_uuids
|
user_uuids
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
|
pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
|
||||||
self.update_users_revision(conn);
|
self.update_users_revision(conn);
|
||||||
self.updated_at = Utc::now().naive_utc();
|
self.updated_at = Utc::now().naive_utc();
|
||||||
|
|
||||||
diesel::replace_into(ciphers::table)
|
diesel::replace_into(ciphers::table)
|
||||||
.values(&*self)
|
.values(&*self)
|
||||||
.execute(&**conn)
|
.execute(&**conn)
|
||||||
.and(Ok(()))
|
.map_res("Error saving cipher")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(&self, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete(&self, conn: &DbConn) -> EmptyResult {
|
||||||
self.update_users_revision(conn);
|
self.update_users_revision(conn);
|
||||||
|
|
||||||
FolderCipher::delete_all_by_cipher(&self.uuid, &conn)?;
|
FolderCipher::delete_all_by_cipher(&self.uuid, &conn)?;
|
||||||
CollectionCipher::delete_all_by_cipher(&self.uuid, &conn)?;
|
CollectionCipher::delete_all_by_cipher(&self.uuid, &conn)?;
|
||||||
Attachment::delete_all_by_cipher(&self.uuid, &conn)?;
|
Attachment::delete_all_by_cipher(&self.uuid, &conn)?;
|
||||||
|
|
||||||
diesel::delete(
|
diesel::delete(ciphers::table.filter(ciphers::uuid.eq(&self.uuid)))
|
||||||
ciphers::table.filter(
|
.execute(&**conn)
|
||||||
ciphers::uuid.eq(&self.uuid)
|
.map_res("Error deleting cipher")
|
||||||
)
|
|
||||||
).execute(&**conn).and(Ok(()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
for cipher in Self::find_by_org(org_uuid, &conn) {
|
for cipher in Self::find_by_org(org_uuid, &conn) {
|
||||||
cipher.delete(&conn)?;
|
cipher.delete(&conn)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
for cipher in Self::find_owned_by_user(user_uuid, &conn) {
|
for cipher in Self::find_owned_by_user(user_uuid, &conn) {
|
||||||
cipher.delete(&conn)?;
|
cipher.delete(&conn)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_to_folder(&self, folder_uuid: Option<String>, user_uuid: &str, conn: &DbConn) -> Result<(), &str> {
|
pub fn move_to_folder(&self, folder_uuid: Option<String>, user_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
match self.get_folder_uuid(&user_uuid, &conn) {
|
match self.get_folder_uuid(&user_uuid, &conn) {
|
||||||
None => {
|
None => {
|
||||||
match folder_uuid {
|
match folder_uuid {
|
||||||
Some(new_folder) => {
|
Some(new_folder) => {
|
||||||
self.update_users_revision(conn);
|
self.update_users_revision(conn);
|
||||||
let folder_cipher = FolderCipher::new(&new_folder, &self.uuid);
|
let folder_cipher = FolderCipher::new(&new_folder, &self.uuid);
|
||||||
folder_cipher.save(&conn).or(Err("Couldn't save folder setting"))
|
folder_cipher.save(&conn)
|
||||||
},
|
}
|
||||||
None => Ok(()) //nothing to do
|
None => Ok(()), //nothing to do
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Some(current_folder) => {
|
Some(current_folder) => {
|
||||||
match folder_uuid {
|
match folder_uuid {
|
||||||
Some(new_folder) => {
|
Some(new_folder) => {
|
||||||
|
@ -206,24 +207,17 @@ impl Cipher {
|
||||||
Ok(()) //nothing to do
|
Ok(()) //nothing to do
|
||||||
} else {
|
} else {
|
||||||
self.update_users_revision(conn);
|
self.update_users_revision(conn);
|
||||||
match FolderCipher::find_by_folder_and_cipher(¤t_folder, &self.uuid, &conn) {
|
if let Some(current_folder) = FolderCipher::find_by_folder_and_cipher(¤t_folder, &self.uuid, &conn) {
|
||||||
Some(current_folder) => {
|
current_folder.delete(&conn)?;
|
||||||
current_folder.delete(&conn).or(Err("Failed removing old folder mapping"))
|
}
|
||||||
},
|
FolderCipher::new(&new_folder, &self.uuid).save(&conn)
|
||||||
None => Ok(()) // Weird, but nothing to do
|
|
||||||
}.and_then(
|
|
||||||
|()| FolderCipher::new(&new_folder, &self.uuid)
|
|
||||||
.save(&conn).or(Err("Couldn't save folder setting"))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
None => {
|
None => {
|
||||||
self.update_users_revision(conn);
|
self.update_users_revision(conn);
|
||||||
match FolderCipher::find_by_folder_and_cipher(¤t_folder, &self.uuid, &conn) {
|
match FolderCipher::find_by_folder_and_cipher(¤t_folder, &self.uuid, &conn) {
|
||||||
Some(current_folder) => {
|
Some(current_folder) => current_folder.delete(&conn),
|
||||||
current_folder.delete(&conn).or(Err("Failed removing old folder mapping"))
|
None => err!("Couldn't move from previous folder"),
|
||||||
},
|
|
||||||
None => Err("Couldn't move from previous folder")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,9 +38,12 @@ use diesel::prelude::*;
|
||||||
use crate::db::DbConn;
|
use crate::db::DbConn;
|
||||||
use crate::db::schema::*;
|
use crate::db::schema::*;
|
||||||
|
|
||||||
|
use crate::api::EmptyResult;
|
||||||
|
use crate::error::MapResult;
|
||||||
|
|
||||||
/// Database methods
|
/// Database methods
|
||||||
impl Collection {
|
impl Collection {
|
||||||
pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
|
pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
|
||||||
// Update affected users revision
|
// Update affected users revision
|
||||||
UserOrganization::find_by_collection_and_org(&self.uuid, &self.org_uuid, conn)
|
UserOrganization::find_by_collection_and_org(&self.uuid, &self.org_uuid, conn)
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -51,10 +54,10 @@ impl Collection {
|
||||||
diesel::replace_into(collections::table)
|
diesel::replace_into(collections::table)
|
||||||
.values(&*self)
|
.values(&*self)
|
||||||
.execute(&**conn)
|
.execute(&**conn)
|
||||||
.and(Ok(()))
|
.map_res("Error saving collection")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||||
CollectionCipher::delete_all_by_collection(&self.uuid, &conn)?;
|
CollectionCipher::delete_all_by_collection(&self.uuid, &conn)?;
|
||||||
CollectionUser::delete_all_by_collection(&self.uuid, &conn)?;
|
CollectionUser::delete_all_by_collection(&self.uuid, &conn)?;
|
||||||
|
|
||||||
|
@ -62,10 +65,11 @@ impl Collection {
|
||||||
collections::table.filter(
|
collections::table.filter(
|
||||||
collections::uuid.eq(self.uuid)
|
collections::uuid.eq(self.uuid)
|
||||||
)
|
)
|
||||||
).execute(&**conn).and(Ok(()))
|
).execute(&**conn)
|
||||||
|
.map_res("Error deleting collection")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
for collection in Self::find_by_organization(org_uuid, &conn) {
|
for collection in Self::find_by_organization(org_uuid, &conn) {
|
||||||
collection.delete(&conn)?;
|
collection.delete(&conn)?;
|
||||||
}
|
}
|
||||||
|
@ -185,7 +189,7 @@ impl CollectionUser {
|
||||||
.load::<Self>(&**conn).expect("Error loading users_collections")
|
.load::<Self>(&**conn).expect("Error loading users_collections")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(user_uuid: &str, collection_uuid: &str, read_only:bool, conn: &DbConn) -> QueryResult<()> {
|
pub fn save(user_uuid: &str, collection_uuid: &str, read_only:bool, conn: &DbConn) -> EmptyResult {
|
||||||
User::update_uuid_revision(&user_uuid, conn);
|
User::update_uuid_revision(&user_uuid, conn);
|
||||||
|
|
||||||
diesel::replace_into(users_collections::table)
|
diesel::replace_into(users_collections::table)
|
||||||
|
@ -193,16 +197,18 @@ impl CollectionUser {
|
||||||
users_collections::user_uuid.eq(user_uuid),
|
users_collections::user_uuid.eq(user_uuid),
|
||||||
users_collections::collection_uuid.eq(collection_uuid),
|
users_collections::collection_uuid.eq(collection_uuid),
|
||||||
users_collections::read_only.eq(read_only),
|
users_collections::read_only.eq(read_only),
|
||||||
)).execute(&**conn).and(Ok(()))
|
)).execute(&**conn)
|
||||||
|
.map_res("Error adding user to collection")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||||
User::update_uuid_revision(&self.user_uuid, conn);
|
User::update_uuid_revision(&self.user_uuid, conn);
|
||||||
|
|
||||||
diesel::delete(users_collections::table
|
diesel::delete(users_collections::table
|
||||||
.filter(users_collections::user_uuid.eq(&self.user_uuid))
|
.filter(users_collections::user_uuid.eq(&self.user_uuid))
|
||||||
.filter(users_collections::collection_uuid.eq(&self.collection_uuid)))
|
.filter(users_collections::collection_uuid.eq(&self.collection_uuid)))
|
||||||
.execute(&**conn).and(Ok(()))
|
.execute(&**conn)
|
||||||
|
.map_res("Error removing user from collection")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_collection(collection_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
pub fn find_by_collection(collection_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
||||||
|
@ -220,7 +226,7 @@ impl CollectionUser {
|
||||||
.first::<Self>(&**conn).ok()
|
.first::<Self>(&**conn).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
CollectionUser::find_by_collection(&collection_uuid, conn)
|
CollectionUser::find_by_collection(&collection_uuid, conn)
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|collection| {
|
.for_each(|collection| {
|
||||||
|
@ -229,15 +235,17 @@ impl CollectionUser {
|
||||||
|
|
||||||
diesel::delete(users_collections::table
|
diesel::delete(users_collections::table
|
||||||
.filter(users_collections::collection_uuid.eq(collection_uuid))
|
.filter(users_collections::collection_uuid.eq(collection_uuid))
|
||||||
).execute(&**conn).and(Ok(()))
|
).execute(&**conn)
|
||||||
|
.map_res("Error deleting users from collection")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
User::update_uuid_revision(&user_uuid, conn);
|
User::update_uuid_revision(&user_uuid, conn);
|
||||||
|
|
||||||
diesel::delete(users_collections::table
|
diesel::delete(users_collections::table
|
||||||
.filter(users_collections::user_uuid.eq(user_uuid))
|
.filter(users_collections::user_uuid.eq(user_uuid))
|
||||||
).execute(&**conn).and(Ok(()))
|
).execute(&**conn)
|
||||||
|
.map_res("Error removing user from collections")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,30 +263,34 @@ pub struct CollectionCipher {
|
||||||
|
|
||||||
/// Database methods
|
/// Database methods
|
||||||
impl CollectionCipher {
|
impl CollectionCipher {
|
||||||
pub fn save(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn save(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::replace_into(ciphers_collections::table)
|
diesel::replace_into(ciphers_collections::table)
|
||||||
.values((
|
.values((
|
||||||
ciphers_collections::cipher_uuid.eq(cipher_uuid),
|
ciphers_collections::cipher_uuid.eq(cipher_uuid),
|
||||||
ciphers_collections::collection_uuid.eq(collection_uuid),
|
ciphers_collections::collection_uuid.eq(collection_uuid),
|
||||||
)).execute(&**conn).and(Ok(()))
|
)).execute(&**conn)
|
||||||
|
.map_res("Error adding cipher to collection")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::delete(ciphers_collections::table
|
diesel::delete(ciphers_collections::table
|
||||||
.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid))
|
.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid))
|
||||||
.filter(ciphers_collections::collection_uuid.eq(collection_uuid)))
|
.filter(ciphers_collections::collection_uuid.eq(collection_uuid)))
|
||||||
.execute(&**conn).and(Ok(()))
|
.execute(&**conn)
|
||||||
|
.map_res("Error deleting cipher from collection")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::delete(ciphers_collections::table
|
diesel::delete(ciphers_collections::table
|
||||||
.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid))
|
.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid))
|
||||||
).execute(&**conn).and(Ok(()))
|
).execute(&**conn)
|
||||||
|
.map_res("Error removing cipher from collections")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::delete(ciphers_collections::table
|
diesel::delete(ciphers_collections::table
|
||||||
.filter(ciphers_collections::collection_uuid.eq(collection_uuid))
|
.filter(ciphers_collections::collection_uuid.eq(collection_uuid))
|
||||||
).execute(&**conn).and(Ok(()))
|
).execute(&**conn)
|
||||||
|
.map_res("Error removing ciphers from collection")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -110,9 +110,12 @@ use diesel::prelude::*;
|
||||||
use crate::db::DbConn;
|
use crate::db::DbConn;
|
||||||
use crate::db::schema::devices;
|
use crate::db::schema::devices;
|
||||||
|
|
||||||
|
use crate::api::EmptyResult;
|
||||||
|
use crate::error::MapResult;
|
||||||
|
|
||||||
/// Database methods
|
/// Database methods
|
||||||
impl Device {
|
impl Device {
|
||||||
pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
|
pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
|
||||||
self.updated_at = Utc::now().naive_utc();
|
self.updated_at = Utc::now().naive_utc();
|
||||||
|
|
||||||
crate::util::retry(
|
crate::util::retry(
|
||||||
|
@ -123,16 +126,17 @@ impl Device {
|
||||||
},
|
},
|
||||||
10,
|
10,
|
||||||
)
|
)
|
||||||
.and(Ok(()))
|
.map_res("Error saving device")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::delete(devices::table.filter(
|
diesel::delete(devices::table.filter(
|
||||||
devices::uuid.eq(self.uuid)
|
devices::uuid.eq(self.uuid)
|
||||||
)).execute(&**conn).and(Ok(()))
|
)).execute(&**conn)
|
||||||
|
.map_res("Error removing device")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
for device in Self::find_by_user(user_uuid, &conn) {
|
for device in Self::find_by_user(user_uuid, &conn) {
|
||||||
device.delete(&conn)?;
|
device.delete(&conn)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,17 +66,21 @@ use diesel::prelude::*;
|
||||||
use crate::db::DbConn;
|
use crate::db::DbConn;
|
||||||
use crate::db::schema::{folders, folders_ciphers};
|
use crate::db::schema::{folders, folders_ciphers};
|
||||||
|
|
||||||
|
use crate::api::EmptyResult;
|
||||||
|
use crate::error::MapResult;
|
||||||
|
|
||||||
/// Database methods
|
/// Database methods
|
||||||
impl Folder {
|
impl Folder {
|
||||||
pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
|
pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
|
||||||
User::update_uuid_revision(&self.user_uuid, conn);
|
User::update_uuid_revision(&self.user_uuid, conn);
|
||||||
self.updated_at = Utc::now().naive_utc();
|
self.updated_at = Utc::now().naive_utc();
|
||||||
|
|
||||||
diesel::replace_into(folders::table)
|
diesel::replace_into(folders::table)
|
||||||
.values(&*self).execute(&**conn).and(Ok(()))
|
.values(&*self).execute(&**conn)
|
||||||
|
.map_res("Error saving folder")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(&self, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete(&self, conn: &DbConn) -> EmptyResult {
|
||||||
User::update_uuid_revision(&self.user_uuid, conn);
|
User::update_uuid_revision(&self.user_uuid, conn);
|
||||||
FolderCipher::delete_all_by_folder(&self.uuid, &conn)?;
|
FolderCipher::delete_all_by_folder(&self.uuid, &conn)?;
|
||||||
|
|
||||||
|
@ -84,10 +88,11 @@ impl Folder {
|
||||||
folders::table.filter(
|
folders::table.filter(
|
||||||
folders::uuid.eq(&self.uuid)
|
folders::uuid.eq(&self.uuid)
|
||||||
)
|
)
|
||||||
).execute(&**conn).and(Ok(()))
|
).execute(&**conn)
|
||||||
|
.map_res("Error deleting folder")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
for folder in Self::find_by_user(user_uuid, &conn) {
|
for folder in Self::find_by_user(user_uuid, &conn) {
|
||||||
folder.delete(&conn)?;
|
folder.delete(&conn)?;
|
||||||
}
|
}
|
||||||
|
@ -108,29 +113,33 @@ impl Folder {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FolderCipher {
|
impl FolderCipher {
|
||||||
pub fn save(&self, conn: &DbConn) -> QueryResult<()> {
|
pub fn save(&self, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::replace_into(folders_ciphers::table)
|
diesel::replace_into(folders_ciphers::table)
|
||||||
.values(&*self)
|
.values(&*self)
|
||||||
.execute(&**conn).and(Ok(()))
|
.execute(&**conn)
|
||||||
|
.map_res("Error adding cipher to folder")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::delete(folders_ciphers::table
|
diesel::delete(folders_ciphers::table
|
||||||
.filter(folders_ciphers::cipher_uuid.eq(self.cipher_uuid))
|
.filter(folders_ciphers::cipher_uuid.eq(self.cipher_uuid))
|
||||||
.filter(folders_ciphers::folder_uuid.eq(self.folder_uuid))
|
.filter(folders_ciphers::folder_uuid.eq(self.folder_uuid))
|
||||||
).execute(&**conn).and(Ok(()))
|
).execute(&**conn)
|
||||||
|
.map_res("Error removing cipher from folder")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::delete(folders_ciphers::table
|
diesel::delete(folders_ciphers::table
|
||||||
.filter(folders_ciphers::cipher_uuid.eq(cipher_uuid))
|
.filter(folders_ciphers::cipher_uuid.eq(cipher_uuid))
|
||||||
).execute(&**conn).and(Ok(()))
|
).execute(&**conn)
|
||||||
|
.map_res("Error removing cipher from folders")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_folder(folder_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_folder(folder_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::delete(folders_ciphers::table
|
diesel::delete(folders_ciphers::table
|
||||||
.filter(folders_ciphers::folder_uuid.eq(folder_uuid))
|
.filter(folders_ciphers::folder_uuid.eq(folder_uuid))
|
||||||
).execute(&**conn).and(Ok(()))
|
).execute(&**conn)
|
||||||
|
.map_res("Error removing ciphers from folder")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_folder_and_cipher(folder_uuid: &str, cipher_uuid: &str, conn: &DbConn) -> Option<Self> {
|
pub fn find_by_folder_and_cipher(folder_uuid: &str, cipher_uuid: &str, conn: &DbConn) -> Option<Self> {
|
||||||
|
|
|
@ -238,11 +238,14 @@ use diesel::prelude::*;
|
||||||
use crate::db::DbConn;
|
use crate::db::DbConn;
|
||||||
use crate::db::schema::{organizations, users_organizations, users_collections, ciphers_collections};
|
use crate::db::schema::{organizations, users_organizations, users_collections, ciphers_collections};
|
||||||
|
|
||||||
|
use crate::api::EmptyResult;
|
||||||
|
use crate::error::MapResult;
|
||||||
|
|
||||||
/// Database methods
|
/// Database methods
|
||||||
impl Organization {
|
impl Organization {
|
||||||
pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
|
pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
|
||||||
if self.uuid == Organization::VIRTUAL_ID {
|
if self.uuid == Organization::VIRTUAL_ID {
|
||||||
return Err(diesel::result::Error::NotFound)
|
err!("diesel::result::Error::NotFound")
|
||||||
}
|
}
|
||||||
|
|
||||||
UserOrganization::find_by_org(&self.uuid, conn)
|
UserOrganization::find_by_org(&self.uuid, conn)
|
||||||
|
@ -252,14 +255,15 @@ impl Organization {
|
||||||
});
|
});
|
||||||
|
|
||||||
diesel::replace_into(organizations::table)
|
diesel::replace_into(organizations::table)
|
||||||
.values(&*self).execute(&**conn).and(Ok(()))
|
.values(&*self).execute(&**conn)
|
||||||
|
.map_res("Error saving organization")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||||
use super::{Cipher, Collection};
|
use super::{Cipher, Collection};
|
||||||
|
|
||||||
if self.uuid == Organization::VIRTUAL_ID {
|
if self.uuid == Organization::VIRTUAL_ID {
|
||||||
return Err(diesel::result::Error::NotFound)
|
err!("diesel::result::Error::NotFound")
|
||||||
}
|
}
|
||||||
|
|
||||||
Cipher::delete_all_by_organization(&self.uuid, &conn)?;
|
Cipher::delete_all_by_organization(&self.uuid, &conn)?;
|
||||||
|
@ -270,7 +274,8 @@ impl Organization {
|
||||||
organizations::table.filter(
|
organizations::table.filter(
|
||||||
organizations::uuid.eq(self.uuid)
|
organizations::uuid.eq(self.uuid)
|
||||||
)
|
)
|
||||||
).execute(&**conn).and(Ok(()))
|
).execute(&**conn)
|
||||||
|
.map_res("Error saving organization")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
|
pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
|
||||||
|
@ -365,19 +370,20 @@ impl UserOrganization {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
|
pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
|
||||||
if self.org_uuid == Organization::VIRTUAL_ID {
|
if self.org_uuid == Organization::VIRTUAL_ID {
|
||||||
return Err(diesel::result::Error::NotFound)
|
err!("diesel::result::Error::NotFound")
|
||||||
}
|
}
|
||||||
User::update_uuid_revision(&self.user_uuid, conn);
|
User::update_uuid_revision(&self.user_uuid, conn);
|
||||||
|
|
||||||
diesel::replace_into(users_organizations::table)
|
diesel::replace_into(users_organizations::table)
|
||||||
.values(&*self).execute(&**conn).and(Ok(()))
|
.values(&*self).execute(&**conn)
|
||||||
|
.map_res("Error adding user to organization")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||||
if self.org_uuid == Organization::VIRTUAL_ID {
|
if self.org_uuid == Organization::VIRTUAL_ID {
|
||||||
return Err(diesel::result::Error::NotFound)
|
err!("diesel::result::Error::NotFound")
|
||||||
}
|
}
|
||||||
User::update_uuid_revision(&self.user_uuid, conn);
|
User::update_uuid_revision(&self.user_uuid, conn);
|
||||||
|
|
||||||
|
@ -387,17 +393,18 @@ impl UserOrganization {
|
||||||
users_organizations::table.filter(
|
users_organizations::table.filter(
|
||||||
users_organizations::uuid.eq(self.uuid)
|
users_organizations::uuid.eq(self.uuid)
|
||||||
)
|
)
|
||||||
).execute(&**conn).and(Ok(()))
|
).execute(&**conn)
|
||||||
|
.map_res("Error removing user from organization")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
for user_org in Self::find_by_org(&org_uuid, &conn) {
|
for user_org in Self::find_by_org(&org_uuid, &conn) {
|
||||||
user_org.delete(&conn)?;
|
user_org.delete(&conn)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
for user_org in Self::find_any_state_by_user(&user_uuid, &conn) {
|
for user_org in Self::find_any_state_by_user(&user_uuid, &conn) {
|
||||||
user_org.delete(&conn)?;
|
user_org.delete(&conn)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,20 +79,25 @@ use diesel::prelude::*;
|
||||||
use crate::db::DbConn;
|
use crate::db::DbConn;
|
||||||
use crate::db::schema::twofactor;
|
use crate::db::schema::twofactor;
|
||||||
|
|
||||||
|
use crate::api::EmptyResult;
|
||||||
|
use crate::error::MapResult;
|
||||||
|
|
||||||
/// Database methods
|
/// Database methods
|
||||||
impl TwoFactor {
|
impl TwoFactor {
|
||||||
pub fn save(&self, conn: &DbConn) -> QueryResult<usize> {
|
pub fn save(&self, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::replace_into(twofactor::table)
|
diesel::replace_into(twofactor::table)
|
||||||
.values(self)
|
.values(self)
|
||||||
.execute(&**conn)
|
.execute(&**conn)
|
||||||
|
.map_res("Error saving twofactor")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(self, conn: &DbConn) -> QueryResult<usize> {
|
pub fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::delete(
|
diesel::delete(
|
||||||
twofactor::table.filter(
|
twofactor::table.filter(
|
||||||
twofactor::uuid.eq(self.uuid)
|
twofactor::uuid.eq(self.uuid)
|
||||||
)
|
)
|
||||||
).execute(&**conn)
|
).execute(&**conn)
|
||||||
|
.map_res("Error deleting twofactor")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
||||||
|
@ -108,11 +113,12 @@ impl TwoFactor {
|
||||||
.first::<Self>(&**conn).ok()
|
.first::<Self>(&**conn).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> QueryResult<usize> {
|
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::delete(
|
diesel::delete(
|
||||||
twofactor::table.filter(
|
twofactor::table.filter(
|
||||||
twofactor::user_uuid.eq(user_uuid)
|
twofactor::user_uuid.eq(user_uuid)
|
||||||
)
|
)
|
||||||
).execute(&**conn)
|
).execute(&**conn)
|
||||||
|
.map_res("Error deleting twofactors")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,9 @@ use crate::db::DbConn;
|
||||||
use crate::db::schema::{users, invitations};
|
use crate::db::schema::{users, invitations};
|
||||||
use super::{Cipher, Folder, Device, UserOrganization, UserOrgType, TwoFactor};
|
use super::{Cipher, Folder, Device, UserOrganization, UserOrgType, TwoFactor};
|
||||||
|
|
||||||
|
use crate::api::EmptyResult;
|
||||||
|
use crate::error::MapResult;
|
||||||
|
|
||||||
/// Database methods
|
/// Database methods
|
||||||
impl User {
|
impl User {
|
||||||
pub fn to_json(&self, conn: &DbConn) -> Value {
|
pub fn to_json(&self, conn: &DbConn) -> Value {
|
||||||
|
@ -145,21 +148,22 @@ impl User {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
|
pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
|
||||||
self.updated_at = Utc::now().naive_utc();
|
self.updated_at = Utc::now().naive_utc();
|
||||||
|
|
||||||
diesel::replace_into(users::table) // Insert or update
|
diesel::replace_into(users::table) // Insert or update
|
||||||
.values(&*self).execute(&**conn).and(Ok(()))
|
.values(&*self).execute(&**conn)
|
||||||
|
.map_res("Error saving user")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||||
for user_org in UserOrganization::find_by_user(&self.uuid, &*conn) {
|
for user_org in UserOrganization::find_by_user(&self.uuid, &*conn) {
|
||||||
if user_org.type_ == UserOrgType::Owner {
|
if user_org.type_ == UserOrgType::Owner {
|
||||||
if UserOrganization::find_by_org_and_type(
|
if UserOrganization::find_by_org_and_type(
|
||||||
&user_org.org_uuid,
|
&user_org.org_uuid,
|
||||||
UserOrgType::Owner as i32, &conn
|
UserOrgType::Owner as i32, &conn
|
||||||
).len() <= 1 {
|
).len() <= 1 {
|
||||||
return Err(diesel::result::Error::NotFound);
|
err!("Can't delete last owner")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,12 +172,13 @@ impl User {
|
||||||
Cipher::delete_all_by_user(&self.uuid, &*conn)?;
|
Cipher::delete_all_by_user(&self.uuid, &*conn)?;
|
||||||
Folder::delete_all_by_user(&self.uuid, &*conn)?;
|
Folder::delete_all_by_user(&self.uuid, &*conn)?;
|
||||||
Device::delete_all_by_user(&self.uuid, &*conn)?;
|
Device::delete_all_by_user(&self.uuid, &*conn)?;
|
||||||
TwoFactor::delete_all_by_user(&self.uuid, &*conn)?;
|
//TwoFactor::delete_all_by_user(&self.uuid, &*conn)?;
|
||||||
Invitation::take(&self.email, &*conn); // Delete invitation if any
|
Invitation::take(&self.email, &*conn); // Delete invitation if any
|
||||||
|
|
||||||
diesel::delete(users::table.filter(
|
diesel::delete(users::table.filter(
|
||||||
users::uuid.eq(self.uuid)))
|
users::uuid.eq(self.uuid)))
|
||||||
.execute(&**conn).and(Ok(()))
|
.execute(&**conn)
|
||||||
|
.map_res("Error deleting user")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_uuid_revision(uuid: &str, conn: &DbConn) {
|
pub fn update_uuid_revision(uuid: &str, conn: &DbConn) {
|
||||||
|
@ -184,7 +189,7 @@ impl User {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_revision(&mut self, conn: &DbConn) -> QueryResult<()> {
|
pub fn update_revision(&mut self, conn: &DbConn) -> EmptyResult {
|
||||||
self.updated_at = Utc::now().naive_utc();
|
self.updated_at = Utc::now().naive_utc();
|
||||||
diesel::update(
|
diesel::update(
|
||||||
users::table.filter(
|
users::table.filter(
|
||||||
|
@ -192,7 +197,8 @@ impl User {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.set(users::updated_at.eq(&self.updated_at))
|
.set(users::updated_at.eq(&self.updated_at))
|
||||||
.execute(&**conn).and(Ok(()))
|
.execute(&**conn)
|
||||||
|
.map_res("Error updating user revision")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> {
|
pub fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> {
|
||||||
|
@ -228,18 +234,18 @@ impl Invitation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
|
pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::replace_into(invitations::table)
|
diesel::replace_into(invitations::table)
|
||||||
.values(&*self)
|
.values(&*self)
|
||||||
.execute(&**conn)
|
.execute(&**conn)
|
||||||
.and(Ok(()))
|
.map_res("Error saving invitation")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
|
pub fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||||
diesel::delete(invitations::table.filter(
|
diesel::delete(invitations::table.filter(
|
||||||
invitations::email.eq(self.email)))
|
invitations::email.eq(self.email)))
|
||||||
.execute(&**conn)
|
.execute(&**conn)
|
||||||
.and(Ok(()))
|
.map_res("Error deleting invitation")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> {
|
pub fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> {
|
||||||
|
|
156
src/error.rs
Normale Datei
156
src/error.rs
Normale Datei
|
@ -0,0 +1,156 @@
|
||||||
|
//
|
||||||
|
// Error generator macro
|
||||||
|
//
|
||||||
|
macro_rules! make_error {
|
||||||
|
( $struct:ident; $( $name:ident ( $ty:ty, _): $show_cause:expr, $usr_msg_fun:expr ),+ $(,)* ) => {
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(unused_variables, dead_code)]
|
||||||
|
pub enum $struct {
|
||||||
|
$($name( $ty, String )),+
|
||||||
|
}
|
||||||
|
$(impl From<$ty> for $struct {
|
||||||
|
fn from(err: $ty) -> Self {
|
||||||
|
$struct::$name(err, String::from(stringify!($name)))
|
||||||
|
}
|
||||||
|
})+
|
||||||
|
$(impl From<($ty, String)> for $struct {
|
||||||
|
fn from(err: ($ty, String)) -> Self {
|
||||||
|
$struct::$name(err.0, err.1)
|
||||||
|
}
|
||||||
|
})+
|
||||||
|
impl $struct {
|
||||||
|
pub fn with_msg<M: Into<String>>(self, msg: M) -> Self {
|
||||||
|
match self {$(
|
||||||
|
$struct::$name(e, _) => $struct::$name(e, msg.into()),
|
||||||
|
)+}
|
||||||
|
}
|
||||||
|
// First value is log message, second is user message
|
||||||
|
pub fn display_error(self) -> String {
|
||||||
|
match &self {$(
|
||||||
|
$struct::$name(e, s) => {
|
||||||
|
let log_msg = format!("{}. {}", &s, &e);
|
||||||
|
|
||||||
|
error!("{}", log_msg);
|
||||||
|
if $show_cause {
|
||||||
|
error!("[CAUSE] {:?}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$usr_msg_fun(e, s)
|
||||||
|
},
|
||||||
|
)+}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
use diesel::result::{Error as DieselError, QueryResult};
|
||||||
|
use serde_json::{Value, Error as SerError};
|
||||||
|
use u2f::u2ferror::U2fError as U2fErr;
|
||||||
|
|
||||||
|
// Error struct
|
||||||
|
// Each variant has two elements, the first is an error of different types, used for logging purposes
|
||||||
|
// The second is a String, and it's contents are displayed to the user when the error occurs. Inside the macro, this is represented as _
|
||||||
|
//
|
||||||
|
// After the variant itself, there are two expressions. The first one is a bool to indicate whether the error cause will be printed to the log.
|
||||||
|
// The second one contains the function used to obtain the response sent to the client
|
||||||
|
make_error! {
|
||||||
|
Error;
|
||||||
|
// Used to represent err! calls
|
||||||
|
SimpleError(String, _): false, _api_error,
|
||||||
|
// Used for special return values, like 2FA errors
|
||||||
|
JsonError(Value, _): false, _serialize,
|
||||||
|
DbError(DieselError, _): true, _api_error,
|
||||||
|
U2fError(U2fErr, _): true, _api_error,
|
||||||
|
SerdeError(SerError, _): true, _api_error,
|
||||||
|
//WsError(ws::Error, _): true, _api_error,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn new<M: Into<String>, N: Into<String>>(usr_msg: M, log_msg: N) -> Self {
|
||||||
|
Error::SimpleError(log_msg.into(), usr_msg.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MapResult<S, E> {
|
||||||
|
fn map_res(self, msg: &str) -> Result<(), E>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapResult<(), Error> for QueryResult<usize> {
|
||||||
|
fn map_res(self, msg: &str) -> Result<(), Error> {
|
||||||
|
self.and(Ok(())).map_err(Error::from).map_err(|e| e.with_msg(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
fn _serialize(e: &impl Serialize, _: &impl Any) -> String {
|
||||||
|
serde_json::to_string(e).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _api_error(_: &impl Any, msg: &str) -> String {
|
||||||
|
let json = json!({
|
||||||
|
"Message": "",
|
||||||
|
"error": "",
|
||||||
|
"error_description": "",
|
||||||
|
"ValidationErrors": {"": [ msg ]},
|
||||||
|
"ErrorModel": {
|
||||||
|
"Message": msg,
|
||||||
|
"Object": "error"
|
||||||
|
},
|
||||||
|
"Object": "error"
|
||||||
|
});
|
||||||
|
|
||||||
|
_serialize(&json, &false)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rocket responder impl
|
||||||
|
//
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use rocket::http::{ContentType, Status};
|
||||||
|
use rocket::request::Request;
|
||||||
|
use rocket::response::{self, Responder, Response};
|
||||||
|
|
||||||
|
impl<'r> Responder<'r> for Error {
|
||||||
|
fn respond_to(self, _: &Request) -> response::Result<'r> {
|
||||||
|
// TODO: We could put the security headers here
|
||||||
|
|
||||||
|
let usr_msg = self.display_error();
|
||||||
|
|
||||||
|
Response::build()
|
||||||
|
.status(Status::BadRequest)
|
||||||
|
.header(ContentType::JSON)
|
||||||
|
.sized_body(Cursor::new(usr_msg))
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Error return macros
|
||||||
|
///
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! err {
|
||||||
|
($msg:expr) => {{
|
||||||
|
return Err(crate::error::Error::new($msg, $msg));
|
||||||
|
}};
|
||||||
|
($usr_msg:expr, $log_value:expr) => {{
|
||||||
|
return Err(crate::error::Error::new($usr_msg, $log_value));
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! err_json {
|
||||||
|
($expr:expr) => {{
|
||||||
|
return Err(crate::error::Error::from($expr));
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! err_handler {
|
||||||
|
($expr:expr) => {{
|
||||||
|
return rocket::Outcome::Failure((rocket::http::Status::Unauthorized, $expr));
|
||||||
|
}};
|
||||||
|
}
|
|
@ -7,6 +7,9 @@ use lettre_email::EmailBuilder;
|
||||||
use crate::MailConfig;
|
use crate::MailConfig;
|
||||||
use crate::CONFIG;
|
use crate::CONFIG;
|
||||||
|
|
||||||
|
use crate::api::EmptyResult;
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
fn mailer(config: &MailConfig) -> SmtpTransport {
|
fn mailer(config: &MailConfig) -> SmtpTransport {
|
||||||
let client_security = if config.smtp_ssl {
|
let client_security = if config.smtp_ssl {
|
||||||
let tls = TlsConnector::builder()
|
let tls = TlsConnector::builder()
|
||||||
|
@ -35,7 +38,7 @@ fn mailer(config: &MailConfig) -> SmtpTransport {
|
||||||
.transport()
|
.transport()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_password_hint(address: &str, hint: Option<String>, config: &MailConfig) -> Result<(), String> {
|
pub fn send_password_hint(address: &str, hint: Option<String>, config: &MailConfig) -> EmptyResult {
|
||||||
let (subject, body) = if let Some(hint) = hint {
|
let (subject, body) = if let Some(hint) = hint {
|
||||||
("Your master password hint",
|
("Your master password hint",
|
||||||
format!(
|
format!(
|
||||||
|
@ -54,11 +57,11 @@ pub fn send_password_hint(address: &str, hint: Option<String>, config: &MailConf
|
||||||
.subject(subject)
|
.subject(subject)
|
||||||
.body(body)
|
.body(body)
|
||||||
.build()
|
.build()
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| Error::new("Error building hint email", e.to_string()))?;
|
||||||
|
|
||||||
mailer(config)
|
mailer(config)
|
||||||
.send(email.into())
|
.send(email.into())
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| Error::new("Error sending hint email", e.to_string()))
|
||||||
.and(Ok(()))
|
.and(Ok(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,8 @@
|
||||||
use std::{path::Path, process::{exit, Command}};
|
use std::{path::Path, process::{exit, Command}};
|
||||||
use rocket::Rocket;
|
use rocket::Rocket;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use] mod error;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod db;
|
mod db;
|
||||||
mod crypto;
|
mod crypto;
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
function updateVis() {
|
function updateVis() {
|
||||||
setVis("#no-key-form", !key);
|
setVis("#no-key-form", !key);
|
||||||
setVis("#users-block", key);
|
setVis("#users-block", key);
|
||||||
setVis("#invite-form", key);
|
setVis("#invite-form-block", key);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setKey() {
|
function setKey() {
|
||||||
|
@ -166,7 +166,7 @@
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="invite-form" class="d-none align-items-center p-3 mb-3 text-white-50 bg-secondary rounded shadow">
|
<div id="invite-form-block" class="d-none align-items-center p-3 mb-3 text-white-50 bg-secondary rounded shadow">
|
||||||
<div>
|
<div>
|
||||||
<h6 class="mb-0 text-white">Invite User</h6>
|
<h6 class="mb-0 text-white">Invite User</h6>
|
||||||
<small>Email:</small>
|
<small>Email:</small>
|
||||||
|
|
47
src/util.rs
47
src/util.rs
|
@ -1,50 +1,3 @@
|
||||||
///
|
|
||||||
/// Macros
|
|
||||||
///
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! _err_object {
|
|
||||||
($msg:expr) => {{
|
|
||||||
err_json!(json!({
|
|
||||||
"Message": "",
|
|
||||||
"error": "",
|
|
||||||
"error_description": "",
|
|
||||||
"ValidationErrors": {"": [ $msg ]},
|
|
||||||
"ErrorModel": {
|
|
||||||
"Message": $msg,
|
|
||||||
"Object": "error"
|
|
||||||
},
|
|
||||||
"Object": "error"
|
|
||||||
}))
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! err {
|
|
||||||
($msg:expr) => {{
|
|
||||||
error!("{}", $msg);
|
|
||||||
_err_object!($msg)
|
|
||||||
}};
|
|
||||||
($usr_msg:expr, $log_value:expr) => {{
|
|
||||||
error!("{}: {:#?}", $usr_msg, $log_value);
|
|
||||||
_err_object!($usr_msg)
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! err_json {
|
|
||||||
($expr:expr) => {{
|
|
||||||
return Err(rocket::response::status::BadRequest(Some(rocket_contrib::json::Json($expr))));
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! err_handler {
|
|
||||||
($expr:expr) => {{
|
|
||||||
error!("{}", $expr);
|
|
||||||
return rocket::Outcome::Failure((rocket::http::Status::Unauthorized, $expr));
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// File handling
|
/// File handling
|
||||||
///
|
///
|
||||||
|
|
Laden …
In neuem Issue referenzieren