From 0cb58add54a85993ba9ff3665df170dfce04972e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Fri, 4 May 2018 19:25:50 +0200 Subject: [PATCH] Implemented some admin methods, inserted CollectionsUsers only when Org accessAll == false, and implemented find_collection when user has access_all in Org --- src/api/core/ciphers.rs | 17 +++++++- src/api/core/mod.rs | 2 + src/api/core/organizations.rs | 78 ++++++++++++++++++++--------------- src/api/mod.rs | 16 +++++++ src/db/models/collection.rs | 59 +++++++++++++------------- 5 files changed, 108 insertions(+), 64 deletions(-) diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index 9aea77ae..8b22b577 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -69,6 +69,12 @@ fn get_cipher(uuid: String, headers: Headers, conn: DbConn) -> JsonResult { Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) } +#[get("/ciphers//admin")] +fn get_cipher_admin(uuid: String, headers: Headers, conn: DbConn) -> JsonResult { + // TODO: Implement this correctly + get_cipher(uuid, headers, conn) +} + #[derive(Deserialize, Debug)] #[allow(non_snake_case)] struct CipherData { @@ -100,6 +106,7 @@ struct CipherData { #[post("/ciphers/admin", data = "")] fn post_ciphers_admin(data: Json, headers: Headers, conn: DbConn) -> JsonResult { + // TODO: Implement this correctly post_ciphers(data, headers, conn) } @@ -230,8 +237,8 @@ fn post_ciphers_import(data: Json, headers: Headers, conn: DbConn) - let data: ImportData = data.into_inner(); // Read and create the folders - let folders: Vec<_> = data.folders.iter().map(|folder| { - let mut folder = Folder::new(headers.user.uuid.clone(), folder.name.clone()); + let folders: Vec<_> = data.folders.into_iter().map(|folder| { + let mut folder = Folder::new(headers.user.uuid.clone(), folder.name); folder.save(&conn); folder }).collect(); @@ -261,6 +268,12 @@ fn post_ciphers_import(data: Json, headers: Headers, conn: DbConn) - Ok(()) } +#[post("/ciphers//admin", data = "")] +fn post_cipher_admin(uuid: String, data: Json, headers: Headers, conn: DbConn) -> JsonResult { + // TODO: Implement this correctly + post_cipher(uuid, data, headers, conn) +} + #[post("/ciphers/", data = "")] fn post_cipher(uuid: String, data: Json, headers: Headers, conn: DbConn) -> JsonResult { put_cipher(uuid, data, headers, conn) diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 75d2fa95..f9c8839e 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -26,12 +26,14 @@ pub fn routes() -> Vec { get_ciphers, get_cipher, + get_cipher_admin, post_ciphers, post_ciphers_admin, post_ciphers_import, post_attachment, delete_attachment_post, delete_attachment, + post_cipher_admin, post_cipher, put_cipher, delete_cipher_post, diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index f719c8e2..811863bb 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -5,7 +5,7 @@ use rocket_contrib::{Json, Value}; use db::DbConn; use db::models::*; -use api::{PasswordData, JsonResult, EmptyResult}; +use api::{PasswordData, JsonResult, EmptyResult, NumberOrString}; use auth::Headers; @@ -41,10 +41,6 @@ fn create_organization(headers: Headers, data: Json, conn: DbConn) -> J headers.user.uuid.clone(), org.uuid.clone()); let mut collection = Collection::new( org.uuid.clone(), data.collectionName); - let mut collection_user = CollectionUsers::new( - headers.user.uuid.clone(), - collection.uuid.clone(), - ); user_org.key = data.key; user_org.access_all = true; @@ -54,7 +50,6 @@ fn create_organization(headers: Headers, data: Json, conn: DbConn) -> J org.save(&conn); user_org.save(&conn); collection.save(&conn); - collection_user.save(&conn); Ok(Json(org.to_json())) } @@ -64,6 +59,8 @@ fn delete_organization(org_id: String, data: Json, headers: Header let data: PasswordData = data.into_inner(); let password_hash = data.masterPasswordHash; + // TODO: Delete ciphers from organization, collection_users, collections, organization_users and the org itself + unimplemented!() } @@ -109,7 +106,6 @@ fn get_user_collections(headers: Headers, conn: DbConn) -> JsonResult { Ok(Json(json!({ "Data": Collection::find_by_user_uuid(&headers.user.uuid, &conn) - .expect("Error loading collections") .iter() .map(|collection| { collection.to_json() @@ -123,7 +119,6 @@ fn get_org_collections(org_id: String, headers: Headers, conn: DbConn) -> JsonRe Ok(Json(json!({ "Data": Collection::find_by_organization_and_user_uuid(&org_id, &headers.user.uuid, &conn) - .unwrap_or(vec![]) .iter() .map(|collection| { collection.to_json() @@ -136,10 +131,12 @@ fn get_org_collections(org_id: String, headers: Headers, conn: DbConn) -> JsonRe fn post_organization_collections(org_id: String, headers: Headers, data: Json, conn: DbConn) -> JsonResult { let data: NewCollectionData = data.into_inner(); - match UserOrganization::find_by_user_and_org( &headers.user.uuid, &org_id, &conn) { + let org_user = match UserOrganization::find_by_user_and_org( &headers.user.uuid, &org_id, &conn) { None => err!("User not in Organization or Organization doesn't exist"), - Some(org_user) => if org_user.type_ > 1 { // not owner or admin + Some(org_user) => if org_user.type_ == UserOrgType::User as i32 { err!("Only Organization owner and admin can add Collection") + } else { + org_user } }; @@ -149,13 +146,12 @@ fn post_organization_collections(org_id: String, headers: Headers, data: Json, #[serde(rename = "type")] - type_: String, + type_: NumberOrString, collections: Vec, accessAll: bool, } @@ -285,7 +281,7 @@ fn send_invite(org_id: String, data: Json, headers: Headers, conn: D err!("Users can't invite other people. Ask an Admin or Owner") } - let new_type = match UserOrgType::from_str(data.type_.as_ref()) { + let new_type = match UserOrgType::from_str(&data.type_.to_string()) { Some(new_type) => new_type as i32, None => err!("Invalid type") }; @@ -304,18 +300,21 @@ fn send_invite(org_id: String, data: Json, headers: Headers, conn: D None => () } - let mut new_user = UserOrganization::new( - user.uuid, org_id.clone()); - - if data.accessAll { - new_user.access_all = data.accessAll; - } else { - err!("Select collections unimplemented") - // TODO create Users_collections - } - + let mut new_user = UserOrganization::new(user.uuid, org_id.clone()); + + new_user.access_all = data.accessAll; new_user.type_ = new_type; + // If no accessAll, add the collections received + if !data.accessAll { + for collection in data.collections.iter() { + // TODO: Check that collection is in org + // TODO: Save the readOnly bit + + CollectionUsers::save(&headers.user.uuid, &collection.id, &conn); + } + } + new_user.save(&conn); } } @@ -381,7 +380,7 @@ fn get_user(org_id: String, user_id: String, headers: Headers, conn: DbConn) -> #[allow(non_snake_case)] struct EditUserData { #[serde(rename = "type")] - type_: String, + type_: NumberOrString, collections: Vec, accessAll: bool, } @@ -396,7 +395,7 @@ fn edit_user(org_id: String, user_id: String, data: Json, headers: None => err!("The current user isn't member of the organization") }; - let new_type = match UserOrgType::from_str(data.type_.as_ref()) { + let new_type = match UserOrgType::from_str(&data.type_.to_string()) { Some(new_type) => new_type as i32, None => err!("Invalid type") }; @@ -436,10 +435,19 @@ fn edit_user(org_id: String, user_id: String, data: Json, headers: user_to_edit.access_all = data.accessAll; user_to_edit.type_ = new_type; - if data.accessAll { - // Remove users_collections if there is any - } else { - // TODO create users_collections + // Delete all the odd collections + for c in Collection::find_by_organization_and_user_uuid(&org_id, ¤t_user.uuid, &conn) { + CollectionUsers::delete(¤t_user.uuid, &c.uuid, &conn); + } + + // If no accessAll, add the collections received + if !data.accessAll { + for collection in data.collections.iter() { + // TODO: Check that collection is in org + // TODO: Save the readOnly bit + + CollectionUsers::save(¤t_user.uuid, &collection.id, &conn); + } } user_to_edit.save(&conn); @@ -482,7 +490,9 @@ fn delete_user(org_id: String, user_id: String, headers: Headers, conn: DbConn) user_to_delete.delete(&conn); - // TODO Delete users_collections from this org + for c in Collection::find_by_organization_and_user_uuid(&org_id, ¤t_user.uuid, &conn) { + CollectionUsers::delete(¤t_user.uuid, &c.uuid, &conn); + } Ok(()) } \ No newline at end of file diff --git a/src/api/mod.rs b/src/api/mod.rs index 09d7265b..7d9e63d5 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -21,3 +21,19 @@ type EmptyResult = Result<(), BadRequest>; struct PasswordData { masterPasswordHash: String } + +#[derive(Deserialize)] +#[serde(untagged)] +enum NumberOrString { + Number(i32), + String(String), +} + +impl NumberOrString { + fn to_string(self) -> String { + match self { + NumberOrString::Number(n) => n.to_string(), + NumberOrString::String(s) => s + } + } +} diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs index 08e03772..61c11b12 100644 --- a/src/db/models/collection.rs +++ b/src/db/models/collection.rs @@ -38,7 +38,7 @@ impl Collection { use diesel; use diesel::prelude::*; use db::DbConn; -use db::schema::collections; +use db::schema::*; /// Database methods impl Collection { @@ -66,19 +66,25 @@ impl Collection { .first::(&**conn).ok() } - pub fn find_by_user_uuid(user_uuid: &str, conn: &DbConn) -> Option> { - users_collections::table.inner_join(collections::table) + pub fn find_by_user_uuid(user_uuid: &str, conn: &DbConn) -> Vec { + let mut all_access_collections = users_organizations::table + .filter(users_organizations::user_uuid.eq(user_uuid)) + .filter(users_organizations::access_all.eq(true)) + .inner_join(collections::table.on(collections::org_uuid.eq(users_organizations::org_uuid))) + .select(collections::all_columns) + .load::(&**conn).expect("Error loading collections"); + + let mut assigned_collections = users_collections::table.inner_join(collections::table) .filter(users_collections::user_uuid.eq(user_uuid)) .select(collections::all_columns) - .load::(&**conn).ok() + .load::(&**conn).expect("Error loading collections"); + + all_access_collections.append(&mut assigned_collections); + all_access_collections } - pub fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Option> { - users_collections::table.inner_join(collections::table) - .filter(users_collections::user_uuid.eq(user_uuid)) - .filter(collections::org_uuid.eq(org_uuid)) - .select(collections::all_columns) - .load::(&**conn).ok() + pub fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Vec { + Self::find_by_user_uuid(user_uuid, conn).into_iter().filter(|c| c.org_uuid == org_uuid).collect() } pub fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option { @@ -102,29 +108,26 @@ pub struct CollectionUsers { pub collection_uuid: String, } -/// Local methods -impl CollectionUsers { - pub fn new( - user_uuid: String, - collection_uuid: String, - ) -> Self { - Self { - user_uuid, - collection_uuid, - } - } -} - -use db::schema::users_collections; - /// Database methods impl CollectionUsers { - pub fn save(&mut self, conn: &DbConn) -> bool { + pub fn save(user_uuid: &str, collection_uuid: &str, conn: &DbConn) -> bool { match diesel::replace_into(users_collections::table) - .values(&*self) - .execute(&**conn) { + .values(( + users_collections::user_uuid.eq(user_uuid), + users_collections::collection_uuid.eq(collection_uuid) + )).execute(&**conn) { Ok(1) => true, // One row inserted _ => false, } } + + pub fn delete(user_uuid: &str, collection_uuid: &str, conn: &DbConn) -> bool { + match diesel::delete(users_collections::table + .filter(users_collections::user_uuid.eq(user_uuid)) + .filter(users_collections::collection_uuid.eq(collection_uuid))) + .execute(&**conn) { + Ok(1) => true, // One row deleted + _ => false, + } + } } \ No newline at end of file