#![allow(unused_imports)] use rocket_contrib::{Json, Value}; use db::DbConn; use db::models::*; use api::{PasswordData, JsonResult, EmptyResult, NumberOrString, JsonUpcase}; use auth::{Headers, AdminHeaders, OwnerHeaders}; #[derive(Deserialize)] #[allow(non_snake_case)] struct OrgData { BillingEmail: String, CollectionName: String, Key: String, Name: String, #[serde(rename = "PlanType")] _PlanType: String, // Ignored, always use the same plan } #[derive(Deserialize, Debug)] #[allow(non_snake_case)] struct OrganizationUpdateData { BillingEmail: String, Name: String, } #[derive(Deserialize, Debug)] #[allow(non_snake_case)] struct NewCollectionData { Name: String, } #[post("/organizations", data = "")] fn create_organization(headers: Headers, data: JsonUpcase, conn: DbConn) -> JsonResult { 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); user_org.key = data.Key; user_org.access_all = true; user_org.type_ = UserOrgType::Owner as i32; user_org.status = UserOrgStatus::Confirmed as i32; org.save(&conn); user_org.save(&conn); collection.save(&conn); Ok(Json(org.to_json())) } #[post("/organizations//delete", data = "")] fn delete_organization(org_id: String, data: JsonUpcase, headers: OwnerHeaders, conn: DbConn) -> EmptyResult { let data: PasswordData = data.into_inner().data; let password_hash = data.MasterPasswordHash; if !headers.user.check_valid_password(&password_hash) { err!("Invalid password") } match Organization::find_by_uuid(&org_id, &conn) { None => err!("Organization not found"), Some(org) => match org.delete(&conn) { Ok(()) => Ok(()), Err(_) => err!("Failed deleting the organization") } } } #[get("/organizations/")] 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") } } #[post("/organizations/", data = "")] fn post_organization(org_id: String, _headers: OwnerHeaders, data: JsonUpcase, 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") }; org.name = data.Name; org.billing_email = data.BillingEmail; org.save(&conn); Ok(Json(org.to_json())) } // 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) .iter() .map(|collection| { collection.to_json() }).collect::(), "Object": "list" }))) } #[get("/organizations//collections")] fn get_org_collections(org_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult { Ok(Json(json!({ "Data": Collection::find_by_organization(&org_id, &conn) .iter() .map(|collection| { collection.to_json() }).collect::(), "Object": "list" }))) } #[post("/organizations//collections", data = "")] fn post_organization_collections(org_id: String, _headers: AdminHeaders, data: JsonUpcase, 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") }; let mut collection = Collection::new(org.uuid.clone(), data.Name); collection.save(&conn); Ok(Json(collection.to_json())) } #[post("/organizations//collections/", data = "")] fn post_organization_collection_update(org_id: String, col_id: String, _headers: AdminHeaders, data: JsonUpcase, 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") }; let mut collection = match Collection::find_by_uuid(&col_id, &conn) { Some(collection) => collection, None => err!("Collection not found") }; if collection.org_uuid != org.uuid { err!("Collection is not owned by organization"); } collection.name = data.Name.clone(); collection.save(&conn); Ok(Json(collection.to_json())) } #[post("/organizations//collections//delete-user/")] fn post_organization_collection_delete_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") } }; match UserOrganization::find_by_uuid(&org_user_id, &conn) { None => err!("User not found in organization"), 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) => { match col_user.delete(&conn) { Ok(()) => Ok(()), Err(_) => err!("Failed removing user from collection") } } } } } } #[derive(Deserialize, Debug)] #[allow(non_snake_case)] struct DeleteCollectionData { Id: String, OrgId: String, } #[post("/organizations//collections//delete", data = "")] fn post_organization_collection_delete(org_id: String, col_id: String, _headers: AdminHeaders, data: JsonUpcase, conn: DbConn) -> EmptyResult { let _data: DeleteCollectionData = data.into_inner().data; match Collection::find_by_uuid(&col_id, &conn) { None => err!("Collection not found"), Some(collection) => if collection.org_uuid == org_id { match collection.delete(&conn) { Ok(()) => Ok(()), Err(_) => err!("Failed deleting collection") } } else { err!("Collection and Organization id do not match") } } } #[get("/organizations//collections//details")] fn get_org_collection_detail(org_id: String, coll_id: String, headers: AdminHeaders, conn: DbConn) -> JsonResult { match Collection::find_by_uuid_and_user(&coll_id, &headers.user.uuid, &conn) { None => err!("Collection not found"), Some(collection) => { if collection.org_uuid != org_id { err!("Collection is not owned by organization") } Ok(Json(collection.to_json())) } } } #[get("/organizations//collections//users")] fn get_collection_users(org_id: String, coll_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult { // 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 }; // Get the users from collection let user_list: Vec = 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(); Ok(Json(json!({ "Data": user_list, "Object": "list" }))) } #[derive(FromForm)] #[allow(non_snake_case)] struct OrgIdData { organizationId: String } #[get("/ciphers/organization-details?")] fn get_org_details(data: OrgIdData, headers: Headers, conn: DbConn) -> JsonResult { let ciphers = Cipher::find_by_org(&data.organizationId, &conn); let ciphers_json: Vec = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect(); Ok(Json(json!({ "Data": ciphers_json, "Object": "list", }))) } #[get("/organizations//users")] fn get_org_users(org_id: String, headers: AdminHeaders, conn: DbConn) -> JsonResult { match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) { Some(_) => (), None => err!("User isn't member of organization") } let users = UserOrganization::find_by_org(&org_id, &conn); let users_json: Vec = users.iter().map(|c| c.to_json_user_details(&conn)).collect(); Ok(Json(json!({ "Data": users_json, "Object": "list" }))) } #[derive(Deserialize)] #[allow(non_snake_case)] struct CollectionData { id: String, readOnly: bool, } #[derive(Deserialize)] #[allow(non_snake_case)] struct InviteData { Emails: Vec, Type: NumberOrString, Collections: Vec, AccessAll: Option, } #[post("/organizations//users/invite", data = "")] fn send_invite(org_id: String, data: JsonUpcase, headers: AdminHeaders, conn: DbConn) -> EmptyResult { let data: InviteData = data.into_inner().data; let new_type = match UserOrgType::from_str(&data.Type.into_string()) { Some(new_type) => new_type as i32, None => err!("Invalid type") }; if new_type != UserOrgType::User as i32 && headers.org_user_type != UserOrgType::Owner as i32 { err!("Only Owners can invite Admins or Owners") } for user_opt in data.Emails.iter().map(|email| User::find_by_mail(email, &conn)) { match user_opt { None => err!("User email does not exist"), Some(user) => { if UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &conn).is_some() { err!("User already in organization") } let mut new_user = UserOrganization::new(user.uuid.clone(), org_id.clone()); let access_all = data.AccessAll.unwrap_or(false); new_user.access_all = access_all; new_user.type_ = new_type; // If no accessAll, add the collections received if !access_all { for col in &data.Collections { match Collection::find_by_uuid_and_org(&col.id, &org_id, &conn) { None => err!("Collection not found in Organization"), Some(collection) => { if CollectionUser::save(&user.uuid, &collection.uuid, col.readOnly, &conn).is_err() { err!("Failed saving collection access for user") } } } } } new_user.save(&conn); } } } Ok(()) } #[post("/organizations//users//confirm", data = "")] fn confirm_invite(org_id: String, user_id: String, data: JsonUpcase, headers: AdminHeaders, conn: DbConn) -> EmptyResult { let data = data.into_inner().data; let mut user_to_confirm = match UserOrganization::find_by_uuid(&user_id, &conn) { Some(user) => user, None => err!("Failed to find user membership") }; if user_to_confirm.org_uuid != org_id { err!("The specified user isn't a member of the organization") } if user_to_confirm.type_ != UserOrgType::User as i32 && headers.org_user_type != UserOrgType::Owner as i32 { err!("Only Owners can confirm Admins or Owners") } if user_to_confirm.status != UserOrgStatus::Accepted as i32 { err!("User in invalid state") } 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") }; user_to_confirm.save(&conn); Ok(()) } #[get("/organizations//users/")] fn get_user(org_id: String, user_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult { let user = match UserOrganization::find_by_uuid(&user_id, &conn) { Some(user) => user, None => err!("Failed to find user membership") }; if user.org_uuid != org_id { err!("The specified user isn't a member of the organization") } Ok(Json(user.to_json_details(&conn))) } #[derive(Deserialize)] #[allow(non_snake_case)] struct EditUserData { Type: NumberOrString, Collections: Vec, AccessAll: bool, } #[post("/organizations//users/", data = "", rank = 1)] fn edit_user(org_id: String, user_id: String, data: JsonUpcase, 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 as i32, None => err!("Invalid type") }; let mut user_to_edit = match UserOrganization::find_by_uuid(&user_id, &conn) { Some(user) => user, None => err!("The specified user isn't member of the organization") }; if new_type != UserOrgType::User as i32 && headers.org_user_type != UserOrgType::Owner as i32 { err!("Only Owners can grant Admin or Owner type") } if user_to_edit.type_ != UserOrgType::User as i32 && headers.org_user_type != UserOrgType::Owner as i32 { err!("Only Owners can edit Admin or Owner") } if user_to_edit.type_ == UserOrgType::Owner as i32 && new_type != UserOrgType::Owner as i32 { // 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(); if num_owners <= 1 { err!("Can't delete the last owner") } } user_to_edit.access_all = data.AccessAll; user_to_edit.type_ = new_type; // Delete all the odd collections for c in CollectionUser::find_by_organization_and_user_uuid(&org_id, &user_to_edit.user_uuid, &conn) { if c.delete(&conn).is_err() { err!("Failed deleting old collection assignment") } } // If no accessAll, add the collections received if !data.AccessAll { for col in &data.Collections { match Collection::find_by_uuid_and_org(&col.id, &org_id, &conn) { None => err!("Collection not found in Organization"), Some(collection) => { if CollectionUser::save(&user_to_edit.user_uuid, &collection.uuid, col.readOnly, &conn).is_err() { err!("Failed saving collection access for user") } } } } } user_to_edit.save(&conn); Ok(()) } #[post("/organizations//users//delete")] fn delete_user(org_id: String, user_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult { let user_to_delete = match UserOrganization::find_by_uuid(&user_id, &conn) { Some(user) => user, None => err!("User to delete isn't member of the organization") }; if user_to_delete.type_ != UserOrgType::User as i32 && headers.org_user_type != UserOrgType::Owner as i32 { err!("Only Owners can delete Admins or Owners") } if user_to_delete.type_ == UserOrgType::Owner as i32 { // 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(); if num_owners <= 1 { err!("Can't delete the last owner") } } match user_to_delete.delete(&conn) { Ok(()) => Ok(()), Err(_) => err!("Failed deleting user from organization") } }