mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-06-12 21:27:37 +02:00
Start using rustfmt and some style changes to make some lines shorter
This commit is contained in:
@ -3,13 +3,13 @@ use rocket_contrib::json::Json;
|
||||
use crate::db::models::*;
|
||||
use crate::db::DbConn;
|
||||
|
||||
use crate::api::{EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData, UpdateType, WebSocketUsers};
|
||||
use crate::auth::{Headers, decode_invite_jwt, InviteJWTClaims};
|
||||
use crate::api::{EmptyResult, JsonResult, JsonUpcase, Notify, NumberOrString, PasswordData, UpdateType};
|
||||
use crate::auth::{decode_invite_jwt, Headers, InviteJWTClaims};
|
||||
use crate::mail;
|
||||
|
||||
use crate::CONFIG;
|
||||
|
||||
use rocket::{Route, State};
|
||||
use rocket::Route;
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![
|
||||
@ -74,9 +74,9 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
|
||||
} else {
|
||||
let token = match &data.Token {
|
||||
Some(token) => token,
|
||||
None => err!("No valid invite token")
|
||||
None => err!("No valid invite token"),
|
||||
};
|
||||
|
||||
|
||||
let claims: InviteJWTClaims = decode_invite_jwt(&token)?;
|
||||
if &claims.email == &data.Email {
|
||||
user
|
||||
@ -257,7 +257,7 @@ struct KeyData {
|
||||
}
|
||||
|
||||
#[post("/accounts/key", data = "<data>")]
|
||||
fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
let data: KeyData = data.into_inner().data;
|
||||
|
||||
if !headers.user.check_valid_password(&data.MasterPasswordHash) {
|
||||
@ -294,7 +294,15 @@ fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, ws:
|
||||
err!("The cipher is not owned by the user")
|
||||
}
|
||||
|
||||
update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &conn, &ws, UpdateType::SyncCipherUpdate)?
|
||||
update_cipher_from_data(
|
||||
&mut saved_cipher,
|
||||
cipher_data,
|
||||
&headers,
|
||||
false,
|
||||
&conn,
|
||||
&nt,
|
||||
UpdateType::CipherUpdate,
|
||||
)?
|
||||
}
|
||||
|
||||
// Update user data
|
||||
|
@ -1,8 +1,8 @@
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::Path;
|
||||
|
||||
use rocket::http::ContentType;
|
||||
use rocket::{request::Form, Data, Route, State};
|
||||
use rocket::{request::Form, Data, Route};
|
||||
|
||||
use rocket_contrib::json::Json;
|
||||
use serde_json::Value;
|
||||
@ -17,7 +17,7 @@ use crate::db::DbConn;
|
||||
|
||||
use crate::crypto;
|
||||
|
||||
use crate::api::{self, EmptyResult, JsonResult, JsonUpcase, PasswordData, UpdateType, WebSocketUsers};
|
||||
use crate::api::{self, EmptyResult, JsonResult, JsonUpcase, Notify, PasswordData, UpdateType};
|
||||
use crate::auth::Headers;
|
||||
|
||||
use crate::CONFIG;
|
||||
@ -56,7 +56,6 @@ pub fn routes() -> Vec<Route> {
|
||||
delete_all,
|
||||
move_cipher_selected,
|
||||
move_cipher_selected_put,
|
||||
|
||||
post_collections_update,
|
||||
post_collections_admin,
|
||||
put_collections_admin,
|
||||
@ -80,9 +79,16 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
let collections_json: Vec<Value> = collections.iter().map(|c| c.to_json()).collect();
|
||||
|
||||
let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn);
|
||||
let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
|
||||
let ciphers_json: Vec<Value> = ciphers
|
||||
.iter()
|
||||
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn))
|
||||
.collect();
|
||||
|
||||
let domains_json = if data.exclude_domains { Value::Null } else { api::core::get_eq_domains(headers).unwrap().into_inner() };
|
||||
let domains_json = if data.exclude_domains {
|
||||
Value::Null
|
||||
} else {
|
||||
api::core::get_eq_domains(headers).unwrap().into_inner()
|
||||
};
|
||||
|
||||
Ok(Json(json!({
|
||||
"Profile": user_json,
|
||||
@ -98,7 +104,10 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult {
|
||||
let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn);
|
||||
|
||||
let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
|
||||
let ciphers_json: Vec<Value> = ciphers
|
||||
.iter()
|
||||
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn))
|
||||
.collect();
|
||||
|
||||
Ok(Json(json!({
|
||||
"Data": ciphers_json,
|
||||
@ -111,7 +120,7 @@ fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult {
|
||||
fn get_cipher(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
|
||||
Some(cipher) => cipher,
|
||||
None => err!("Cipher doesn't exist")
|
||||
None => err!("Cipher doesn't exist"),
|
||||
};
|
||||
|
||||
if !cipher.is_accessible_to_user(&headers.user.uuid, &conn) {
|
||||
@ -166,7 +175,7 @@ pub struct CipherData {
|
||||
// These are used during key rotation
|
||||
#[serde(rename = "Attachments")]
|
||||
_Attachments: Option<Value>, // Unused, contains map of {id: filename}
|
||||
Attachments2: Option<HashMap<String, Attachments2Data>>
|
||||
Attachments2: Option<HashMap<String, Attachments2Data>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
@ -177,42 +186,53 @@ pub struct Attachments2Data {
|
||||
}
|
||||
|
||||
#[post("/ciphers/admin", data = "<data>")]
|
||||
fn post_ciphers_admin(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
fn post_ciphers_admin(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
|
||||
let data: ShareCipherData = data.into_inner().data;
|
||||
|
||||
let mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone());
|
||||
cipher.user_uuid = Some(headers.user.uuid.clone());
|
||||
cipher.save(&conn)?;
|
||||
|
||||
share_cipher_by_uuid(&cipher.uuid, data, &headers, &conn, &ws)
|
||||
share_cipher_by_uuid(&cipher.uuid, data, &headers, &conn, &nt)
|
||||
}
|
||||
|
||||
#[post("/ciphers/create", data = "<data>")]
|
||||
fn post_ciphers_create(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
post_ciphers_admin(data, headers, conn, ws)
|
||||
fn post_ciphers_create(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
|
||||
post_ciphers_admin(data, headers, conn, nt)
|
||||
}
|
||||
|
||||
#[post("/ciphers", data = "<data>")]
|
||||
fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
|
||||
let data: CipherData = data.into_inner().data;
|
||||
|
||||
let mut cipher = Cipher::new(data.Type, data.Name.clone());
|
||||
update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &ws, UpdateType::SyncCipherCreate)?;
|
||||
update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherCreate)?;
|
||||
|
||||
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
|
||||
}
|
||||
|
||||
pub fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &Headers, shared_to_collection: bool, conn: &DbConn, ws: &State<WebSocketUsers>, ut: UpdateType) -> EmptyResult {
|
||||
pub fn update_cipher_from_data(
|
||||
cipher: &mut Cipher,
|
||||
data: CipherData,
|
||||
headers: &Headers,
|
||||
shared_to_collection: bool,
|
||||
conn: &DbConn,
|
||||
nt: &Notify,
|
||||
ut: UpdateType,
|
||||
) -> EmptyResult {
|
||||
if let Some(org_id) = data.OrganizationId {
|
||||
match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) {
|
||||
None => err!("You don't have permission to add item to organization"),
|
||||
Some(org_user) => if shared_to_collection
|
||||
|| org_user.has_full_access()
|
||||
|| cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
|
||||
cipher.organization_uuid = Some(org_id);
|
||||
cipher.user_uuid = None;
|
||||
} else {
|
||||
err!("You don't have permission to add cipher directly to organization")
|
||||
Some(org_user) => {
|
||||
if shared_to_collection
|
||||
|| org_user.has_full_access()
|
||||
|| cipher.is_write_accessible_to_user(&headers.user.uuid, &conn)
|
||||
{
|
||||
cipher.organization_uuid = Some(org_id);
|
||||
cipher.user_uuid = None;
|
||||
} else {
|
||||
err!("You don't have permission to add cipher directly to organization")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -226,7 +246,7 @@ pub fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &
|
||||
err!("Folder is not owned by user")
|
||||
}
|
||||
}
|
||||
None => err!("Folder doesn't exist")
|
||||
None => err!("Folder doesn't exist"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,7 +255,7 @@ pub fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &
|
||||
for (id, attachment) in attachments {
|
||||
let mut saved_att = match Attachment::find_by_id(&id, &conn) {
|
||||
Some(att) => att,
|
||||
None => err!("Attachment doesn't exist")
|
||||
None => err!("Attachment doesn't exist"),
|
||||
};
|
||||
|
||||
if saved_att.cipher_uuid != cipher.uuid {
|
||||
@ -254,12 +274,12 @@ pub fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &
|
||||
2 => data.SecureNote,
|
||||
3 => data.Card,
|
||||
4 => data.Identity,
|
||||
_ => err!("Invalid type")
|
||||
_ => err!("Invalid type"),
|
||||
};
|
||||
|
||||
let mut type_data = match type_data_opt {
|
||||
Some(data) => data,
|
||||
None => err!("Data missing")
|
||||
None => err!("Data missing"),
|
||||
};
|
||||
|
||||
// TODO: ******* Backwards compat start **********
|
||||
@ -280,7 +300,7 @@ pub fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &
|
||||
|
||||
cipher.save(&conn)?;
|
||||
|
||||
ws.send_cipher_update(ut, &cipher, &cipher.update_users_revision(&conn));
|
||||
nt.send_cipher_update(ut, &cipher, &cipher.update_users_revision(&conn));
|
||||
|
||||
cipher.move_to_folder(data.FolderId, &headers.user.uuid, &conn)
|
||||
}
|
||||
@ -304,9 +324,8 @@ struct RelationsData {
|
||||
Value: usize,
|
||||
}
|
||||
|
||||
|
||||
#[post("/ciphers/import", data = "<data>")]
|
||||
fn post_ciphers_import(data: JsonUpcase<ImportData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
fn post_ciphers_import(data: JsonUpcase<ImportData>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
let data: ImportData = data.into_inner().data;
|
||||
|
||||
// Read and create the folders
|
||||
@ -327,49 +346,67 @@ fn post_ciphers_import(data: JsonUpcase<ImportData>, headers: Headers, conn: DbC
|
||||
|
||||
// Read and create the ciphers
|
||||
for (index, cipher_data) in data.Ciphers.into_iter().enumerate() {
|
||||
let folder_uuid = relations_map.get(&index)
|
||||
.map(|i| folders[*i].uuid.clone());
|
||||
let folder_uuid = relations_map.get(&index).map(|i| folders[*i].uuid.clone());
|
||||
|
||||
let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone());
|
||||
update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &conn, &ws, UpdateType::SyncCipherCreate)?;
|
||||
update_cipher_from_data(
|
||||
&mut cipher,
|
||||
cipher_data,
|
||||
&headers,
|
||||
false,
|
||||
&conn,
|
||||
&nt,
|
||||
UpdateType::CipherCreate,
|
||||
)?;
|
||||
|
||||
cipher.move_to_folder(folder_uuid, &headers.user.uuid.clone(), &conn).ok();
|
||||
cipher.move_to_folder(folder_uuid, &headers.user.uuid.clone(), &conn)?;
|
||||
}
|
||||
|
||||
let mut user = headers.user;
|
||||
user.update_revision(&conn)
|
||||
}
|
||||
|
||||
|
||||
#[put("/ciphers/<uuid>/admin", data = "<data>")]
|
||||
fn put_cipher_admin(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
put_cipher(uuid, data, headers, conn, ws)
|
||||
fn put_cipher_admin(
|
||||
uuid: String,
|
||||
data: JsonUpcase<CipherData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
nt: Notify,
|
||||
) -> JsonResult {
|
||||
put_cipher(uuid, data, headers, conn, nt)
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/admin", data = "<data>")]
|
||||
fn post_cipher_admin(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
post_cipher(uuid, data, headers, conn, ws)
|
||||
fn post_cipher_admin(
|
||||
uuid: String,
|
||||
data: JsonUpcase<CipherData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
nt: Notify,
|
||||
) -> JsonResult {
|
||||
post_cipher(uuid, data, headers, conn, nt)
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>", data = "<data>")]
|
||||
fn post_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
put_cipher(uuid, data, headers, conn, ws)
|
||||
fn post_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
|
||||
put_cipher(uuid, data, headers, conn, nt)
|
||||
}
|
||||
|
||||
#[put("/ciphers/<uuid>", data = "<data>")]
|
||||
fn put_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
fn put_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
|
||||
let data: CipherData = data.into_inner().data;
|
||||
|
||||
let mut cipher = match Cipher::find_by_uuid(&uuid, &conn) {
|
||||
Some(cipher) => cipher,
|
||||
None => err!("Cipher doesn't exist")
|
||||
None => err!("Cipher doesn't exist"),
|
||||
};
|
||||
|
||||
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
|
||||
err!("Cipher is not write accessible")
|
||||
}
|
||||
|
||||
update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &ws, UpdateType::SyncCipherUpdate)?;
|
||||
update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherUpdate)?;
|
||||
|
||||
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
|
||||
}
|
||||
@ -381,22 +418,37 @@ struct CollectionsAdminData {
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/collections", data = "<data>")]
|
||||
fn post_collections_update(uuid: String, data: JsonUpcase<CollectionsAdminData>, headers: Headers, conn: DbConn) -> EmptyResult {
|
||||
fn post_collections_update(
|
||||
uuid: String,
|
||||
data: JsonUpcase<CollectionsAdminData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
post_collections_admin(uuid, data, headers, conn)
|
||||
}
|
||||
|
||||
#[put("/ciphers/<uuid>/collections-admin", data = "<data>")]
|
||||
fn put_collections_admin(uuid: String, data: JsonUpcase<CollectionsAdminData>, headers: Headers, conn: DbConn) -> EmptyResult {
|
||||
fn put_collections_admin(
|
||||
uuid: String,
|
||||
data: JsonUpcase<CollectionsAdminData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
post_collections_admin(uuid, data, headers, conn)
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/collections-admin", data = "<data>")]
|
||||
fn post_collections_admin(uuid: String, data: JsonUpcase<CollectionsAdminData>, headers: Headers, conn: DbConn) -> EmptyResult {
|
||||
fn post_collections_admin(
|
||||
uuid: String,
|
||||
data: JsonUpcase<CollectionsAdminData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
let data: CollectionsAdminData = data.into_inner().data;
|
||||
|
||||
let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
|
||||
Some(cipher) => cipher,
|
||||
None => err!("Cipher doesn't exist")
|
||||
None => err!("Cipher doesn't exist"),
|
||||
};
|
||||
|
||||
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
|
||||
@ -404,16 +456,22 @@ fn post_collections_admin(uuid: String, data: JsonUpcase<CollectionsAdminData>,
|
||||
}
|
||||
|
||||
let posted_collections: HashSet<String> = data.CollectionIds.iter().cloned().collect();
|
||||
let current_collections: HashSet<String> = cipher.get_collections(&headers.user.uuid ,&conn).iter().cloned().collect();
|
||||
let current_collections: HashSet<String> = cipher
|
||||
.get_collections(&headers.user.uuid, &conn)
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
for collection in posted_collections.symmetric_difference(¤t_collections) {
|
||||
match Collection::find_by_uuid(&collection, &conn) {
|
||||
None => err!("Invalid collection ID provided"),
|
||||
Some(collection) => {
|
||||
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
|
||||
CollectionCipher::save(&cipher.uuid, &collection.uuid, &conn)?;
|
||||
} else { // Remove from collection
|
||||
} else {
|
||||
// Remove from collection
|
||||
CollectionCipher::delete(&cipher.uuid, &collection.uuid, &conn)?;
|
||||
}
|
||||
} else {
|
||||
@ -434,28 +492,45 @@ struct ShareCipherData {
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/share", data = "<data>")]
|
||||
fn post_cipher_share(uuid: String, data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
fn post_cipher_share(
|
||||
uuid: String,
|
||||
data: JsonUpcase<ShareCipherData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
nt: Notify,
|
||||
) -> JsonResult {
|
||||
let data: ShareCipherData = data.into_inner().data;
|
||||
|
||||
share_cipher_by_uuid(&uuid, data, &headers, &conn, &ws)
|
||||
share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt)
|
||||
}
|
||||
|
||||
#[put("/ciphers/<uuid>/share", data = "<data>")]
|
||||
fn put_cipher_share(uuid: String, data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
fn put_cipher_share(
|
||||
uuid: String,
|
||||
data: JsonUpcase<ShareCipherData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
nt: Notify,
|
||||
) -> JsonResult {
|
||||
let data: ShareCipherData = data.into_inner().data;
|
||||
|
||||
share_cipher_by_uuid(&uuid, data, &headers, &conn, &ws)
|
||||
share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
struct ShareSelectedCipherData {
|
||||
Ciphers: Vec<CipherData>,
|
||||
CollectionIds: Vec<String>
|
||||
CollectionIds: Vec<String>,
|
||||
}
|
||||
|
||||
#[put("/ciphers/share", data = "<data>")]
|
||||
fn put_cipher_share_seleted(data: JsonUpcase<ShareSelectedCipherData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
fn put_cipher_share_seleted(
|
||||
data: JsonUpcase<ShareSelectedCipherData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
nt: Notify,
|
||||
) -> EmptyResult {
|
||||
let mut data: ShareSelectedCipherData = data.into_inner().data;
|
||||
let mut cipher_ids: Vec<String> = Vec::new();
|
||||
|
||||
@ -470,7 +545,7 @@ fn put_cipher_share_seleted(data: JsonUpcase<ShareSelectedCipherData>, headers:
|
||||
for cipher in data.Ciphers.iter() {
|
||||
match cipher.Id {
|
||||
Some(ref id) => cipher_ids.push(id.to_string()),
|
||||
None => err!("Request missing ids field")
|
||||
None => err!("Request missing ids field"),
|
||||
};
|
||||
}
|
||||
|
||||
@ -483,20 +558,25 @@ fn put_cipher_share_seleted(data: JsonUpcase<ShareSelectedCipherData>, headers:
|
||||
while let Some(cipher) = data.Ciphers.pop() {
|
||||
let mut shared_cipher_data = ShareCipherData {
|
||||
Cipher: cipher,
|
||||
CollectionIds: data.CollectionIds.clone()
|
||||
CollectionIds: data.CollectionIds.clone(),
|
||||
};
|
||||
|
||||
match shared_cipher_data.Cipher.Id.take() {
|
||||
Some(id) => share_cipher_by_uuid(&id, shared_cipher_data , &headers, &conn, &ws)?,
|
||||
None => err!("Request missing ids field")
|
||||
|
||||
Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &conn, &nt)?,
|
||||
None => err!("Request missing ids field"),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn share_cipher_by_uuid(uuid: &str, data: ShareCipherData, headers: &Headers, conn: &DbConn, ws: &State<WebSocketUsers>) -> JsonResult {
|
||||
fn share_cipher_by_uuid(
|
||||
uuid: &str,
|
||||
data: ShareCipherData,
|
||||
headers: &Headers,
|
||||
conn: &DbConn,
|
||||
nt: &Notify,
|
||||
) -> JsonResult {
|
||||
let mut cipher = match Cipher::find_by_uuid(&uuid, &conn) {
|
||||
Some(cipher) => {
|
||||
if cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
|
||||
@ -504,8 +584,8 @@ fn share_cipher_by_uuid(uuid: &str, data: ShareCipherData, headers: &Headers, co
|
||||
} else {
|
||||
err!("Cipher is not write accessible")
|
||||
}
|
||||
},
|
||||
None => err!("Cipher doesn't exist")
|
||||
}
|
||||
None => err!("Cipher doesn't exist"),
|
||||
};
|
||||
|
||||
match data.Cipher.OrganizationId.clone() {
|
||||
@ -525,7 +605,15 @@ fn share_cipher_by_uuid(uuid: &str, data: ShareCipherData, headers: &Headers, co
|
||||
}
|
||||
}
|
||||
}
|
||||
update_cipher_from_data(&mut cipher, data.Cipher, &headers, shared_to_collection, &conn, &ws, UpdateType::SyncCipherUpdate)?;
|
||||
update_cipher_from_data(
|
||||
&mut cipher,
|
||||
data.Cipher,
|
||||
&headers,
|
||||
shared_to_collection,
|
||||
&conn,
|
||||
&nt,
|
||||
UpdateType::CipherUpdate,
|
||||
)?;
|
||||
|
||||
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
|
||||
}
|
||||
@ -536,7 +624,7 @@ fn share_cipher_by_uuid(uuid: &str, data: ShareCipherData, headers: &Headers, co
|
||||
fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
|
||||
Some(cipher) => cipher,
|
||||
None => err!("Cipher doesn't exist")
|
||||
None => err!("Cipher doesn't exist"),
|
||||
};
|
||||
|
||||
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
|
||||
@ -551,117 +639,152 @@ fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers
|
||||
|
||||
let mut attachment_key = None;
|
||||
|
||||
Multipart::with_body(data.open(), boundary).foreach_entry(|mut field| {
|
||||
match field.headers.name.as_str() {
|
||||
"key" => {
|
||||
use std::io::Read;
|
||||
let mut key_buffer = String::new();
|
||||
if field.data.read_to_string(&mut key_buffer).is_ok() {
|
||||
attachment_key = Some(key_buffer);
|
||||
}
|
||||
},
|
||||
"data" => {
|
||||
// This is provided by the client, don't trust it
|
||||
let name = field.headers.filename.expect("No filename provided");
|
||||
|
||||
let file_name = HEXLOWER.encode(&crypto::get_random(vec![0; 10]));
|
||||
let path = base_path.join(&file_name);
|
||||
|
||||
let size = match field.data.save()
|
||||
.memory_threshold(0)
|
||||
.size_limit(None)
|
||||
.with_path(path) {
|
||||
SaveResult::Full(SavedData::File(_, size)) => size as i32,
|
||||
SaveResult::Full(other) => {
|
||||
error!("Attachment is not a file: {:?}", other);
|
||||
return;
|
||||
},
|
||||
SaveResult::Partial(_, reason) => {
|
||||
error!("Partial result: {:?}", reason);
|
||||
return;
|
||||
},
|
||||
SaveResult::Error(e) => {
|
||||
error!("Error: {:?}", e);
|
||||
return;
|
||||
Multipart::with_body(data.open(), boundary)
|
||||
.foreach_entry(|mut field| {
|
||||
match field.headers.name.as_str() {
|
||||
"key" => {
|
||||
use std::io::Read;
|
||||
let mut key_buffer = String::new();
|
||||
if field.data.read_to_string(&mut key_buffer).is_ok() {
|
||||
attachment_key = Some(key_buffer);
|
||||
}
|
||||
};
|
||||
}
|
||||
"data" => {
|
||||
// This is provided by the client, don't trust it
|
||||
let name = field.headers.filename.expect("No filename provided");
|
||||
|
||||
let mut attachment = Attachment::new(file_name, cipher.uuid.clone(), name, size);
|
||||
attachment.key = attachment_key.clone();
|
||||
attachment.save(&conn).expect("Error saving attachment");
|
||||
},
|
||||
_ => error!("Invalid multipart name")
|
||||
}
|
||||
}).expect("Error processing multipart data");
|
||||
let file_name = HEXLOWER.encode(&crypto::get_random(vec![0; 10]));
|
||||
let path = base_path.join(&file_name);
|
||||
|
||||
let size = match field.data.save().memory_threshold(0).size_limit(None).with_path(path) {
|
||||
SaveResult::Full(SavedData::File(_, size)) => size as i32,
|
||||
SaveResult::Full(other) => {
|
||||
error!("Attachment is not a file: {:?}", other);
|
||||
return;
|
||||
}
|
||||
SaveResult::Partial(_, reason) => {
|
||||
error!("Partial result: {:?}", reason);
|
||||
return;
|
||||
}
|
||||
SaveResult::Error(e) => {
|
||||
error!("Error: {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut attachment = Attachment::new(file_name, cipher.uuid.clone(), name, size);
|
||||
attachment.key = attachment_key.clone();
|
||||
attachment.save(&conn).expect("Error saving attachment");
|
||||
}
|
||||
_ => error!("Invalid multipart name"),
|
||||
}
|
||||
})
|
||||
.expect("Error processing multipart data");
|
||||
|
||||
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/attachment-admin", format = "multipart/form-data", data = "<data>")]
|
||||
fn post_attachment_admin(uuid: String, data: Data, content_type: &ContentType, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
fn post_attachment_admin(
|
||||
uuid: String,
|
||||
data: Data,
|
||||
content_type: &ContentType,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
post_attachment(uuid, data, content_type, headers, conn)
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/attachment/<attachment_id>/share", format = "multipart/form-data", data = "<data>")]
|
||||
fn post_attachment_share(uuid: String, attachment_id: String, data: Data, content_type: &ContentType, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &ws)?;
|
||||
#[post(
|
||||
"/ciphers/<uuid>/attachment/<attachment_id>/share",
|
||||
format = "multipart/form-data",
|
||||
data = "<data>"
|
||||
)]
|
||||
fn post_attachment_share(
|
||||
uuid: String,
|
||||
attachment_id: String,
|
||||
data: Data,
|
||||
content_type: &ContentType,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
nt: Notify,
|
||||
) -> JsonResult {
|
||||
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt)?;
|
||||
post_attachment(uuid, data, content_type, headers, conn)
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete-admin")]
|
||||
fn delete_attachment_post_admin(uuid: String, attachment_id: String, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
delete_attachment(uuid, attachment_id, headers, conn, ws)
|
||||
fn delete_attachment_post_admin(
|
||||
uuid: String,
|
||||
attachment_id: String,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
nt: Notify,
|
||||
) -> EmptyResult {
|
||||
delete_attachment(uuid, attachment_id, headers, conn, nt)
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")]
|
||||
fn delete_attachment_post(uuid: String, attachment_id: String, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
delete_attachment(uuid, attachment_id, headers, conn, ws)
|
||||
fn delete_attachment_post(
|
||||
uuid: String,
|
||||
attachment_id: String,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
nt: Notify,
|
||||
) -> EmptyResult {
|
||||
delete_attachment(uuid, attachment_id, headers, conn, nt)
|
||||
}
|
||||
|
||||
#[delete("/ciphers/<uuid>/attachment/<attachment_id>")]
|
||||
fn delete_attachment(uuid: String, attachment_id: String, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &ws)
|
||||
fn delete_attachment(uuid: String, attachment_id: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt)
|
||||
}
|
||||
|
||||
#[delete("/ciphers/<uuid>/attachment/<attachment_id>/admin")]
|
||||
fn delete_attachment_admin(uuid: String, attachment_id: String, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &ws)
|
||||
fn delete_attachment_admin(
|
||||
uuid: String,
|
||||
attachment_id: String,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
nt: Notify,
|
||||
) -> EmptyResult {
|
||||
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt)
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/delete")]
|
||||
fn delete_cipher_post(uuid: String, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &conn, &ws)
|
||||
fn delete_cipher_post(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &conn, &nt)
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/delete-admin")]
|
||||
fn delete_cipher_post_admin(uuid: String, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &conn, &ws)
|
||||
fn delete_cipher_post_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &conn, &nt)
|
||||
}
|
||||
|
||||
#[delete("/ciphers/<uuid>")]
|
||||
fn delete_cipher(uuid: String, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &conn, &ws)
|
||||
fn delete_cipher(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &conn, &nt)
|
||||
}
|
||||
|
||||
#[delete("/ciphers/<uuid>/admin")]
|
||||
fn delete_cipher_admin(uuid: String, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &conn, &ws)
|
||||
fn delete_cipher_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &conn, &nt)
|
||||
}
|
||||
|
||||
#[delete("/ciphers", data = "<data>")]
|
||||
fn delete_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
fn delete_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
let data: Value = data.into_inner().data;
|
||||
|
||||
let uuids = match data.get("Ids") {
|
||||
Some(ids) => match ids.as_array() {
|
||||
Some(ids) => ids.iter().filter_map(Value::as_str),
|
||||
None => err!("Posted ids field is not an array")
|
||||
None => err!("Posted ids field is not an array"),
|
||||
},
|
||||
None => err!("Request missing ids field")
|
||||
None => err!("Request missing ids field"),
|
||||
};
|
||||
|
||||
for uuid in uuids {
|
||||
if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &conn, &ws) {
|
||||
if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &conn, &nt) {
|
||||
return error;
|
||||
};
|
||||
}
|
||||
@ -670,46 +793,42 @@ fn delete_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbCon
|
||||
}
|
||||
|
||||
#[post("/ciphers/delete", data = "<data>")]
|
||||
fn delete_cipher_selected_post(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
delete_cipher_selected(data, headers, conn, ws)
|
||||
fn delete_cipher_selected_post(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
delete_cipher_selected(data, headers, conn, nt)
|
||||
}
|
||||
|
||||
#[post("/ciphers/move", data = "<data>")]
|
||||
fn move_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
fn move_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
let data = data.into_inner().data;
|
||||
|
||||
let folder_id = match data.get("FolderId") {
|
||||
Some(folder_id) => {
|
||||
match folder_id.as_str() {
|
||||
Some(folder_id) => {
|
||||
match Folder::find_by_uuid(folder_id, &conn) {
|
||||
Some(folder) => {
|
||||
if folder.user_uuid != headers.user.uuid {
|
||||
err!("Folder is not owned by user")
|
||||
}
|
||||
Some(folder.uuid)
|
||||
}
|
||||
None => err!("Folder doesn't exist")
|
||||
Some(folder_id) => match folder_id.as_str() {
|
||||
Some(folder_id) => match Folder::find_by_uuid(folder_id, &conn) {
|
||||
Some(folder) => {
|
||||
if folder.user_uuid != headers.user.uuid {
|
||||
err!("Folder is not owned by user")
|
||||
}
|
||||
Some(folder.uuid)
|
||||
}
|
||||
None => err!("Folder id provided in wrong format")
|
||||
}
|
||||
}
|
||||
None => None
|
||||
None => err!("Folder doesn't exist"),
|
||||
},
|
||||
None => err!("Folder id provided in wrong format"),
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
let uuids = match data.get("Ids") {
|
||||
Some(ids) => match ids.as_array() {
|
||||
Some(ids) => ids.iter().filter_map(Value::as_str),
|
||||
None => err!("Posted ids field is not an array")
|
||||
None => err!("Posted ids field is not an array"),
|
||||
},
|
||||
None => err!("Request missing ids field")
|
||||
None => err!("Request missing ids field"),
|
||||
};
|
||||
|
||||
for uuid in uuids {
|
||||
let mut cipher = match Cipher::find_by_uuid(uuid, &conn) {
|
||||
Some(cipher) => cipher,
|
||||
None => err!("Cipher doesn't exist")
|
||||
None => err!("Cipher doesn't exist"),
|
||||
};
|
||||
|
||||
if !cipher.is_accessible_to_user(&headers.user.uuid, &conn) {
|
||||
@ -720,19 +839,19 @@ fn move_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbConn,
|
||||
cipher.move_to_folder(folder_id.clone(), &headers.user.uuid, &conn)?;
|
||||
cipher.save(&conn)?;
|
||||
|
||||
ws.send_cipher_update(UpdateType::SyncCipherUpdate, &cipher, &cipher.update_users_revision(&conn));
|
||||
nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&conn));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[put("/ciphers/move", data = "<data>")]
|
||||
fn move_cipher_selected_put(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
move_cipher_selected(data, headers, conn, ws)
|
||||
fn move_cipher_selected_put(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
move_cipher_selected(data, headers, conn, nt)
|
||||
}
|
||||
|
||||
#[post("/ciphers/purge", data = "<data>")]
|
||||
fn delete_all(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
fn delete_all(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
let data: PasswordData = data.into_inner().data;
|
||||
let password_hash = data.MasterPasswordHash;
|
||||
|
||||
@ -745,19 +864,19 @@ fn delete_all(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn, ws
|
||||
// Delete ciphers and their attachments
|
||||
for cipher in Cipher::find_owned_by_user(&user.uuid, &conn) {
|
||||
cipher.delete(&conn)?;
|
||||
ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
|
||||
nt.send_cipher_update(UpdateType::CipherDelete, &cipher, &cipher.update_users_revision(&conn));
|
||||
}
|
||||
|
||||
// Delete folders
|
||||
for f in Folder::find_by_user(&user.uuid, &conn) {
|
||||
f.delete(&conn)?;
|
||||
ws.send_folder_update(UpdateType::SyncFolderCreate, &f);
|
||||
nt.send_folder_update(UpdateType::FolderCreate, &f);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn _delete_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, ws: &State<WebSocketUsers>) -> EmptyResult {
|
||||
fn _delete_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, nt: &Notify) -> EmptyResult {
|
||||
let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
|
||||
Some(cipher) => cipher,
|
||||
None => err!("Cipher doesn't exist"),
|
||||
@ -768,14 +887,20 @@ fn _delete_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, ws: &Sta
|
||||
}
|
||||
|
||||
cipher.delete(&conn)?;
|
||||
ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
|
||||
nt.send_cipher_update(UpdateType::CipherDelete, &cipher, &cipher.update_users_revision(&conn));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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,
|
||||
nt: &Notify,
|
||||
) -> EmptyResult {
|
||||
let attachment = match Attachment::find_by_id(&attachment_id, &conn) {
|
||||
Some(attachment) => attachment,
|
||||
None => err!("Attachment doesn't exist")
|
||||
None => err!("Attachment doesn't exist"),
|
||||
};
|
||||
|
||||
if attachment.cipher_uuid != uuid {
|
||||
@ -784,7 +909,7 @@ fn _delete_cipher_attachment_by_id(uuid: &str, attachment_id: &str, headers: &He
|
||||
|
||||
let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
|
||||
Some(cipher) => cipher,
|
||||
None => err!("Cipher doesn't exist")
|
||||
None => err!("Cipher doesn't exist"),
|
||||
};
|
||||
|
||||
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
|
||||
@ -793,6 +918,6 @@ fn _delete_cipher_attachment_by_id(uuid: &str, attachment_id: &str, headers: &He
|
||||
|
||||
// Delete attachment
|
||||
attachment.delete(&conn)?;
|
||||
ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
|
||||
nt.send_cipher_update(UpdateType::CipherDelete, &cipher, &cipher.update_users_revision(&conn));
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
use rocket::State;
|
||||
use rocket_contrib::json::Json;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::db::DbConn;
|
||||
use crate::db::models::*;
|
||||
use crate::db::DbConn;
|
||||
|
||||
use crate::api::{JsonResult, EmptyResult, JsonUpcase, WebSocketUsers, UpdateType};
|
||||
use crate::api::{EmptyResult, JsonResult, JsonUpcase, Notify, UpdateType};
|
||||
use crate::auth::Headers;
|
||||
|
||||
use rocket::Route;
|
||||
@ -39,7 +38,7 @@ fn get_folders(headers: Headers, conn: DbConn) -> JsonResult {
|
||||
fn get_folder(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
let folder = match Folder::find_by_uuid(&uuid, &conn) {
|
||||
Some(folder) => folder,
|
||||
_ => err!("Invalid folder")
|
||||
_ => err!("Invalid folder"),
|
||||
};
|
||||
|
||||
if folder.user_uuid != headers.user.uuid {
|
||||
@ -53,33 +52,33 @@ fn get_folder(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
#[allow(non_snake_case)]
|
||||
|
||||
pub struct FolderData {
|
||||
pub Name: String
|
||||
pub Name: String,
|
||||
}
|
||||
|
||||
#[post("/folders", data = "<data>")]
|
||||
fn post_folders(data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
fn post_folders(data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
|
||||
let data: FolderData = data.into_inner().data;
|
||||
|
||||
let mut folder = Folder::new(headers.user.uuid.clone(), data.Name);
|
||||
|
||||
folder.save(&conn)?;
|
||||
ws.send_folder_update(UpdateType::SyncFolderCreate, &folder);
|
||||
nt.send_folder_update(UpdateType::FolderCreate, &folder);
|
||||
|
||||
Ok(Json(folder.to_json()))
|
||||
}
|
||||
|
||||
#[post("/folders/<uuid>", data = "<data>")]
|
||||
fn post_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
put_folder(uuid, data, headers, conn, ws)
|
||||
fn post_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
|
||||
put_folder(uuid, data, headers, conn, nt)
|
||||
}
|
||||
|
||||
#[put("/folders/<uuid>", data = "<data>")]
|
||||
fn put_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
|
||||
fn put_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
|
||||
let data: FolderData = data.into_inner().data;
|
||||
|
||||
let mut folder = match Folder::find_by_uuid(&uuid, &conn) {
|
||||
Some(folder) => folder,
|
||||
_ => err!("Invalid folder")
|
||||
_ => err!("Invalid folder"),
|
||||
};
|
||||
|
||||
if folder.user_uuid != headers.user.uuid {
|
||||
@ -89,21 +88,21 @@ fn put_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn
|
||||
folder.name = data.Name;
|
||||
|
||||
folder.save(&conn)?;
|
||||
ws.send_folder_update(UpdateType::SyncFolderUpdate, &folder);
|
||||
nt.send_folder_update(UpdateType::FolderUpdate, &folder);
|
||||
|
||||
Ok(Json(folder.to_json()))
|
||||
}
|
||||
|
||||
#[post("/folders/<uuid>/delete")]
|
||||
fn delete_folder_post(uuid: String, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
delete_folder(uuid, headers, conn, ws)
|
||||
fn delete_folder_post(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
delete_folder(uuid, headers, conn, nt)
|
||||
}
|
||||
|
||||
#[delete("/folders/<uuid>")]
|
||||
fn delete_folder(uuid: String, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
fn delete_folder(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
|
||||
let folder = match Folder::find_by_uuid(&uuid, &conn) {
|
||||
Some(folder) => folder,
|
||||
_ => err!("Invalid folder")
|
||||
_ => err!("Invalid folder"),
|
||||
};
|
||||
|
||||
if folder.user_uuid != headers.user.uuid {
|
||||
@ -113,6 +112,6 @@ fn delete_folder(uuid: String, headers: Headers, conn: DbConn, ws: State<WebSock
|
||||
// Delete the actual folder entry
|
||||
folder.delete(&conn)?;
|
||||
|
||||
ws.send_folder_update(UpdateType::SyncFolderDelete, &folder);
|
||||
nt.send_folder_update(UpdateType::FolderDelete, &folder);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ pub fn routes() -> Vec<Route> {
|
||||
let mut mod_routes = routes![
|
||||
clear_device_token,
|
||||
put_device_token,
|
||||
|
||||
get_eq_domains,
|
||||
post_eq_domains,
|
||||
put_eq_domains,
|
||||
@ -25,9 +24,9 @@ pub fn routes() -> Vec<Route> {
|
||||
routes
|
||||
}
|
||||
|
||||
///
|
||||
/// Move this somewhere else
|
||||
///
|
||||
//
|
||||
// Move this somewhere else
|
||||
//
|
||||
use rocket::Route;
|
||||
|
||||
use rocket_contrib::json::Json;
|
||||
|
@ -1,14 +1,13 @@
|
||||
use rocket::State;
|
||||
use rocket::request::Form;
|
||||
use rocket_contrib::json::Json;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::CONFIG;
|
||||
use crate::db::DbConn;
|
||||
use crate::db::models::*;
|
||||
use crate::db::DbConn;
|
||||
use crate::CONFIG;
|
||||
|
||||
use crate::api::{PasswordData, JsonResult, EmptyResult, NumberOrString, JsonUpcase, WebSocketUsers, UpdateType};
|
||||
use crate::auth::{Headers, AdminHeaders, OwnerHeaders, encode_jwt, decode_invite_jwt, InviteJWTClaims, JWT_ISSUER};
|
||||
use crate::api::{EmptyResult, JsonResult, JsonUpcase, Notify, NumberOrString, PasswordData, UpdateType};
|
||||
use crate::auth::{decode_invite_jwt, encode_jwt, AdminHeaders, Headers, InviteJWTClaims, OwnerHeaders, JWT_ISSUER};
|
||||
|
||||
use crate::mail;
|
||||
|
||||
@ -53,7 +52,6 @@ pub fn routes() -> Vec<Route> {
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
struct OrgData {
|
||||
@ -83,10 +81,8 @@ fn create_organization(headers: Headers, data: JsonUpcase<OrgData>, conn: DbConn
|
||||
let data: OrgData = data.into_inner().data;
|
||||
|
||||
let mut org = Organization::new(data.Name, data.BillingEmail);
|
||||
let mut user_org = UserOrganization::new(
|
||||
headers.user.uuid.clone(), org.uuid.clone());
|
||||
let mut collection = Collection::new(
|
||||
org.uuid.clone(), data.CollectionName);
|
||||
let mut user_org = UserOrganization::new(headers.user.uuid.clone(), org.uuid.clone());
|
||||
let mut collection = Collection::new(org.uuid.clone(), data.CollectionName);
|
||||
|
||||
user_org.key = data.Key;
|
||||
user_org.access_all = true;
|
||||
@ -101,7 +97,12 @@ fn create_organization(headers: Headers, data: JsonUpcase<OrgData>, conn: DbConn
|
||||
}
|
||||
|
||||
#[delete("/organizations/<org_id>", data = "<data>")]
|
||||
fn delete_organization(org_id: String, data: JsonUpcase<PasswordData>, headers: OwnerHeaders, conn: DbConn) -> EmptyResult {
|
||||
fn delete_organization(
|
||||
org_id: String,
|
||||
data: JsonUpcase<PasswordData>,
|
||||
headers: OwnerHeaders,
|
||||
conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
let data: PasswordData = data.into_inner().data;
|
||||
let password_hash = data.MasterPasswordHash;
|
||||
|
||||
@ -111,12 +112,17 @@ fn delete_organization(org_id: String, data: JsonUpcase<PasswordData>, headers:
|
||||
|
||||
match Organization::find_by_uuid(&org_id, &conn) {
|
||||
None => err!("Organization not found"),
|
||||
Some(org) => org.delete(&conn)
|
||||
Some(org) => org.delete(&conn),
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>/delete", data = "<data>")]
|
||||
fn post_delete_organization(org_id: String, data: JsonUpcase<PasswordData>, headers: OwnerHeaders, conn: DbConn) -> EmptyResult {
|
||||
fn post_delete_organization(
|
||||
org_id: String,
|
||||
data: JsonUpcase<PasswordData>,
|
||||
headers: OwnerHeaders,
|
||||
conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
delete_organization(org_id, data, headers, conn)
|
||||
}
|
||||
|
||||
@ -126,15 +132,14 @@ fn leave_organization(org_id: String, headers: Headers, conn: DbConn) -> EmptyRe
|
||||
None => err!("User not part of organization"),
|
||||
Some(user_org) => {
|
||||
if user_org.type_ == UserOrgType::Owner {
|
||||
let num_owners = UserOrganization::find_by_org_and_type(
|
||||
&org_id, UserOrgType::Owner as i32, &conn)
|
||||
.len();
|
||||
let num_owners =
|
||||
UserOrganization::find_by_org_and_type(&org_id, UserOrgType::Owner as i32, &conn).len();
|
||||
|
||||
if num_owners <= 1 {
|
||||
err!("The last owner can't leave")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
user_org.delete(&conn)
|
||||
}
|
||||
}
|
||||
@ -144,22 +149,32 @@ fn leave_organization(org_id: String, headers: Headers, conn: DbConn) -> EmptyRe
|
||||
fn get_organization(org_id: String, _headers: OwnerHeaders, conn: DbConn) -> JsonResult {
|
||||
match Organization::find_by_uuid(&org_id, &conn) {
|
||||
Some(organization) => Ok(Json(organization.to_json())),
|
||||
None => err!("Can't find organization details")
|
||||
None => err!("Can't find organization details"),
|
||||
}
|
||||
}
|
||||
|
||||
#[put("/organizations/<org_id>", data = "<data>")]
|
||||
fn put_organization(org_id: String, headers: OwnerHeaders, data: JsonUpcase<OrganizationUpdateData>, conn: DbConn) -> JsonResult {
|
||||
fn put_organization(
|
||||
org_id: String,
|
||||
headers: OwnerHeaders,
|
||||
data: JsonUpcase<OrganizationUpdateData>,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
post_organization(org_id, headers, data, conn)
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>", data = "<data>")]
|
||||
fn post_organization(org_id: String, _headers: OwnerHeaders, data: JsonUpcase<OrganizationUpdateData>, conn: DbConn) -> JsonResult {
|
||||
fn post_organization(
|
||||
org_id: String,
|
||||
_headers: OwnerHeaders,
|
||||
data: JsonUpcase<OrganizationUpdateData>,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
let data: OrganizationUpdateData = data.into_inner().data;
|
||||
|
||||
let mut org = match Organization::find_by_uuid(&org_id, &conn) {
|
||||
Some(organization) => organization,
|
||||
None => err!("Can't find organization details")
|
||||
None => err!("Can't find organization details"),
|
||||
};
|
||||
|
||||
org.name = data.Name;
|
||||
@ -172,7 +187,6 @@ fn post_organization(org_id: String, _headers: OwnerHeaders, data: JsonUpcase<Or
|
||||
// GET /api/collections?writeOnly=false
|
||||
#[get("/collections")]
|
||||
fn get_user_collections(headers: Headers, conn: DbConn) -> JsonResult {
|
||||
|
||||
Ok(Json(json!({
|
||||
"Data":
|
||||
Collection::find_by_user_uuid(&headers.user.uuid, &conn)
|
||||
@ -198,12 +212,17 @@ fn get_org_collections(org_id: String, _headers: AdminHeaders, conn: DbConn) ->
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>/collections", data = "<data>")]
|
||||
fn post_organization_collections(org_id: String, _headers: AdminHeaders, data: JsonUpcase<NewCollectionData>, conn: DbConn) -> JsonResult {
|
||||
fn post_organization_collections(
|
||||
org_id: String,
|
||||
_headers: AdminHeaders,
|
||||
data: JsonUpcase<NewCollectionData>,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
let data: NewCollectionData = data.into_inner().data;
|
||||
|
||||
let org = match Organization::find_by_uuid(&org_id, &conn) {
|
||||
Some(organization) => organization,
|
||||
None => err!("Can't find organization details")
|
||||
None => err!("Can't find organization details"),
|
||||
};
|
||||
|
||||
let mut collection = Collection::new(org.uuid.clone(), data.Name);
|
||||
@ -213,22 +232,34 @@ fn post_organization_collections(org_id: String, _headers: AdminHeaders, data: J
|
||||
}
|
||||
|
||||
#[put("/organizations/<org_id>/collections/<col_id>", data = "<data>")]
|
||||
fn put_organization_collection_update(org_id: String, col_id: String, headers: AdminHeaders, data: JsonUpcase<NewCollectionData>, conn: DbConn) -> JsonResult {
|
||||
fn put_organization_collection_update(
|
||||
org_id: String,
|
||||
col_id: String,
|
||||
headers: AdminHeaders,
|
||||
data: JsonUpcase<NewCollectionData>,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
post_organization_collection_update(org_id, col_id, headers, data, conn)
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>/collections/<col_id>", data = "<data>")]
|
||||
fn post_organization_collection_update(org_id: String, col_id: String, _headers: AdminHeaders, data: JsonUpcase<NewCollectionData>, conn: DbConn) -> JsonResult {
|
||||
fn post_organization_collection_update(
|
||||
org_id: String,
|
||||
col_id: String,
|
||||
_headers: AdminHeaders,
|
||||
data: JsonUpcase<NewCollectionData>,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
let data: NewCollectionData = data.into_inner().data;
|
||||
|
||||
let org = match Organization::find_by_uuid(&org_id, &conn) {
|
||||
Some(organization) => organization,
|
||||
None => err!("Can't find organization details")
|
||||
None => err!("Can't find organization details"),
|
||||
};
|
||||
|
||||
let mut collection = match Collection::find_by_uuid(&col_id, &conn) {
|
||||
Some(collection) => collection,
|
||||
None => err!("Collection not found")
|
||||
None => err!("Collection not found"),
|
||||
};
|
||||
|
||||
if collection.org_uuid != org.uuid {
|
||||
@ -241,15 +272,22 @@ fn post_organization_collection_update(org_id: String, col_id: String, _headers:
|
||||
Ok(Json(collection.to_json()))
|
||||
}
|
||||
|
||||
|
||||
#[delete("/organizations/<org_id>/collections/<col_id>/user/<org_user_id>")]
|
||||
fn delete_organization_collection_user(org_id: String, col_id: String, org_user_id: String, _headers: AdminHeaders, conn: DbConn) -> EmptyResult {
|
||||
fn delete_organization_collection_user(
|
||||
org_id: String,
|
||||
col_id: String,
|
||||
org_user_id: String,
|
||||
_headers: AdminHeaders,
|
||||
conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
let collection = match Collection::find_by_uuid(&col_id, &conn) {
|
||||
None => err!("Collection not found"),
|
||||
Some(collection) => if collection.org_uuid == org_id {
|
||||
collection
|
||||
} else {
|
||||
err!("Collection and Organization id do not match")
|
||||
Some(collection) => {
|
||||
if collection.org_uuid == org_id {
|
||||
collection
|
||||
} else {
|
||||
err!("Collection and Organization id do not match")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -258,16 +296,20 @@ fn delete_organization_collection_user(org_id: String, col_id: String, org_user_
|
||||
Some(user_org) => {
|
||||
match CollectionUser::find_by_collection_and_user(&collection.uuid, &user_org.user_uuid, &conn) {
|
||||
None => err!("User not assigned to collection"),
|
||||
Some(col_user) => {
|
||||
col_user.delete(&conn)
|
||||
}
|
||||
Some(col_user) => col_user.delete(&conn),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>/collections/<col_id>/delete-user/<org_user_id>")]
|
||||
fn post_organization_collection_delete_user(org_id: String, col_id: String, org_user_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
|
||||
fn post_organization_collection_delete_user(
|
||||
org_id: String,
|
||||
col_id: String,
|
||||
org_user_id: String,
|
||||
headers: AdminHeaders,
|
||||
conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
delete_organization_collection_user(org_id, col_id, org_user_id, headers, conn)
|
||||
}
|
||||
|
||||
@ -275,10 +317,12 @@ fn post_organization_collection_delete_user(org_id: String, col_id: String, org_
|
||||
fn delete_organization_collection(org_id: String, col_id: String, _headers: AdminHeaders, conn: DbConn) -> EmptyResult {
|
||||
match Collection::find_by_uuid(&col_id, &conn) {
|
||||
None => err!("Collection not found"),
|
||||
Some(collection) => if collection.org_uuid == org_id {
|
||||
collection.delete(&conn)
|
||||
} else {
|
||||
err!("Collection and Organization id do not match")
|
||||
Some(collection) => {
|
||||
if collection.org_uuid == org_id {
|
||||
collection.delete(&conn)
|
||||
} else {
|
||||
err!("Collection and Organization id do not match")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -291,7 +335,13 @@ struct DeleteCollectionData {
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>/collections/<col_id>/delete", data = "<_data>")]
|
||||
fn post_organization_collection_delete(org_id: String, col_id: String, headers: AdminHeaders, _data: JsonUpcase<DeleteCollectionData>, conn: DbConn) -> EmptyResult {
|
||||
fn post_organization_collection_delete(
|
||||
org_id: String,
|
||||
col_id: String,
|
||||
headers: AdminHeaders,
|
||||
_data: JsonUpcase<DeleteCollectionData>,
|
||||
conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
delete_organization_collection(org_id, col_id, headers, conn)
|
||||
}
|
||||
|
||||
@ -314,16 +364,18 @@ fn get_collection_users(org_id: String, coll_id: String, _headers: AdminHeaders,
|
||||
// Get org and collection, check that collection is from org
|
||||
let collection = match Collection::find_by_uuid_and_org(&coll_id, &org_id, &conn) {
|
||||
None => err!("Collection not found in Organization"),
|
||||
Some(collection) => collection
|
||||
Some(collection) => collection,
|
||||
};
|
||||
|
||||
// Get the users from collection
|
||||
let user_list: Vec<Value> = CollectionUser::find_by_collection(&collection.uuid, &conn)
|
||||
.iter().map(|col_user| {
|
||||
UserOrganization::find_by_user_and_org(&col_user.user_uuid, &org_id, &conn)
|
||||
.unwrap()
|
||||
.to_json_collection_user_details(col_user.read_only, &conn)
|
||||
}).collect();
|
||||
.iter()
|
||||
.map(|col_user| {
|
||||
UserOrganization::find_by_user_and_org(&col_user.user_uuid, &org_id, &conn)
|
||||
.unwrap()
|
||||
.to_json_collection_user_details(col_user.read_only, &conn)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Json(json!({
|
||||
"Data": user_list,
|
||||
@ -335,13 +387,16 @@ fn get_collection_users(org_id: String, coll_id: String, _headers: AdminHeaders,
|
||||
#[derive(FromForm)]
|
||||
struct OrgIdData {
|
||||
#[form(field = "organizationId")]
|
||||
organization_id: String
|
||||
organization_id: String,
|
||||
}
|
||||
|
||||
#[get("/ciphers/organization-details?<data..>")]
|
||||
fn get_org_details(data: Form<OrgIdData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
let ciphers = Cipher::find_by_org(&data.organization_id, &conn);
|
||||
let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
|
||||
let ciphers_json: Vec<Value> = ciphers
|
||||
.iter()
|
||||
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn))
|
||||
.collect();
|
||||
|
||||
Ok(Json(json!({
|
||||
"Data": ciphers_json,
|
||||
@ -367,7 +422,7 @@ where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
// Deserialize null to empty Vec
|
||||
Deserialize::deserialize(deserializer).or(Ok(vec![]))
|
||||
Deserialize::deserialize(deserializer).or(Ok(vec![]))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -393,11 +448,10 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
|
||||
|
||||
let new_type = match UserOrgType::from_str(&data.Type.into_string()) {
|
||||
Some(new_type) => new_type as i32,
|
||||
None => err!("Invalid type")
|
||||
None => err!("Invalid type"),
|
||||
};
|
||||
|
||||
if new_type != UserOrgType::User &&
|
||||
headers.org_user_type != UserOrgType::Owner {
|
||||
if new_type != UserOrgType::User && headers.org_user_type != UserOrgType::Owner {
|
||||
err!("Only Owners can invite Managers, Admins or Owners")
|
||||
}
|
||||
|
||||
@ -407,23 +461,26 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
|
||||
None => UserOrgStatus::Accepted as i32, // Automatically mark user as accepted if no email invites
|
||||
};
|
||||
let user = match User::find_by_mail(&email, &conn) {
|
||||
None => if CONFIG.invitations_allowed { // Invite user if that's enabled
|
||||
let mut invitation = Invitation::new(email.clone());
|
||||
invitation.save(&conn)?;
|
||||
let mut user = User::new(email.clone());
|
||||
user.save(&conn)?;
|
||||
user_org_status = UserOrgStatus::Invited as i32;
|
||||
user
|
||||
|
||||
} else {
|
||||
err!(format!("User email does not exist: {}", email))
|
||||
},
|
||||
Some(user) => if UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &conn).is_some() {
|
||||
err!(format!("User already in organization: {}", email))
|
||||
} else {
|
||||
user
|
||||
None => {
|
||||
if CONFIG.invitations_allowed {
|
||||
// Invite user if that's enabled
|
||||
let mut invitation = Invitation::new(email.clone());
|
||||
invitation.save(&conn)?;
|
||||
let mut user = User::new(email.clone());
|
||||
user.save(&conn)?;
|
||||
user_org_status = UserOrgStatus::Invited as i32;
|
||||
user
|
||||
} else {
|
||||
err!(format!("User email does not exist: {}", email))
|
||||
}
|
||||
}
|
||||
Some(user) => {
|
||||
if UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &conn).is_some() {
|
||||
err!(format!("User already in organization: {}", email))
|
||||
} else {
|
||||
user
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
let mut new_user = UserOrganization::new(user.uuid.clone(), org_id.clone());
|
||||
@ -449,9 +506,14 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
|
||||
if let Some(ref mail_config) = CONFIG.mail {
|
||||
let org_name = match Organization::find_by_uuid(&org_id, &conn) {
|
||||
Some(org) => org.name,
|
||||
None => err!("Error looking up organization")
|
||||
None => err!("Error looking up organization"),
|
||||
};
|
||||
let claims = generate_invite_claims(user.uuid.to_string(), user.email.clone(), org_id.clone(), Some(new_user.uuid.clone()));
|
||||
let claims = generate_invite_claims(
|
||||
user.uuid.to_string(),
|
||||
user.email.clone(),
|
||||
org_id.clone(),
|
||||
Some(new_user.uuid.clone()),
|
||||
);
|
||||
let invite_token = encode_jwt(&claims);
|
||||
mail::send_invite(&email, &org_id, &new_user.uuid, &invite_token, &org_name, mail_config)?;
|
||||
}
|
||||
@ -481,20 +543,32 @@ fn reinvite_user(org_id: String, user_org: String, _headers: AdminHeaders, conn:
|
||||
};
|
||||
|
||||
if Invitation::find_by_mail(&user.email, &conn).is_none() {
|
||||
err!("No invitation found for user to resend. Try inviting them first.")
|
||||
err!("No invitation found for user to resend. Try inviting them first.")
|
||||
}
|
||||
|
||||
let org_name = match Organization::find_by_uuid(&org_id, &conn) {
|
||||
Some(org) => org.name,
|
||||
None => err!("Error looking up organization.")
|
||||
None => err!("Error looking up organization."),
|
||||
};
|
||||
|
||||
let claims = generate_invite_claims(user.uuid.to_string(), user.email.clone(), org_id.clone(), Some(user_org.uuid.clone()));
|
||||
let claims = generate_invite_claims(
|
||||
user.uuid.to_string(),
|
||||
user.email.clone(),
|
||||
org_id.clone(),
|
||||
Some(user_org.uuid.clone()),
|
||||
);
|
||||
let invite_token = encode_jwt(&claims);
|
||||
if let Some(ref mail_config) = CONFIG.mail {
|
||||
mail::send_invite(&user.email, &org_id, &user_org.uuid, &invite_token, &org_name, mail_config)?;
|
||||
mail::send_invite(
|
||||
&user.email,
|
||||
&org_id,
|
||||
&user_org.uuid,
|
||||
&invite_token,
|
||||
&org_name,
|
||||
mail_config,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -519,7 +593,7 @@ fn generate_invite_claims(uuid: String, email: String, org_id: String, org_user_
|
||||
|
||||
#[post("/organizations/<_org_id>/users/<_org_user_id>/accept", data = "<data>")]
|
||||
fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase<AcceptData>, conn: DbConn) -> EmptyResult {
|
||||
// The web-vault passes org_id and org_user_id in the URL, but we are just reading them from the JWT instead
|
||||
// The web-vault passes org_id and org_user_id in the URL, but we are just reading them from the JWT instead
|
||||
let data: AcceptData = data.into_inner().data;
|
||||
let token = &data.Token;
|
||||
let claims: InviteJWTClaims = decode_invite_jwt(&token)?;
|
||||
@ -529,35 +603,39 @@ fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase<AcceptD
|
||||
Invitation::take(&claims.email, &conn);
|
||||
if claims.user_org_id.is_some() {
|
||||
// If this isn't the virtual_org, mark userorg as accepted
|
||||
let mut user_org = match UserOrganization::find_by_uuid_and_org(&claims.user_org_id.unwrap(), &claims.org_id, &conn) {
|
||||
Some(user_org) => user_org,
|
||||
None => err!("Error accepting the invitation")
|
||||
};
|
||||
let mut user_org =
|
||||
match UserOrganization::find_by_uuid_and_org(&claims.user_org_id.unwrap(), &claims.org_id, &conn) {
|
||||
Some(user_org) => user_org,
|
||||
None => err!("Error accepting the invitation"),
|
||||
};
|
||||
user_org.status = UserOrgStatus::Accepted as i32;
|
||||
if user_org.save(&conn).is_err() {
|
||||
err!("Failed to accept user to organization")
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
err!("Invited user not found")
|
||||
},
|
||||
}
|
||||
None => err!("Invited user not found"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>/users/<org_user_id>/confirm", data = "<data>")]
|
||||
fn confirm_invite(org_id: String, org_user_id: String, data: JsonUpcase<Value>, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
|
||||
fn confirm_invite(
|
||||
org_id: String,
|
||||
org_user_id: String,
|
||||
data: JsonUpcase<Value>,
|
||||
headers: AdminHeaders,
|
||||
conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
let data = data.into_inner().data;
|
||||
|
||||
let mut user_to_confirm = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &conn) {
|
||||
Some(user) => user,
|
||||
None => err!("The specified user isn't a member of the organization")
|
||||
None => err!("The specified user isn't a member of the organization"),
|
||||
};
|
||||
|
||||
if user_to_confirm.type_ != UserOrgType::User &&
|
||||
headers.org_user_type != UserOrgType::Owner {
|
||||
if user_to_confirm.type_ != UserOrgType::User && headers.org_user_type != UserOrgType::Owner {
|
||||
err!("Only Owners can confirm Managers, Admins or Owners")
|
||||
}
|
||||
|
||||
@ -568,7 +646,7 @@ fn confirm_invite(org_id: String, org_user_id: String, data: JsonUpcase<Value>,
|
||||
user_to_confirm.status = UserOrgStatus::Confirmed as i32;
|
||||
user_to_confirm.key = match data["Key"].as_str() {
|
||||
Some(key) => key.to_string(),
|
||||
None => err!("Invalid key provided")
|
||||
None => err!("Invalid key provided"),
|
||||
};
|
||||
|
||||
user_to_confirm.save(&conn)
|
||||
@ -578,7 +656,7 @@ fn confirm_invite(org_id: String, org_user_id: String, data: JsonUpcase<Value>,
|
||||
fn get_user(org_id: String, org_user_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
|
||||
let user = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &conn) {
|
||||
Some(user) => user,
|
||||
None => err!("The specified user isn't a member of the organization")
|
||||
None => err!("The specified user isn't a member of the organization"),
|
||||
};
|
||||
|
||||
Ok(Json(user.to_json_details(&conn)))
|
||||
@ -594,44 +672,50 @@ struct EditUserData {
|
||||
}
|
||||
|
||||
#[put("/organizations/<org_id>/users/<org_user_id>", data = "<data>", rank = 1)]
|
||||
fn put_organization_user(org_id: String, org_user_id: String, data: JsonUpcase<EditUserData>, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
|
||||
fn put_organization_user(
|
||||
org_id: String,
|
||||
org_user_id: String,
|
||||
data: JsonUpcase<EditUserData>,
|
||||
headers: AdminHeaders,
|
||||
conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
edit_user(org_id, org_user_id, data, headers, conn)
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>/users/<org_user_id>", data = "<data>", rank = 1)]
|
||||
fn edit_user(org_id: String, org_user_id: String, data: JsonUpcase<EditUserData>, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
|
||||
fn edit_user(
|
||||
org_id: String,
|
||||
org_user_id: String,
|
||||
data: JsonUpcase<EditUserData>,
|
||||
headers: AdminHeaders,
|
||||
conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
let data: EditUserData = data.into_inner().data;
|
||||
|
||||
let new_type = match UserOrgType::from_str(&data.Type.into_string()) {
|
||||
Some(new_type) => new_type,
|
||||
None => err!("Invalid type")
|
||||
None => err!("Invalid type"),
|
||||
};
|
||||
|
||||
let mut user_to_edit = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &conn) {
|
||||
Some(user) => user,
|
||||
None => err!("The specified user isn't member of the organization")
|
||||
None => err!("The specified user isn't member of the organization"),
|
||||
};
|
||||
|
||||
if new_type != user_to_edit.type_ && (
|
||||
user_to_edit.type_ >= UserOrgType::Admin ||
|
||||
new_type >= UserOrgType::Admin
|
||||
) &&
|
||||
headers.org_user_type != UserOrgType::Owner {
|
||||
if new_type != user_to_edit.type_
|
||||
&& (user_to_edit.type_ >= UserOrgType::Admin || new_type >= UserOrgType::Admin)
|
||||
&& headers.org_user_type != UserOrgType::Owner
|
||||
{
|
||||
err!("Only Owners can grant and remove Admin or Owner privileges")
|
||||
}
|
||||
|
||||
if user_to_edit.type_ == UserOrgType::Owner &&
|
||||
headers.org_user_type != UserOrgType::Owner {
|
||||
if user_to_edit.type_ == UserOrgType::Owner && headers.org_user_type != UserOrgType::Owner {
|
||||
err!("Only Owners can edit Owner users")
|
||||
}
|
||||
|
||||
if user_to_edit.type_ == UserOrgType::Owner &&
|
||||
new_type != UserOrgType::Owner {
|
||||
|
||||
if user_to_edit.type_ == UserOrgType::Owner && new_type != UserOrgType::Owner {
|
||||
// Removing owner permmission, check that there are at least another owner
|
||||
let num_owners = UserOrganization::find_by_org_and_type(
|
||||
&org_id, UserOrgType::Owner as i32, &conn)
|
||||
.len();
|
||||
let num_owners = UserOrganization::find_by_org_and_type(&org_id, UserOrgType::Owner as i32, &conn).len();
|
||||
|
||||
if num_owners <= 1 {
|
||||
err!("Can't delete the last owner")
|
||||
@ -665,19 +749,16 @@ fn edit_user(org_id: String, org_user_id: String, data: JsonUpcase<EditUserData>
|
||||
fn delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
|
||||
let user_to_delete = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &conn) {
|
||||
Some(user) => user,
|
||||
None => err!("User to delete isn't member of the organization")
|
||||
None => err!("User to delete isn't member of the organization"),
|
||||
};
|
||||
|
||||
if user_to_delete.type_ != UserOrgType::User &&
|
||||
headers.org_user_type != UserOrgType::Owner {
|
||||
if user_to_delete.type_ != UserOrgType::User && headers.org_user_type != UserOrgType::Owner {
|
||||
err!("Only Owners can delete Admins or Owners")
|
||||
}
|
||||
|
||||
if user_to_delete.type_ == UserOrgType::Owner {
|
||||
// Removing owner, check that there are at least another owner
|
||||
let num_owners = UserOrganization::find_by_org_and_type(
|
||||
&org_id, UserOrgType::Owner as i32, &conn)
|
||||
.len();
|
||||
let num_owners = UserOrganization::find_by_org_and_type(&org_id, UserOrgType::Owner as i32, &conn).len();
|
||||
|
||||
if num_owners <= 1 {
|
||||
err!("Can't delete the last owner")
|
||||
@ -692,8 +773,8 @@ fn post_delete_user(org_id: String, org_user_id: String, headers: AdminHeaders,
|
||||
delete_user(org_id, org_user_id, headers, conn)
|
||||
}
|
||||
|
||||
use super::ciphers::CipherData;
|
||||
use super::ciphers::update_cipher_from_data;
|
||||
use super::ciphers::CipherData;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
@ -713,13 +794,19 @@ struct RelationsData {
|
||||
}
|
||||
|
||||
#[post("/ciphers/import-organization?<query..>", data = "<data>")]
|
||||
fn post_org_import(query: Form<OrgIdData>, data: JsonUpcase<ImportData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
|
||||
fn post_org_import(
|
||||
query: Form<OrgIdData>,
|
||||
data: JsonUpcase<ImportData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
nt: Notify,
|
||||
) -> EmptyResult {
|
||||
let data: ImportData = data.into_inner().data;
|
||||
let org_id = query.into_inner().organization_id;
|
||||
|
||||
let org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) {
|
||||
Some(user) => user,
|
||||
None => err!("User is not part of the organization")
|
||||
None => err!("User is not part of the organization"),
|
||||
};
|
||||
|
||||
if org_user.type_ < UserOrgType::Admin {
|
||||
@ -727,14 +814,18 @@ fn post_org_import(query: Form<OrgIdData>, data: JsonUpcase<ImportData>, headers
|
||||
}
|
||||
|
||||
// Read and create the collections
|
||||
let collections: Vec<_> = data.Collections.into_iter().map(|coll| {
|
||||
let mut collection = Collection::new(org_id.clone(), coll.Name);
|
||||
if collection.save(&conn).is_err() {
|
||||
err!("Failed to create Collection");
|
||||
}
|
||||
|
||||
Ok(collection)
|
||||
}).collect();
|
||||
let collections: Vec<_> = data
|
||||
.Collections
|
||||
.into_iter()
|
||||
.map(|coll| {
|
||||
let mut collection = Collection::new(org_id.clone(), coll.Name);
|
||||
if collection.save(&conn).is_err() {
|
||||
err!("Failed to create Collection");
|
||||
}
|
||||
|
||||
Ok(collection)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Read the relations between collections and ciphers
|
||||
let mut relations = Vec::new();
|
||||
@ -743,11 +834,24 @@ fn post_org_import(query: Form<OrgIdData>, data: JsonUpcase<ImportData>, headers
|
||||
}
|
||||
|
||||
// Read and create the ciphers
|
||||
let ciphers: Vec<_> = data.Ciphers.into_iter().map(|cipher_data| {
|
||||
let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone());
|
||||
update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &conn, &ws, UpdateType::SyncCipherCreate).ok();
|
||||
cipher
|
||||
}).collect();
|
||||
let ciphers: Vec<_> = data
|
||||
.Ciphers
|
||||
.into_iter()
|
||||
.map(|cipher_data| {
|
||||
let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone());
|
||||
update_cipher_from_data(
|
||||
&mut cipher,
|
||||
cipher_data,
|
||||
&headers,
|
||||
false,
|
||||
&conn,
|
||||
&nt,
|
||||
UpdateType::CipherCreate,
|
||||
)
|
||||
.ok();
|
||||
cipher
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Assign the collections
|
||||
for (cipher_index, coll_index) in relations {
|
||||
@ -755,9 +859,9 @@ fn post_org_import(query: Form<OrgIdData>, data: JsonUpcase<ImportData>, headers
|
||||
let coll = &collections[coll_index];
|
||||
let coll_id = match coll {
|
||||
Ok(coll) => coll.uuid.as_str(),
|
||||
Err(_) => err!("Failed to assign to collection")
|
||||
Err(_) => err!("Failed to assign to collection"),
|
||||
};
|
||||
|
||||
|
||||
CollectionCipher::save(cipher_id, coll_id, &conn)?;
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ use rocket_contrib::json::Json;
|
||||
use serde_json;
|
||||
use serde_json::Value;
|
||||
|
||||
|
||||
use crate::db::{
|
||||
models::{TwoFactor, TwoFactorType, User},
|
||||
DbConn,
|
||||
@ -111,11 +110,7 @@ struct DisableTwoFactorData {
|
||||
}
|
||||
|
||||
#[post("/two-factor/disable", data = "<data>")]
|
||||
fn disable_twofactor(
|
||||
data: JsonUpcase<DisableTwoFactorData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
let data: DisableTwoFactorData = data.into_inner().data;
|
||||
let password_hash = data.MasterPasswordHash;
|
||||
|
||||
@ -137,20 +132,12 @@ fn disable_twofactor(
|
||||
}
|
||||
|
||||
#[put("/two-factor/disable", data = "<data>")]
|
||||
fn disable_twofactor_put(
|
||||
data: JsonUpcase<DisableTwoFactorData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
fn disable_twofactor_put(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
disable_twofactor(data, headers, conn)
|
||||
}
|
||||
|
||||
#[post("/two-factor/get-authenticator", data = "<data>")]
|
||||
fn generate_authenticator(
|
||||
data: JsonUpcase<PasswordData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
fn generate_authenticator(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
let data: PasswordData = data.into_inner().data;
|
||||
|
||||
if !headers.user.check_valid_password(&data.MasterPasswordHash) {
|
||||
@ -181,11 +168,7 @@ struct EnableAuthenticatorData {
|
||||
}
|
||||
|
||||
#[post("/two-factor/authenticator", data = "<data>")]
|
||||
fn activate_authenticator(
|
||||
data: JsonUpcase<EnableAuthenticatorData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
fn activate_authenticator(data: JsonUpcase<EnableAuthenticatorData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
let data: EnableAuthenticatorData = data.into_inner().data;
|
||||
let password_hash = data.MasterPasswordHash;
|
||||
let key = data.Key;
|
||||
@ -228,11 +211,7 @@ fn activate_authenticator(
|
||||
}
|
||||
|
||||
#[put("/two-factor/authenticator", data = "<data>")]
|
||||
fn activate_authenticator_put(
|
||||
data: JsonUpcase<EnableAuthenticatorData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
fn activate_authenticator_put(data: JsonUpcase<EnableAuthenticatorData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
activate_authenticator(data, headers, conn)
|
||||
}
|
||||
|
||||
@ -338,11 +317,8 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
|
||||
err!("Invalid password");
|
||||
}
|
||||
|
||||
let tf_challenge = TwoFactor::find_by_user_and_type(
|
||||
&headers.user.uuid,
|
||||
TwoFactorType::U2fRegisterChallenge as i32,
|
||||
&conn,
|
||||
);
|
||||
let tf_challenge =
|
||||
TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::U2fRegisterChallenge as i32, &conn);
|
||||
|
||||
if let Some(tf_challenge) = tf_challenge {
|
||||
let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
|
||||
@ -394,17 +370,14 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
|
||||
|
||||
#[put("/two-factor/u2f", data = "<data>")]
|
||||
fn activate_u2f_put(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
activate_u2f(data,headers, conn)
|
||||
activate_u2f(data, headers, conn)
|
||||
}
|
||||
|
||||
fn _create_u2f_challenge(user_uuid: &str, type_: TwoFactorType, conn: &DbConn) -> Challenge {
|
||||
let challenge = U2F.generate_challenge().unwrap();
|
||||
|
||||
TwoFactor::new(
|
||||
user_uuid.into(),
|
||||
type_,
|
||||
serde_json::to_string(&challenge).unwrap(),
|
||||
).save(conn)
|
||||
TwoFactor::new(user_uuid.into(), type_, serde_json::to_string(&challenge).unwrap())
|
||||
.save(conn)
|
||||
.expect("Error saving challenge");
|
||||
|
||||
challenge
|
||||
@ -478,8 +451,7 @@ pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> Emp
|
||||
|
||||
let mut _counter: u32 = 0;
|
||||
for registration in registrations {
|
||||
let response =
|
||||
U2F.sign_response(challenge.clone(), registration, response.clone(), _counter);
|
||||
let response = U2F.sign_response(challenge.clone(), registration, response.clone(), _counter);
|
||||
match response {
|
||||
Ok(new_counter) => {
|
||||
_counter = new_counter;
|
||||
@ -495,7 +467,6 @@ pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> Emp
|
||||
err!("error verifying response")
|
||||
}
|
||||
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
struct EnableYubikeyData {
|
||||
@ -515,8 +486,8 @@ pub struct YubikeyMetadata {
|
||||
pub Nfc: bool,
|
||||
}
|
||||
|
||||
use yubico::Yubico;
|
||||
use yubico::config::Config;
|
||||
use yubico::Yubico;
|
||||
|
||||
fn parse_yubikeys(data: &EnableYubikeyData) -> Vec<String> {
|
||||
let mut yubikeys: Vec<String> = Vec::new();
|
||||
@ -548,7 +519,7 @@ fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value {
|
||||
let mut result = json!({});
|
||||
|
||||
for (i, key) in yubikeys.into_iter().enumerate() {
|
||||
result[format!("Key{}", i+1)] = Value::String(key);
|
||||
result[format!("Key{}", i + 1)] = Value::String(key);
|
||||
}
|
||||
|
||||
result
|
||||
@ -556,16 +527,17 @@ fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value {
|
||||
|
||||
fn verify_yubikey_otp(otp: String) -> JsonResult {
|
||||
if !CONFIG.yubico_cred_set {
|
||||
err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. \
|
||||
Yubikey OTP Disabled")
|
||||
err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled")
|
||||
}
|
||||
|
||||
let yubico = Yubico::new();
|
||||
let config = Config::default().set_client_id(CONFIG.yubico_client_id.to_owned()).set_key(CONFIG.yubico_secret_key.to_owned());
|
||||
let config = Config::default()
|
||||
.set_client_id(CONFIG.yubico_client_id.to_owned())
|
||||
.set_key(CONFIG.yubico_secret_key.to_owned());
|
||||
|
||||
let result = match CONFIG.yubico_server {
|
||||
Some(ref server) => yubico.verify(otp, config.set_api_hosts(vec![server.to_owned()])),
|
||||
None => yubico.verify(otp, config)
|
||||
None => yubico.verify(otp, config),
|
||||
};
|
||||
|
||||
match result {
|
||||
@ -577,8 +549,7 @@ fn verify_yubikey_otp(otp: String) -> JsonResult {
|
||||
#[post("/two-factor/get-yubikey", data = "<data>")]
|
||||
fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
if !CONFIG.yubico_cred_set {
|
||||
err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. \
|
||||
Yubikey OTP Disabled")
|
||||
err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled")
|
||||
}
|
||||
|
||||
let data: PasswordData = data.into_inner().data;
|
||||
@ -619,11 +590,7 @@ fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn:
|
||||
}
|
||||
|
||||
// Check if we already have some data
|
||||
let yubikey_data = TwoFactor::find_by_user_and_type(
|
||||
&headers.user.uuid,
|
||||
TwoFactorType::YubiKey as i32,
|
||||
&conn,
|
||||
);
|
||||
let yubikey_data = TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::YubiKey as i32, &conn);
|
||||
|
||||
if let Some(yubikey_data) = yubikey_data {
|
||||
yubikey_data.delete(&conn)?;
|
||||
@ -642,7 +609,7 @@ fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn:
|
||||
for yubikey in &yubikeys {
|
||||
if yubikey.len() == 12 {
|
||||
// YubiKey ID
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
let result = verify_yubikey_otp(yubikey.to_owned());
|
||||
@ -692,7 +659,8 @@ pub fn validate_yubikey_login(user_uuid: &str, response: &str, conn: &DbConn) ->
|
||||
None => err!("No YubiKey devices registered"),
|
||||
};
|
||||
|
||||
let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&twofactor.data).expect("Can't parse Yubikey Metadata");
|
||||
let yubikey_metadata: YubikeyMetadata =
|
||||
serde_json::from_str(&twofactor.data).expect("Can't parse Yubikey Metadata");
|
||||
let response_id = &response[..12];
|
||||
|
||||
if !yubikey_metadata.Keys.contains(&response_id.to_owned()) {
|
||||
|
Reference in New Issue
Block a user