ciphers.rs 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377
  1. use std::collections::{HashMap, HashSet};
  2. use chrono::{NaiveDateTime, Utc};
  3. use rocket::fs::TempFile;
  4. use rocket::serde::json::Json;
  5. use rocket::{
  6. form::{Form, FromForm},
  7. Route,
  8. };
  9. use serde_json::Value;
  10. use crate::{
  11. api::{self, EmptyResult, JsonResult, JsonUpcase, Notify, PasswordData, UpdateType},
  12. auth::Headers,
  13. crypto,
  14. db::{models::*, DbConn, DbPool},
  15. CONFIG,
  16. };
  17. pub fn routes() -> Vec<Route> {
  18. // Note that many routes have an `admin` variant; this seems to be
  19. // because the stored procedure that upstream Bitwarden uses to determine
  20. // whether the user can edit a cipher doesn't take into account whether
  21. // the user is an org owner/admin. The `admin` variant first checks
  22. // whether the user is an owner/admin of the relevant org, and if so,
  23. // allows the operation unconditionally.
  24. //
  25. // vaultwarden factors in the org owner/admin status as part of
  26. // determining the write accessibility of a cipher, so most
  27. // admin/non-admin implementations can be shared.
  28. routes![
  29. sync,
  30. get_ciphers,
  31. get_cipher,
  32. get_cipher_admin,
  33. get_cipher_details,
  34. post_ciphers,
  35. put_cipher_admin,
  36. post_ciphers_admin,
  37. post_ciphers_create,
  38. post_ciphers_import,
  39. get_attachment,
  40. post_attachment_v2,
  41. post_attachment_v2_data,
  42. post_attachment, // legacy
  43. post_attachment_admin, // legacy
  44. post_attachment_share,
  45. delete_attachment_post,
  46. delete_attachment_post_admin,
  47. delete_attachment,
  48. delete_attachment_admin,
  49. post_cipher_admin,
  50. post_cipher_share,
  51. put_cipher_share,
  52. put_cipher_share_selected,
  53. post_cipher,
  54. put_cipher,
  55. delete_cipher_post,
  56. delete_cipher_post_admin,
  57. delete_cipher_put,
  58. delete_cipher_put_admin,
  59. delete_cipher,
  60. delete_cipher_admin,
  61. delete_cipher_selected,
  62. delete_cipher_selected_post,
  63. delete_cipher_selected_put,
  64. delete_cipher_selected_admin,
  65. delete_cipher_selected_post_admin,
  66. delete_cipher_selected_put_admin,
  67. restore_cipher_put,
  68. restore_cipher_put_admin,
  69. restore_cipher_selected,
  70. delete_all,
  71. move_cipher_selected,
  72. move_cipher_selected_put,
  73. put_collections_update,
  74. post_collections_update,
  75. post_collections_admin,
  76. put_collections_admin,
  77. ]
  78. }
  79. pub async fn purge_trashed_ciphers(pool: DbPool) {
  80. debug!("Purging trashed ciphers");
  81. if let Ok(conn) = pool.get().await {
  82. Cipher::purge_trash(&conn);
  83. } else {
  84. error!("Failed to get DB connection while purging trashed ciphers")
  85. }
  86. }
  87. #[derive(FromForm, Default)]
  88. struct SyncData {
  89. #[field(name = "excludeDomains")]
  90. exclude_domains: bool, // Default: 'false'
  91. }
  92. #[get("/sync?<data..>")]
  93. fn sync(data: SyncData, headers: Headers, conn: DbConn) -> Json<Value> {
  94. let user_json = headers.user.to_json(&conn);
  95. let folders = Folder::find_by_user(&headers.user.uuid, &conn);
  96. let folders_json: Vec<Value> = folders.iter().map(Folder::to_json).collect();
  97. let collections = Collection::find_by_user_uuid(&headers.user.uuid, &conn);
  98. let collections_json: Vec<Value> =
  99. collections.iter().map(|c| c.to_json_details(&headers.user.uuid, &conn)).collect();
  100. let policies = OrgPolicy::find_confirmed_by_user(&headers.user.uuid, &conn);
  101. let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
  102. let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn);
  103. let ciphers_json: Vec<Value> =
  104. ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
  105. let sends = Send::find_by_user(&headers.user.uuid, &conn);
  106. let sends_json: Vec<Value> = sends.iter().map(|s| s.to_json()).collect();
  107. let domains_json = if data.exclude_domains {
  108. Value::Null
  109. } else {
  110. api::core::_get_eq_domains(headers, true).into_inner()
  111. };
  112. Json(json!({
  113. "Profile": user_json,
  114. "Folders": folders_json,
  115. "Collections": collections_json,
  116. "Policies": policies_json,
  117. "Ciphers": ciphers_json,
  118. "Domains": domains_json,
  119. "Sends": sends_json,
  120. "unofficialServer": true,
  121. "Object": "sync"
  122. }))
  123. }
  124. #[get("/ciphers")]
  125. fn get_ciphers(headers: Headers, conn: DbConn) -> Json<Value> {
  126. let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn);
  127. let ciphers_json: Vec<Value> =
  128. ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
  129. Json(json!({
  130. "Data": ciphers_json,
  131. "Object": "list",
  132. "ContinuationToken": null
  133. }))
  134. }
  135. #[get("/ciphers/<uuid>")]
  136. fn get_cipher(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
  137. let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
  138. Some(cipher) => cipher,
  139. None => err!("Cipher doesn't exist"),
  140. };
  141. if !cipher.is_accessible_to_user(&headers.user.uuid, &conn) {
  142. err!("Cipher is not owned by user")
  143. }
  144. Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
  145. }
  146. #[get("/ciphers/<uuid>/admin")]
  147. fn get_cipher_admin(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
  148. // TODO: Implement this correctly
  149. get_cipher(uuid, headers, conn)
  150. }
  151. #[get("/ciphers/<uuid>/details")]
  152. fn get_cipher_details(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
  153. get_cipher(uuid, headers, conn)
  154. }
  155. #[derive(Deserialize, Debug)]
  156. #[allow(non_snake_case)]
  157. pub struct CipherData {
  158. // Id is optional as it is included only in bulk share
  159. pub Id: Option<String>,
  160. // Folder id is not included in import
  161. FolderId: Option<String>,
  162. // TODO: Some of these might appear all the time, no need for Option
  163. OrganizationId: Option<String>,
  164. /*
  165. Login = 1,
  166. SecureNote = 2,
  167. Card = 3,
  168. Identity = 4
  169. */
  170. pub Type: i32, // TODO: Change this to NumberOrString
  171. pub Name: String,
  172. Notes: Option<String>,
  173. Fields: Option<Value>,
  174. // Only one of these should exist, depending on type
  175. Login: Option<Value>,
  176. SecureNote: Option<Value>,
  177. Card: Option<Value>,
  178. Identity: Option<Value>,
  179. Favorite: Option<bool>,
  180. Reprompt: Option<i32>,
  181. PasswordHistory: Option<Value>,
  182. // These are used during key rotation
  183. #[serde(rename = "Attachments")]
  184. _Attachments: Option<Value>, // Unused, contains map of {id: filename}
  185. Attachments2: Option<HashMap<String, Attachments2Data>>,
  186. // The revision datetime (in ISO 8601 format) of the client's local copy
  187. // of the cipher. This is used to prevent a client from updating a cipher
  188. // when it doesn't have the latest version, as that can result in data
  189. // loss. It's not an error when no value is provided; this can happen
  190. // when using older client versions, or if the operation doesn't involve
  191. // updating an existing cipher.
  192. LastKnownRevisionDate: Option<String>,
  193. }
  194. #[derive(Deserialize, Debug)]
  195. #[allow(non_snake_case)]
  196. pub struct Attachments2Data {
  197. FileName: String,
  198. Key: String,
  199. }
  200. /// Called when an org admin clones an org cipher.
  201. #[post("/ciphers/admin", data = "<data>")]
  202. fn post_ciphers_admin(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
  203. post_ciphers_create(data, headers, conn, nt)
  204. }
  205. /// Called when creating a new org-owned cipher, or cloning a cipher (whether
  206. /// user- or org-owned). When cloning a cipher to a user-owned cipher,
  207. /// `organizationId` is null.
  208. #[post("/ciphers/create", data = "<data>")]
  209. fn post_ciphers_create(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
  210. let mut data: ShareCipherData = data.into_inner().data;
  211. // Check if there are one more more collections selected when this cipher is part of an organization.
  212. // err if this is not the case before creating an empty cipher.
  213. if data.Cipher.OrganizationId.is_some() && data.CollectionIds.is_empty() {
  214. err!("You must select at least one collection.");
  215. }
  216. // This check is usually only needed in update_cipher_from_data(), but we
  217. // need it here as well to avoid creating an empty cipher in the call to
  218. // cipher.save() below.
  219. enforce_personal_ownership_policy(Some(&data.Cipher), &headers, &conn)?;
  220. let mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone());
  221. cipher.user_uuid = Some(headers.user.uuid.clone());
  222. cipher.save(&conn)?;
  223. // When cloning a cipher, the Bitwarden clients seem to set this field
  224. // based on the cipher being cloned (when creating a new cipher, it's set
  225. // to null as expected). However, `cipher.created_at` is initialized to
  226. // the current time, so the stale data check will end up failing down the
  227. // line. Since this function only creates new ciphers (whether by cloning
  228. // or otherwise), we can just ignore this field entirely.
  229. data.Cipher.LastKnownRevisionDate = None;
  230. share_cipher_by_uuid(&cipher.uuid, data, &headers, &conn, &nt)
  231. }
  232. /// Called when creating a new user-owned cipher.
  233. #[post("/ciphers", data = "<data>")]
  234. fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
  235. let mut data: CipherData = data.into_inner().data;
  236. // The web/browser clients set this field to null as expected, but the
  237. // mobile clients seem to set the invalid value `0001-01-01T00:00:00`,
  238. // which results in a warning message being logged. This field isn't
  239. // needed when creating a new cipher, so just ignore it unconditionally.
  240. data.LastKnownRevisionDate = None;
  241. let mut cipher = Cipher::new(data.Type, data.Name.clone());
  242. update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherCreate)?;
  243. Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
  244. }
  245. /// Enforces the personal ownership policy on user-owned ciphers, if applicable.
  246. /// A non-owner/admin user belonging to an org with the personal ownership policy
  247. /// enabled isn't allowed to create new user-owned ciphers or modify existing ones
  248. /// (that were created before the policy was applicable to the user). The user is
  249. /// allowed to delete or share such ciphers to an org, however.
  250. ///
  251. /// Ref: https://bitwarden.com/help/article/policies/#personal-ownership
  252. fn enforce_personal_ownership_policy(data: Option<&CipherData>, headers: &Headers, conn: &DbConn) -> EmptyResult {
  253. if data.is_none() || data.unwrap().OrganizationId.is_none() {
  254. let user_uuid = &headers.user.uuid;
  255. let policy_type = OrgPolicyType::PersonalOwnership;
  256. if OrgPolicy::is_applicable_to_user(user_uuid, policy_type, conn) {
  257. err!("Due to an Enterprise Policy, you are restricted from saving items to your personal vault.")
  258. }
  259. }
  260. Ok(())
  261. }
  262. pub fn update_cipher_from_data(
  263. cipher: &mut Cipher,
  264. data: CipherData,
  265. headers: &Headers,
  266. shared_to_collection: bool,
  267. conn: &DbConn,
  268. nt: &Notify,
  269. ut: UpdateType,
  270. ) -> EmptyResult {
  271. enforce_personal_ownership_policy(Some(&data), headers, conn)?;
  272. // Check that the client isn't updating an existing cipher with stale data.
  273. if let Some(dt) = data.LastKnownRevisionDate {
  274. match NaiveDateTime::parse_from_str(&dt, "%+") {
  275. // ISO 8601 format
  276. Err(err) => warn!("Error parsing LastKnownRevisionDate '{}': {}", dt, err),
  277. Ok(dt) if cipher.updated_at.signed_duration_since(dt).num_seconds() > 1 => {
  278. err!("The client copy of this cipher is out of date. Resync the client and try again.")
  279. }
  280. Ok(_) => (),
  281. }
  282. }
  283. if cipher.organization_uuid.is_some() && cipher.organization_uuid != data.OrganizationId {
  284. err!("Organization mismatch. Please resync the client before updating the cipher")
  285. }
  286. if let Some(org_id) = data.OrganizationId {
  287. match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, conn) {
  288. None => err!("You don't have permission to add item to organization"),
  289. Some(org_user) => {
  290. if shared_to_collection
  291. || org_user.has_full_access()
  292. || cipher.is_write_accessible_to_user(&headers.user.uuid, conn)
  293. {
  294. cipher.organization_uuid = Some(org_id);
  295. // After some discussion in PR #1329 re-added the user_uuid = None again.
  296. // TODO: Audit/Check the whole save/update cipher chain.
  297. // Upstream uses the user_uuid to allow a cipher added by a user to an org to still allow the user to view/edit the cipher
  298. // even when the user has hide-passwords configured as there policy.
  299. // Removing the line below would fix that, but we have to check which effect this would have on the rest of the code.
  300. cipher.user_uuid = None;
  301. } else {
  302. err!("You don't have permission to add cipher directly to organization")
  303. }
  304. }
  305. }
  306. } else {
  307. cipher.user_uuid = Some(headers.user.uuid.clone());
  308. }
  309. if let Some(ref folder_id) = data.FolderId {
  310. match Folder::find_by_uuid(folder_id, conn) {
  311. Some(folder) => {
  312. if folder.user_uuid != headers.user.uuid {
  313. err!("Folder is not owned by user")
  314. }
  315. }
  316. None => err!("Folder doesn't exist"),
  317. }
  318. }
  319. // Modify attachments name and keys when rotating
  320. if let Some(attachments) = data.Attachments2 {
  321. for (id, attachment) in attachments {
  322. let mut saved_att = match Attachment::find_by_id(&id, conn) {
  323. Some(att) => att,
  324. None => err!("Attachment doesn't exist"),
  325. };
  326. if saved_att.cipher_uuid != cipher.uuid {
  327. // Warn and break here since cloning ciphers provides attachment data but will not be cloned.
  328. // If we error out here it will break the whole cloning and causes empty ciphers to appear.
  329. warn!("Attachment is not owned by the cipher");
  330. break;
  331. }
  332. saved_att.akey = Some(attachment.Key);
  333. saved_att.file_name = attachment.FileName;
  334. saved_att.save(conn)?;
  335. }
  336. }
  337. // Cleanup cipher data, like removing the 'Response' key.
  338. // This key is somewhere generated during Javascript so no way for us this fix this.
  339. // Also, upstream only retrieves keys they actually want to store, and thus skip the 'Response' key.
  340. // We do not mind which data is in it, the keep our model more flexible when there are upstream changes.
  341. // But, we at least know we do not need to store and return this specific key.
  342. fn _clean_cipher_data(mut json_data: Value) -> Value {
  343. if json_data.is_array() {
  344. json_data.as_array_mut().unwrap().iter_mut().for_each(|ref mut f| {
  345. f.as_object_mut().unwrap().remove("Response");
  346. });
  347. };
  348. json_data
  349. }
  350. let type_data_opt = match data.Type {
  351. 1 => data.Login,
  352. 2 => data.SecureNote,
  353. 3 => data.Card,
  354. 4 => data.Identity,
  355. _ => err!("Invalid type"),
  356. };
  357. let type_data = match type_data_opt {
  358. Some(mut data) => {
  359. // Remove the 'Response' key from the base object.
  360. data.as_object_mut().unwrap().remove("Response");
  361. // Remove the 'Response' key from every Uri.
  362. if data["Uris"].is_array() {
  363. data["Uris"] = _clean_cipher_data(data["Uris"].clone());
  364. }
  365. data
  366. }
  367. None => err!("Data missing"),
  368. };
  369. cipher.name = data.Name;
  370. cipher.notes = data.Notes;
  371. cipher.fields = data.Fields.map(|f| _clean_cipher_data(f).to_string());
  372. cipher.data = type_data.to_string();
  373. cipher.password_history = data.PasswordHistory.map(|f| f.to_string());
  374. cipher.reprompt = data.Reprompt;
  375. cipher.save(conn)?;
  376. cipher.move_to_folder(data.FolderId, &headers.user.uuid, conn)?;
  377. cipher.set_favorite(data.Favorite, &headers.user.uuid, conn)?;
  378. if ut != UpdateType::None {
  379. nt.send_cipher_update(ut, cipher, &cipher.update_users_revision(conn));
  380. }
  381. Ok(())
  382. }
  383. use super::folders::FolderData;
  384. #[derive(Deserialize)]
  385. #[allow(non_snake_case)]
  386. struct ImportData {
  387. Ciphers: Vec<CipherData>,
  388. Folders: Vec<FolderData>,
  389. FolderRelationships: Vec<RelationsData>,
  390. }
  391. #[derive(Deserialize)]
  392. #[allow(non_snake_case)]
  393. struct RelationsData {
  394. // Cipher id
  395. Key: usize,
  396. // Folder id
  397. Value: usize,
  398. }
  399. #[post("/ciphers/import", data = "<data>")]
  400. fn post_ciphers_import(data: JsonUpcase<ImportData>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  401. enforce_personal_ownership_policy(None, &headers, &conn)?;
  402. let data: ImportData = data.into_inner().data;
  403. // Read and create the folders
  404. let mut folders: Vec<_> = Vec::new();
  405. for folder in data.Folders.into_iter() {
  406. let mut new_folder = Folder::new(headers.user.uuid.clone(), folder.Name);
  407. new_folder.save(&conn)?;
  408. folders.push(new_folder);
  409. }
  410. // Read the relations between folders and ciphers
  411. let mut relations_map = HashMap::new();
  412. for relation in data.FolderRelationships {
  413. relations_map.insert(relation.Key, relation.Value);
  414. }
  415. // Read and create the ciphers
  416. for (index, mut cipher_data) in data.Ciphers.into_iter().enumerate() {
  417. let folder_uuid = relations_map.get(&index).map(|i| folders[*i].uuid.clone());
  418. cipher_data.FolderId = folder_uuid;
  419. let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone());
  420. update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::None)?;
  421. }
  422. let mut user = headers.user;
  423. user.update_revision(&conn)?;
  424. nt.send_user_update(UpdateType::Vault, &user);
  425. Ok(())
  426. }
  427. /// Called when an org admin modifies an existing org cipher.
  428. #[put("/ciphers/<uuid>/admin", data = "<data>")]
  429. fn put_cipher_admin(
  430. uuid: String,
  431. data: JsonUpcase<CipherData>,
  432. headers: Headers,
  433. conn: DbConn,
  434. nt: Notify,
  435. ) -> JsonResult {
  436. put_cipher(uuid, data, headers, conn, nt)
  437. }
  438. #[post("/ciphers/<uuid>/admin", data = "<data>")]
  439. fn post_cipher_admin(
  440. uuid: String,
  441. data: JsonUpcase<CipherData>,
  442. headers: Headers,
  443. conn: DbConn,
  444. nt: Notify,
  445. ) -> JsonResult {
  446. post_cipher(uuid, data, headers, conn, nt)
  447. }
  448. #[post("/ciphers/<uuid>", data = "<data>")]
  449. fn post_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
  450. put_cipher(uuid, data, headers, conn, nt)
  451. }
  452. #[put("/ciphers/<uuid>", data = "<data>")]
  453. fn put_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
  454. let data: CipherData = data.into_inner().data;
  455. let mut cipher = match Cipher::find_by_uuid(&uuid, &conn) {
  456. Some(cipher) => cipher,
  457. None => err!("Cipher doesn't exist"),
  458. };
  459. // TODO: Check if only the folder ID or favorite status is being changed.
  460. // These are per-user properties that technically aren't part of the
  461. // cipher itself, so the user shouldn't need write access to change these.
  462. // Interestingly, upstream Bitwarden doesn't properly handle this either.
  463. if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
  464. err!("Cipher is not write accessible")
  465. }
  466. update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherUpdate)?;
  467. Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
  468. }
  469. #[derive(Deserialize)]
  470. #[allow(non_snake_case)]
  471. struct CollectionsAdminData {
  472. CollectionIds: Vec<String>,
  473. }
  474. #[put("/ciphers/<uuid>/collections", data = "<data>")]
  475. fn put_collections_update(
  476. uuid: String,
  477. data: JsonUpcase<CollectionsAdminData>,
  478. headers: Headers,
  479. conn: DbConn,
  480. ) -> EmptyResult {
  481. post_collections_admin(uuid, data, headers, conn)
  482. }
  483. #[post("/ciphers/<uuid>/collections", data = "<data>")]
  484. fn post_collections_update(
  485. uuid: String,
  486. data: JsonUpcase<CollectionsAdminData>,
  487. headers: Headers,
  488. conn: DbConn,
  489. ) -> EmptyResult {
  490. post_collections_admin(uuid, data, headers, conn)
  491. }
  492. #[put("/ciphers/<uuid>/collections-admin", data = "<data>")]
  493. fn put_collections_admin(
  494. uuid: String,
  495. data: JsonUpcase<CollectionsAdminData>,
  496. headers: Headers,
  497. conn: DbConn,
  498. ) -> EmptyResult {
  499. post_collections_admin(uuid, data, headers, conn)
  500. }
  501. #[post("/ciphers/<uuid>/collections-admin", data = "<data>")]
  502. fn post_collections_admin(
  503. uuid: String,
  504. data: JsonUpcase<CollectionsAdminData>,
  505. headers: Headers,
  506. conn: DbConn,
  507. ) -> EmptyResult {
  508. let data: CollectionsAdminData = data.into_inner().data;
  509. let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
  510. Some(cipher) => cipher,
  511. None => err!("Cipher doesn't exist"),
  512. };
  513. if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
  514. err!("Cipher is not write accessible")
  515. }
  516. let posted_collections: HashSet<String> = data.CollectionIds.iter().cloned().collect();
  517. let current_collections: HashSet<String> =
  518. cipher.get_collections(&headers.user.uuid, &conn).iter().cloned().collect();
  519. for collection in posted_collections.symmetric_difference(&current_collections) {
  520. match Collection::find_by_uuid(collection, &conn) {
  521. None => err!("Invalid collection ID provided"),
  522. Some(collection) => {
  523. if collection.is_writable_by_user(&headers.user.uuid, &conn) {
  524. if posted_collections.contains(&collection.uuid) {
  525. // Add to collection
  526. CollectionCipher::save(&cipher.uuid, &collection.uuid, &conn)?;
  527. } else {
  528. // Remove from collection
  529. CollectionCipher::delete(&cipher.uuid, &collection.uuid, &conn)?;
  530. }
  531. } else {
  532. err!("No rights to modify the collection")
  533. }
  534. }
  535. }
  536. }
  537. Ok(())
  538. }
  539. #[derive(Deserialize)]
  540. #[allow(non_snake_case)]
  541. struct ShareCipherData {
  542. Cipher: CipherData,
  543. CollectionIds: Vec<String>,
  544. }
  545. #[post("/ciphers/<uuid>/share", data = "<data>")]
  546. fn post_cipher_share(
  547. uuid: String,
  548. data: JsonUpcase<ShareCipherData>,
  549. headers: Headers,
  550. conn: DbConn,
  551. nt: Notify,
  552. ) -> JsonResult {
  553. let data: ShareCipherData = data.into_inner().data;
  554. share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt)
  555. }
  556. #[put("/ciphers/<uuid>/share", data = "<data>")]
  557. fn put_cipher_share(
  558. uuid: String,
  559. data: JsonUpcase<ShareCipherData>,
  560. headers: Headers,
  561. conn: DbConn,
  562. nt: Notify,
  563. ) -> JsonResult {
  564. let data: ShareCipherData = data.into_inner().data;
  565. share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt)
  566. }
  567. #[derive(Deserialize)]
  568. #[allow(non_snake_case)]
  569. struct ShareSelectedCipherData {
  570. Ciphers: Vec<CipherData>,
  571. CollectionIds: Vec<String>,
  572. }
  573. #[put("/ciphers/share", data = "<data>")]
  574. fn put_cipher_share_selected(
  575. data: JsonUpcase<ShareSelectedCipherData>,
  576. headers: Headers,
  577. conn: DbConn,
  578. nt: Notify,
  579. ) -> EmptyResult {
  580. let mut data: ShareSelectedCipherData = data.into_inner().data;
  581. let mut cipher_ids: Vec<String> = Vec::new();
  582. if data.Ciphers.is_empty() {
  583. err!("You must select at least one cipher.")
  584. }
  585. if data.CollectionIds.is_empty() {
  586. err!("You must select at least one collection.")
  587. }
  588. for cipher in data.Ciphers.iter() {
  589. match cipher.Id {
  590. Some(ref id) => cipher_ids.push(id.to_string()),
  591. None => err!("Request missing ids field"),
  592. };
  593. }
  594. while let Some(cipher) = data.Ciphers.pop() {
  595. let mut shared_cipher_data = ShareCipherData {
  596. Cipher: cipher,
  597. CollectionIds: data.CollectionIds.clone(),
  598. };
  599. match shared_cipher_data.Cipher.Id.take() {
  600. Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &conn, &nt)?,
  601. None => err!("Request missing ids field"),
  602. };
  603. }
  604. Ok(())
  605. }
  606. fn share_cipher_by_uuid(
  607. uuid: &str,
  608. data: ShareCipherData,
  609. headers: &Headers,
  610. conn: &DbConn,
  611. nt: &Notify,
  612. ) -> JsonResult {
  613. let mut cipher = match Cipher::find_by_uuid(uuid, conn) {
  614. Some(cipher) => {
  615. if cipher.is_write_accessible_to_user(&headers.user.uuid, conn) {
  616. cipher
  617. } else {
  618. err!("Cipher is not write accessible")
  619. }
  620. }
  621. None => err!("Cipher doesn't exist"),
  622. };
  623. let mut shared_to_collection = false;
  624. match data.Cipher.OrganizationId.clone() {
  625. // If we don't get an organization ID, we don't do anything
  626. // No error because this is used when using the Clone functionality
  627. None => {}
  628. Some(organization_uuid) => {
  629. for uuid in &data.CollectionIds {
  630. match Collection::find_by_uuid_and_org(uuid, &organization_uuid, conn) {
  631. None => err!("Invalid collection ID provided"),
  632. Some(collection) => {
  633. if collection.is_writable_by_user(&headers.user.uuid, conn) {
  634. CollectionCipher::save(&cipher.uuid, &collection.uuid, conn)?;
  635. shared_to_collection = true;
  636. } else {
  637. err!("No rights to modify the collection")
  638. }
  639. }
  640. }
  641. }
  642. }
  643. };
  644. update_cipher_from_data(
  645. &mut cipher,
  646. data.Cipher,
  647. headers,
  648. shared_to_collection,
  649. conn,
  650. nt,
  651. UpdateType::CipherUpdate,
  652. )?;
  653. Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, conn)))
  654. }
  655. /// v2 API for downloading an attachment. This just redirects the client to
  656. /// the actual location of an attachment.
  657. ///
  658. /// Upstream added this v2 API to support direct download of attachments from
  659. /// their object storage service. For self-hosted instances, it basically just
  660. /// redirects to the same location as before the v2 API.
  661. #[get("/ciphers/<uuid>/attachment/<attachment_id>")]
  662. fn get_attachment(uuid: String, attachment_id: String, headers: Headers, conn: DbConn) -> JsonResult {
  663. match Attachment::find_by_id(&attachment_id, &conn) {
  664. Some(attachment) if uuid == attachment.cipher_uuid => Ok(Json(attachment.to_json(&headers.host))),
  665. Some(_) => err!("Attachment doesn't belong to cipher"),
  666. None => err!("Attachment doesn't exist"),
  667. }
  668. }
  669. #[derive(Deserialize)]
  670. #[allow(non_snake_case)]
  671. struct AttachmentRequestData {
  672. Key: String,
  673. FileName: String,
  674. FileSize: i32,
  675. AdminRequest: Option<bool>, // true when attaching from an org vault view
  676. }
  677. enum FileUploadType {
  678. Direct = 0,
  679. // Azure = 1, // only used upstream
  680. }
  681. /// v2 API for creating an attachment associated with a cipher.
  682. /// This redirects the client to the API it should use to upload the attachment.
  683. /// For upstream's cloud-hosted service, it's an Azure object storage API.
  684. /// For self-hosted instances, it's another API on the local instance.
  685. #[post("/ciphers/<uuid>/attachment/v2", data = "<data>")]
  686. fn post_attachment_v2(
  687. uuid: String,
  688. data: JsonUpcase<AttachmentRequestData>,
  689. headers: Headers,
  690. conn: DbConn,
  691. ) -> JsonResult {
  692. let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
  693. Some(cipher) => cipher,
  694. None => err!("Cipher doesn't exist"),
  695. };
  696. if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
  697. err!("Cipher is not write accessible")
  698. }
  699. let attachment_id = crypto::generate_attachment_id();
  700. let data: AttachmentRequestData = data.into_inner().data;
  701. let attachment =
  702. Attachment::new(attachment_id.clone(), cipher.uuid.clone(), data.FileName, data.FileSize, Some(data.Key));
  703. attachment.save(&conn).expect("Error saving attachment");
  704. let url = format!("/ciphers/{}/attachment/{}", cipher.uuid, attachment_id);
  705. let response_key = match data.AdminRequest {
  706. Some(b) if b => "CipherMiniResponse",
  707. _ => "CipherResponse",
  708. };
  709. Ok(Json(json!({ // AttachmentUploadDataResponseModel
  710. "Object": "attachment-fileUpload",
  711. "AttachmentId": attachment_id,
  712. "Url": url,
  713. "FileUploadType": FileUploadType::Direct as i32,
  714. response_key: cipher.to_json(&headers.host, &headers.user.uuid, &conn),
  715. })))
  716. }
  717. #[derive(FromForm)]
  718. struct UploadData<'f> {
  719. key: Option<String>,
  720. data: TempFile<'f>,
  721. }
  722. /// Saves the data content of an attachment to a file. This is common code
  723. /// shared between the v2 and legacy attachment APIs.
  724. ///
  725. /// When used with the legacy API, this function is responsible for creating
  726. /// the attachment database record, so `attachment` is None.
  727. ///
  728. /// When used with the v2 API, post_attachment_v2() has already created the
  729. /// database record, which is passed in as `attachment`.
  730. async fn save_attachment(
  731. mut attachment: Option<Attachment>,
  732. cipher_uuid: String,
  733. data: Form<UploadData<'_>>,
  734. headers: &Headers,
  735. conn: DbConn,
  736. nt: Notify<'_>,
  737. ) -> Result<(Cipher, DbConn), crate::error::Error> {
  738. let cipher = match Cipher::find_by_uuid(&cipher_uuid, &conn) {
  739. Some(cipher) => cipher,
  740. None => err!("Cipher doesn't exist"),
  741. };
  742. if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
  743. err!("Cipher is not write accessible")
  744. }
  745. // In the v2 API, the attachment record has already been created,
  746. // so the size limit needs to be adjusted to account for that.
  747. let size_adjust = match &attachment {
  748. None => 0, // Legacy API
  749. Some(a) => a.file_size as i64, // v2 API
  750. };
  751. let size_limit = if let Some(ref user_uuid) = cipher.user_uuid {
  752. match CONFIG.user_attachment_limit() {
  753. Some(0) => err!("Attachments are disabled"),
  754. Some(limit_kb) => {
  755. let left = (limit_kb * 1024) - Attachment::size_by_user(user_uuid, &conn) + size_adjust;
  756. if left <= 0 {
  757. err!("Attachment storage limit reached! Delete some attachments to free up space")
  758. }
  759. Some(left as u64)
  760. }
  761. None => None,
  762. }
  763. } else if let Some(ref org_uuid) = cipher.organization_uuid {
  764. match CONFIG.org_attachment_limit() {
  765. Some(0) => err!("Attachments are disabled"),
  766. Some(limit_kb) => {
  767. let left = (limit_kb * 1024) - Attachment::size_by_org(org_uuid, &conn) + size_adjust;
  768. if left <= 0 {
  769. err!("Attachment storage limit reached! Delete some attachments to free up space")
  770. }
  771. Some(left as u64)
  772. }
  773. None => None,
  774. }
  775. } else {
  776. err!("Cipher is neither owned by a user nor an organization");
  777. };
  778. let mut data = data.into_inner();
  779. if let Some(size_limit) = size_limit {
  780. if data.data.len() > size_limit {
  781. err!("Attachment storage limit exceeded with this file");
  782. }
  783. }
  784. let file_id = match &attachment {
  785. Some(attachment) => attachment.id.clone(), // v2 API
  786. None => crypto::generate_attachment_id(), // Legacy API
  787. };
  788. let folder_path = tokio::fs::canonicalize(&CONFIG.attachments_folder()).await?.join(&cipher_uuid);
  789. let file_path = folder_path.join(&file_id);
  790. tokio::fs::create_dir_all(&folder_path).await?;
  791. let size = data.data.len() as i32;
  792. if let Some(attachment) = &mut attachment {
  793. // v2 API
  794. // Check the actual size against the size initially provided by
  795. // the client. Upstream allows +/- 1 MiB deviation from this
  796. // size, but it's not clear when or why this is needed.
  797. const LEEWAY: i32 = 1024 * 1024; // 1 MiB
  798. let min_size = attachment.file_size - LEEWAY;
  799. let max_size = attachment.file_size + LEEWAY;
  800. if min_size <= size && size <= max_size {
  801. if size != attachment.file_size {
  802. // Update the attachment with the actual file size.
  803. attachment.file_size = size;
  804. attachment.save(&conn).expect("Error updating attachment");
  805. }
  806. } else {
  807. attachment.delete(&conn).ok();
  808. err!(format!("Attachment size mismatch (expected within [{}, {}], got {})", min_size, max_size, size));
  809. }
  810. } else {
  811. // Legacy API
  812. let encrypted_filename = data.data.raw_name().map(|s| s.dangerous_unsafe_unsanitized_raw().to_string());
  813. if encrypted_filename.is_none() {
  814. err!("No filename provided")
  815. }
  816. if data.key.is_none() {
  817. err!("No attachment key provided")
  818. }
  819. let attachment = Attachment::new(file_id, cipher_uuid.clone(), encrypted_filename.unwrap(), size, data.key);
  820. attachment.save(&conn).expect("Error saving attachment");
  821. }
  822. data.data.persist_to(file_path).await?;
  823. nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&conn));
  824. Ok((cipher, conn))
  825. }
  826. /// v2 API for uploading the actual data content of an attachment.
  827. /// This route needs a rank specified so that Rocket prioritizes the
  828. /// /ciphers/<uuid>/attachment/v2 route, which would otherwise conflict
  829. /// with this one.
  830. #[post("/ciphers/<uuid>/attachment/<attachment_id>", format = "multipart/form-data", data = "<data>", rank = 1)]
  831. async fn post_attachment_v2_data(
  832. uuid: String,
  833. attachment_id: String,
  834. data: Form<UploadData<'_>>,
  835. headers: Headers,
  836. conn: DbConn,
  837. nt: Notify<'_>,
  838. ) -> EmptyResult {
  839. let attachment = match Attachment::find_by_id(&attachment_id, &conn) {
  840. Some(attachment) if uuid == attachment.cipher_uuid => Some(attachment),
  841. Some(_) => err!("Attachment doesn't belong to cipher"),
  842. None => err!("Attachment doesn't exist"),
  843. };
  844. save_attachment(attachment, uuid, data, &headers, conn, nt).await?;
  845. Ok(())
  846. }
  847. /// Legacy API for creating an attachment associated with a cipher.
  848. #[post("/ciphers/<uuid>/attachment", format = "multipart/form-data", data = "<data>")]
  849. async fn post_attachment(
  850. uuid: String,
  851. data: Form<UploadData<'_>>,
  852. headers: Headers,
  853. conn: DbConn,
  854. nt: Notify<'_>,
  855. ) -> JsonResult {
  856. // Setting this as None signifies to save_attachment() that it should create
  857. // the attachment database record as well as saving the data to disk.
  858. let attachment = None;
  859. let (cipher, conn) = save_attachment(attachment, uuid, data, &headers, conn, nt).await?;
  860. Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
  861. }
  862. #[post("/ciphers/<uuid>/attachment-admin", format = "multipart/form-data", data = "<data>")]
  863. async fn post_attachment_admin(
  864. uuid: String,
  865. data: Form<UploadData<'_>>,
  866. headers: Headers,
  867. conn: DbConn,
  868. nt: Notify<'_>,
  869. ) -> JsonResult {
  870. post_attachment(uuid, data, headers, conn, nt).await
  871. }
  872. #[post("/ciphers/<uuid>/attachment/<attachment_id>/share", format = "multipart/form-data", data = "<data>")]
  873. async fn post_attachment_share(
  874. uuid: String,
  875. attachment_id: String,
  876. data: Form<UploadData<'_>>,
  877. headers: Headers,
  878. conn: DbConn,
  879. nt: Notify<'_>,
  880. ) -> JsonResult {
  881. _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt)?;
  882. post_attachment(uuid, data, headers, conn, nt).await
  883. }
  884. #[post("/ciphers/<uuid>/attachment/<attachment_id>/delete-admin")]
  885. fn delete_attachment_post_admin(
  886. uuid: String,
  887. attachment_id: String,
  888. headers: Headers,
  889. conn: DbConn,
  890. nt: Notify,
  891. ) -> EmptyResult {
  892. delete_attachment(uuid, attachment_id, headers, conn, nt)
  893. }
  894. #[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")]
  895. fn delete_attachment_post(
  896. uuid: String,
  897. attachment_id: String,
  898. headers: Headers,
  899. conn: DbConn,
  900. nt: Notify,
  901. ) -> EmptyResult {
  902. delete_attachment(uuid, attachment_id, headers, conn, nt)
  903. }
  904. #[delete("/ciphers/<uuid>/attachment/<attachment_id>")]
  905. fn delete_attachment(uuid: String, attachment_id: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  906. _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt)
  907. }
  908. #[delete("/ciphers/<uuid>/attachment/<attachment_id>/admin")]
  909. fn delete_attachment_admin(
  910. uuid: String,
  911. attachment_id: String,
  912. headers: Headers,
  913. conn: DbConn,
  914. nt: Notify,
  915. ) -> EmptyResult {
  916. _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt)
  917. }
  918. #[post("/ciphers/<uuid>/delete")]
  919. fn delete_cipher_post(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  920. _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt)
  921. }
  922. #[post("/ciphers/<uuid>/delete-admin")]
  923. fn delete_cipher_post_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  924. _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt)
  925. }
  926. #[put("/ciphers/<uuid>/delete")]
  927. fn delete_cipher_put(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  928. _delete_cipher_by_uuid(&uuid, &headers, &conn, true, &nt)
  929. }
  930. #[put("/ciphers/<uuid>/delete-admin")]
  931. fn delete_cipher_put_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  932. _delete_cipher_by_uuid(&uuid, &headers, &conn, true, &nt)
  933. }
  934. #[delete("/ciphers/<uuid>")]
  935. fn delete_cipher(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  936. _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt)
  937. }
  938. #[delete("/ciphers/<uuid>/admin")]
  939. fn delete_cipher_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  940. _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt)
  941. }
  942. #[delete("/ciphers", data = "<data>")]
  943. fn delete_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  944. _delete_multiple_ciphers(data, headers, conn, false, nt)
  945. }
  946. #[post("/ciphers/delete", data = "<data>")]
  947. fn delete_cipher_selected_post(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  948. _delete_multiple_ciphers(data, headers, conn, false, nt)
  949. }
  950. #[put("/ciphers/delete", data = "<data>")]
  951. fn delete_cipher_selected_put(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  952. _delete_multiple_ciphers(data, headers, conn, true, nt) // soft delete
  953. }
  954. #[delete("/ciphers/admin", data = "<data>")]
  955. fn delete_cipher_selected_admin(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  956. delete_cipher_selected(data, headers, conn, nt)
  957. }
  958. #[post("/ciphers/delete-admin", data = "<data>")]
  959. fn delete_cipher_selected_post_admin(
  960. data: JsonUpcase<Value>,
  961. headers: Headers,
  962. conn: DbConn,
  963. nt: Notify,
  964. ) -> EmptyResult {
  965. delete_cipher_selected_post(data, headers, conn, nt)
  966. }
  967. #[put("/ciphers/delete-admin", data = "<data>")]
  968. fn delete_cipher_selected_put_admin(
  969. data: JsonUpcase<Value>,
  970. headers: Headers,
  971. conn: DbConn,
  972. nt: Notify,
  973. ) -> EmptyResult {
  974. delete_cipher_selected_put(data, headers, conn, nt)
  975. }
  976. #[put("/ciphers/<uuid>/restore")]
  977. fn restore_cipher_put(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
  978. _restore_cipher_by_uuid(&uuid, &headers, &conn, &nt)
  979. }
  980. #[put("/ciphers/<uuid>/restore-admin")]
  981. fn restore_cipher_put_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
  982. _restore_cipher_by_uuid(&uuid, &headers, &conn, &nt)
  983. }
  984. #[put("/ciphers/restore", data = "<data>")]
  985. fn restore_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
  986. _restore_multiple_ciphers(data, &headers, &conn, &nt)
  987. }
  988. #[derive(Deserialize)]
  989. #[allow(non_snake_case)]
  990. struct MoveCipherData {
  991. FolderId: Option<String>,
  992. Ids: Vec<String>,
  993. }
  994. #[post("/ciphers/move", data = "<data>")]
  995. fn move_cipher_selected(data: JsonUpcase<MoveCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
  996. let data = data.into_inner().data;
  997. let user_uuid = headers.user.uuid;
  998. if let Some(ref folder_id) = data.FolderId {
  999. match Folder::find_by_uuid(folder_id, &conn) {
  1000. Some(folder) => {
  1001. if folder.user_uuid != user_uuid {
  1002. err!("Folder is not owned by user")
  1003. }
  1004. }
  1005. None => err!("Folder doesn't exist"),
  1006. }
  1007. }
  1008. for uuid in data.Ids {
  1009. let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
  1010. Some(cipher) => cipher,
  1011. None => err!("Cipher doesn't exist"),
  1012. };
  1013. if !cipher.is_accessible_to_user(&user_uuid, &conn) {
  1014. err!("Cipher is not accessible by user")
  1015. }
  1016. // Move cipher
  1017. cipher.move_to_folder(data.FolderId.clone(), &user_uuid, &conn)?;
  1018. nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &[user_uuid.clone()]);
  1019. }
  1020. Ok(())
  1021. }
  1022. #[put("/ciphers/move", data = "<data>")]
  1023. fn move_cipher_selected_put(
  1024. data: JsonUpcase<MoveCipherData>,
  1025. headers: Headers,
  1026. conn: DbConn,
  1027. nt: Notify,
  1028. ) -> EmptyResult {
  1029. move_cipher_selected(data, headers, conn, nt)
  1030. }
  1031. #[derive(FromForm)]
  1032. struct OrganizationId {
  1033. #[field(name = "organizationId")]
  1034. org_id: String,
  1035. }
  1036. #[post("/ciphers/purge?<organization..>", data = "<data>")]
  1037. fn delete_all(
  1038. organization: Option<OrganizationId>,
  1039. data: JsonUpcase<PasswordData>,
  1040. headers: Headers,
  1041. conn: DbConn,
  1042. nt: Notify,
  1043. ) -> EmptyResult {
  1044. let data: PasswordData = data.into_inner().data;
  1045. let password_hash = data.MasterPasswordHash;
  1046. let mut user = headers.user;
  1047. if !user.check_valid_password(&password_hash) {
  1048. err!("Invalid password")
  1049. }
  1050. match organization {
  1051. Some(org_data) => {
  1052. // Organization ID in query params, purging organization vault
  1053. match UserOrganization::find_by_user_and_org(&user.uuid, &org_data.org_id, &conn) {
  1054. None => err!("You don't have permission to purge the organization vault"),
  1055. Some(user_org) => {
  1056. if user_org.atype == UserOrgType::Owner {
  1057. Cipher::delete_all_by_organization(&org_data.org_id, &conn)?;
  1058. nt.send_user_update(UpdateType::Vault, &user);
  1059. Ok(())
  1060. } else {
  1061. err!("You don't have permission to purge the organization vault");
  1062. }
  1063. }
  1064. }
  1065. }
  1066. None => {
  1067. // No organization ID in query params, purging user vault
  1068. // Delete ciphers and their attachments
  1069. for cipher in Cipher::find_owned_by_user(&user.uuid, &conn) {
  1070. cipher.delete(&conn)?;
  1071. }
  1072. // Delete folders
  1073. for f in Folder::find_by_user(&user.uuid, &conn) {
  1074. f.delete(&conn)?;
  1075. }
  1076. user.update_revision(&conn)?;
  1077. nt.send_user_update(UpdateType::Vault, &user);
  1078. Ok(())
  1079. }
  1080. }
  1081. }
  1082. fn _delete_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, soft_delete: bool, nt: &Notify) -> EmptyResult {
  1083. let mut cipher = match Cipher::find_by_uuid(uuid, conn) {
  1084. Some(cipher) => cipher,
  1085. None => err!("Cipher doesn't exist"),
  1086. };
  1087. if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn) {
  1088. err!("Cipher can't be deleted by user")
  1089. }
  1090. if soft_delete {
  1091. cipher.deleted_at = Some(Utc::now().naive_utc());
  1092. cipher.save(conn)?;
  1093. nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn));
  1094. } else {
  1095. cipher.delete(conn)?;
  1096. nt.send_cipher_update(UpdateType::CipherDelete, &cipher, &cipher.update_users_revision(conn));
  1097. }
  1098. Ok(())
  1099. }
  1100. fn _delete_multiple_ciphers(
  1101. data: JsonUpcase<Value>,
  1102. headers: Headers,
  1103. conn: DbConn,
  1104. soft_delete: bool,
  1105. nt: Notify,
  1106. ) -> EmptyResult {
  1107. let data: Value = data.into_inner().data;
  1108. let uuids = match data.get("Ids") {
  1109. Some(ids) => match ids.as_array() {
  1110. Some(ids) => ids.iter().filter_map(Value::as_str),
  1111. None => err!("Posted ids field is not an array"),
  1112. },
  1113. None => err!("Request missing ids field"),
  1114. };
  1115. for uuid in uuids {
  1116. if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &conn, soft_delete, &nt) {
  1117. return error;
  1118. };
  1119. }
  1120. Ok(())
  1121. }
  1122. fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, nt: &Notify) -> JsonResult {
  1123. let mut cipher = match Cipher::find_by_uuid(uuid, conn) {
  1124. Some(cipher) => cipher,
  1125. None => err!("Cipher doesn't exist"),
  1126. };
  1127. if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn) {
  1128. err!("Cipher can't be restored by user")
  1129. }
  1130. cipher.deleted_at = None;
  1131. cipher.save(conn)?;
  1132. nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn));
  1133. Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, conn)))
  1134. }
  1135. fn _restore_multiple_ciphers(data: JsonUpcase<Value>, headers: &Headers, conn: &DbConn, nt: &Notify) -> JsonResult {
  1136. let data: Value = data.into_inner().data;
  1137. let uuids = match data.get("Ids") {
  1138. Some(ids) => match ids.as_array() {
  1139. Some(ids) => ids.iter().filter_map(Value::as_str),
  1140. None => err!("Posted ids field is not an array"),
  1141. },
  1142. None => err!("Request missing ids field"),
  1143. };
  1144. let mut ciphers: Vec<Value> = Vec::new();
  1145. for uuid in uuids {
  1146. match _restore_cipher_by_uuid(uuid, headers, conn, nt) {
  1147. Ok(json) => ciphers.push(json.into_inner()),
  1148. err => return err,
  1149. }
  1150. }
  1151. Ok(Json(json!({
  1152. "Data": ciphers,
  1153. "Object": "list",
  1154. "ContinuationToken": null
  1155. })))
  1156. }
  1157. fn _delete_cipher_attachment_by_id(
  1158. uuid: &str,
  1159. attachment_id: &str,
  1160. headers: &Headers,
  1161. conn: &DbConn,
  1162. nt: &Notify,
  1163. ) -> EmptyResult {
  1164. let attachment = match Attachment::find_by_id(attachment_id, conn) {
  1165. Some(attachment) => attachment,
  1166. None => err!("Attachment doesn't exist"),
  1167. };
  1168. if attachment.cipher_uuid != uuid {
  1169. err!("Attachment from other cipher")
  1170. }
  1171. let cipher = match Cipher::find_by_uuid(uuid, conn) {
  1172. Some(cipher) => cipher,
  1173. None => err!("Cipher doesn't exist"),
  1174. };
  1175. if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn) {
  1176. err!("Cipher cannot be deleted by user")
  1177. }
  1178. // Delete attachment
  1179. attachment.delete(conn)?;
  1180. nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn));
  1181. Ok(())
  1182. }