cipher.rs 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  1. use crate::util::LowerCase;
  2. use crate::CONFIG;
  3. use chrono::{DateTime, NaiveDateTime, TimeDelta, Utc};
  4. use serde_json::Value;
  5. use super::{
  6. Attachment, CollectionCipher, Favorite, FolderCipher, Group, User, UserOrgStatus, UserOrgType, UserOrganization,
  7. };
  8. use crate::api::core::{CipherData, CipherSyncData, CipherSyncType};
  9. use std::borrow::Cow;
  10. db_object! {
  11. #[derive(Identifiable, Queryable, Insertable, AsChangeset)]
  12. #[diesel(table_name = ciphers)]
  13. #[diesel(treat_none_as_null = true)]
  14. #[diesel(primary_key(uuid))]
  15. pub struct Cipher {
  16. pub uuid: String,
  17. pub created_at: NaiveDateTime,
  18. pub updated_at: NaiveDateTime,
  19. pub user_uuid: Option<String>,
  20. pub organization_uuid: Option<String>,
  21. pub key: Option<String>,
  22. /*
  23. Login = 1,
  24. SecureNote = 2,
  25. Card = 3,
  26. Identity = 4
  27. */
  28. pub atype: i32,
  29. pub name: String,
  30. pub notes: Option<String>,
  31. pub fields: Option<String>,
  32. pub data: String,
  33. pub password_history: Option<String>,
  34. pub deleted_at: Option<NaiveDateTime>,
  35. pub reprompt: Option<i32>,
  36. }
  37. }
  38. #[allow(dead_code)]
  39. pub enum RepromptType {
  40. None = 0,
  41. Password = 1, // not currently used in server
  42. }
  43. /// Local methods
  44. impl Cipher {
  45. pub fn new(atype: i32, name: String) -> Self {
  46. let now = Utc::now().naive_utc();
  47. Self {
  48. uuid: crate::util::get_uuid(),
  49. created_at: now,
  50. updated_at: now,
  51. user_uuid: None,
  52. organization_uuid: None,
  53. key: None,
  54. atype,
  55. name,
  56. notes: None,
  57. fields: None,
  58. data: String::new(),
  59. password_history: None,
  60. deleted_at: None,
  61. reprompt: None,
  62. }
  63. }
  64. pub fn validate_cipher_data(cipher_data: &[CipherData]) -> EmptyResult {
  65. let mut validation_errors = serde_json::Map::new();
  66. let max_note_size = CONFIG._max_note_size();
  67. let max_note_size_msg =
  68. format!("The field Notes exceeds the maximum encrypted value length of {} characters.", &max_note_size);
  69. for (index, cipher) in cipher_data.iter().enumerate() {
  70. // Validate the note size and if it is exceeded return a warning
  71. if let Some(note) = &cipher.notes {
  72. if note.len() > max_note_size {
  73. validation_errors
  74. .insert(format!("Ciphers[{index}].Notes"), serde_json::to_value([&max_note_size_msg]).unwrap());
  75. }
  76. }
  77. // Validate the password history if it contains `null` values and if so, return a warning
  78. if let Some(Value::Array(password_history)) = &cipher.password_history {
  79. for pwh in password_history {
  80. if let Value::Object(pwo) = pwh {
  81. if pwo.get("password").is_some_and(|p| !p.is_string()) {
  82. validation_errors.insert(
  83. format!("Ciphers[{index}].Notes"),
  84. serde_json::to_value([
  85. "The password history contains a `null` value. Only strings are allowed.",
  86. ])
  87. .unwrap(),
  88. );
  89. break;
  90. }
  91. }
  92. }
  93. }
  94. }
  95. if !validation_errors.is_empty() {
  96. let err_json = json!({
  97. "message": "The model state is invalid.",
  98. "validationErrors" : validation_errors,
  99. "object": "error"
  100. });
  101. err_json!(err_json, "Import validation errors")
  102. } else {
  103. Ok(())
  104. }
  105. }
  106. }
  107. use crate::db::DbConn;
  108. use crate::api::EmptyResult;
  109. use crate::error::MapResult;
  110. /// Database methods
  111. impl Cipher {
  112. pub async fn to_json(
  113. &self,
  114. host: &str,
  115. user_uuid: &str,
  116. cipher_sync_data: Option<&CipherSyncData>,
  117. sync_type: CipherSyncType,
  118. conn: &mut DbConn,
  119. ) -> Value {
  120. use crate::util::format_date;
  121. let mut attachments_json: Value = Value::Null;
  122. if let Some(cipher_sync_data) = cipher_sync_data {
  123. if let Some(attachments) = cipher_sync_data.cipher_attachments.get(&self.uuid) {
  124. attachments_json = attachments.iter().map(|c| c.to_json(host)).collect();
  125. }
  126. } else {
  127. let attachments = Attachment::find_by_cipher(&self.uuid, conn).await;
  128. if !attachments.is_empty() {
  129. attachments_json = attachments.iter().map(|c| c.to_json(host)).collect()
  130. }
  131. }
  132. // We don't need these values at all for Organizational syncs
  133. // Skip any other database calls if this is the case and just return false.
  134. let (read_only, hide_passwords) = if sync_type == CipherSyncType::User {
  135. match self.get_access_restrictions(user_uuid, cipher_sync_data, conn).await {
  136. Some((ro, hp)) => (ro, hp),
  137. None => {
  138. error!("Cipher ownership assertion failure");
  139. (true, true)
  140. }
  141. }
  142. } else {
  143. (false, false)
  144. };
  145. let fields_json: Vec<_> = self
  146. .fields
  147. .as_ref()
  148. .and_then(|s| {
  149. serde_json::from_str::<Vec<LowerCase<Value>>>(s)
  150. .inspect_err(|e| warn!("Error parsing fields {e:?} for {}", self.uuid))
  151. .ok()
  152. })
  153. .map(|d| {
  154. d.into_iter()
  155. .map(|mut f| {
  156. // Check if the `type` key is a number, strings break some clients
  157. // The fallback type is the hidden type `1`. this should prevent accidental data disclosure
  158. // If not try to convert the string value to a number and fallback to `1`
  159. // If it is both not a number and not a string, fallback to `1`
  160. match f.data.get("type") {
  161. Some(t) if t.is_number() => {}
  162. Some(t) if t.is_string() => {
  163. let type_num = &t.as_str().unwrap_or("1").parse::<u8>().unwrap_or(1);
  164. f.data["type"] = json!(type_num);
  165. }
  166. _ => {
  167. f.data["type"] = json!(1);
  168. }
  169. }
  170. f.data
  171. })
  172. .collect()
  173. })
  174. .unwrap_or_default();
  175. let password_history_json: Vec<_> = self
  176. .password_history
  177. .as_ref()
  178. .and_then(|s| {
  179. serde_json::from_str::<Vec<LowerCase<Value>>>(s)
  180. .inspect_err(|e| warn!("Error parsing password history {e:?} for {}", self.uuid))
  181. .ok()
  182. })
  183. .map(|d| {
  184. // Check every password history item if they are valid and return it.
  185. // If a password field has the type `null` skip it, it breaks newer Bitwarden clients
  186. // A second check is done to verify the lastUsedDate exists and is a valid DateTime string, if not the epoch start time will be used
  187. d.into_iter()
  188. .filter_map(|d| match d.data.get("password") {
  189. Some(p) if p.is_string() => Some(d.data),
  190. _ => None,
  191. })
  192. .map(|d| match d.get("lastUsedDate").and_then(|l| l.as_str()) {
  193. Some(l) if DateTime::parse_from_rfc3339(l).is_ok() => d,
  194. _ => {
  195. let mut d = d;
  196. d["lastUsedDate"] = json!("1970-01-01T00:00:00.000Z");
  197. d
  198. }
  199. })
  200. .collect()
  201. })
  202. .unwrap_or_default();
  203. // Get the type_data or a default to an empty json object '{}'.
  204. // If not passing an empty object, mobile clients will crash.
  205. let mut type_data_json =
  206. serde_json::from_str::<LowerCase<Value>>(&self.data).map(|d| d.data).unwrap_or_else(|_| {
  207. warn!("Error parsing data field for {}", self.uuid);
  208. Value::Object(serde_json::Map::new())
  209. });
  210. // NOTE: This was marked as *Backwards Compatibility Code*, but as of January 2021 this is still being used by upstream
  211. // Set the first element of the Uris array as Uri, this is needed several (mobile) clients.
  212. if self.atype == 1 {
  213. if type_data_json["uris"].is_array() {
  214. let uri = type_data_json["uris"][0]["uri"].clone();
  215. type_data_json["uri"] = uri;
  216. } else {
  217. // Upstream always has an Uri key/value
  218. type_data_json["uri"] = Value::Null;
  219. }
  220. }
  221. // Fix secure note issues when data is invalid
  222. // This breaks at least the native mobile clients
  223. if self.atype == 2 {
  224. match type_data_json {
  225. Value::Object(ref t) if t.get("type").is_some_and(|t| t.is_number()) => {}
  226. _ => {
  227. type_data_json = json!({"type": 0});
  228. }
  229. }
  230. }
  231. // Clone the type_data and add some default value.
  232. let mut data_json = type_data_json.clone();
  233. // NOTE: This was marked as *Backwards Compatibility Code*, but as of January 2021 this is still being used by upstream
  234. // data_json should always contain the following keys with every atype
  235. data_json["fields"] = json!(fields_json);
  236. data_json["name"] = json!(self.name);
  237. data_json["notes"] = json!(self.notes);
  238. data_json["passwordHistory"] = Value::Array(password_history_json.clone());
  239. let collection_ids = if let Some(cipher_sync_data) = cipher_sync_data {
  240. if let Some(cipher_collections) = cipher_sync_data.cipher_collections.get(&self.uuid) {
  241. Cow::from(cipher_collections)
  242. } else {
  243. Cow::from(Vec::with_capacity(0))
  244. }
  245. } else {
  246. Cow::from(self.get_admin_collections(user_uuid.to_string(), conn).await)
  247. };
  248. // There are three types of cipher response models in upstream
  249. // Bitwarden: "cipherMini", "cipher", and "cipherDetails" (in order
  250. // of increasing level of detail). vaultwarden currently only
  251. // supports the "cipherDetails" type, though it seems like the
  252. // Bitwarden clients will ignore extra fields.
  253. //
  254. // Ref: https://github.com/bitwarden/server/blob/master/src/Core/Models/Api/Response/CipherResponseModel.cs
  255. let mut json_object = json!({
  256. "object": "cipherDetails",
  257. "id": self.uuid,
  258. "type": self.atype,
  259. "creationDate": format_date(&self.created_at),
  260. "revisionDate": format_date(&self.updated_at),
  261. "deletedDate": self.deleted_at.map_or(Value::Null, |d| Value::String(format_date(&d))),
  262. "reprompt": self.reprompt.unwrap_or(RepromptType::None as i32),
  263. "organizationId": self.organization_uuid,
  264. "key": self.key,
  265. "attachments": attachments_json,
  266. // We have UseTotp set to true by default within the Organization model.
  267. // This variable together with UsersGetPremium is used to show or hide the TOTP counter.
  268. "organizationUseTotp": true,
  269. // This field is specific to the cipherDetails type.
  270. "collectionIds": collection_ids,
  271. "name": self.name,
  272. "notes": self.notes,
  273. "fields": fields_json,
  274. "data": data_json,
  275. "passwordHistory": password_history_json,
  276. // All Cipher types are included by default as null, but only the matching one will be populated
  277. "login": null,
  278. "secureNote": null,
  279. "card": null,
  280. "identity": null,
  281. });
  282. // These values are only needed for user/default syncs
  283. // Not during an organizational sync like `get_org_details`
  284. // Skip adding these fields in that case
  285. if sync_type == CipherSyncType::User {
  286. json_object["folderId"] = json!(if let Some(cipher_sync_data) = cipher_sync_data {
  287. cipher_sync_data.cipher_folders.get(&self.uuid).map(|c| c.to_string())
  288. } else {
  289. self.get_folder_uuid(user_uuid, conn).await
  290. });
  291. json_object["favorite"] = json!(if let Some(cipher_sync_data) = cipher_sync_data {
  292. cipher_sync_data.cipher_favorites.contains(&self.uuid)
  293. } else {
  294. self.is_favorite(user_uuid, conn).await
  295. });
  296. // These values are true by default, but can be false if the
  297. // cipher belongs to a collection or group where the org owner has enabled
  298. // the "Read Only" or "Hide Passwords" restrictions for the user.
  299. json_object["edit"] = json!(!read_only);
  300. json_object["viewPassword"] = json!(!hide_passwords);
  301. }
  302. let key = match self.atype {
  303. 1 => "login",
  304. 2 => "secureNote",
  305. 3 => "card",
  306. 4 => "identity",
  307. _ => panic!("Wrong type"),
  308. };
  309. json_object[key] = type_data_json;
  310. json_object
  311. }
  312. pub async fn update_users_revision(&self, conn: &mut DbConn) -> Vec<String> {
  313. let mut user_uuids = Vec::new();
  314. match self.user_uuid {
  315. Some(ref user_uuid) => {
  316. User::update_uuid_revision(user_uuid, conn).await;
  317. user_uuids.push(user_uuid.clone())
  318. }
  319. None => {
  320. // Belongs to Organization, need to update affected users
  321. if let Some(ref org_uuid) = self.organization_uuid {
  322. // users having access to the collection
  323. let mut collection_users =
  324. UserOrganization::find_by_cipher_and_org(&self.uuid, org_uuid, conn).await;
  325. if CONFIG.org_groups_enabled() {
  326. // members of a group having access to the collection
  327. let group_users =
  328. UserOrganization::find_by_cipher_and_org_with_group(&self.uuid, org_uuid, conn).await;
  329. collection_users.extend(group_users);
  330. }
  331. for user_org in collection_users {
  332. User::update_uuid_revision(&user_org.user_uuid, conn).await;
  333. user_uuids.push(user_org.user_uuid.clone())
  334. }
  335. }
  336. }
  337. };
  338. user_uuids
  339. }
  340. pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult {
  341. self.update_users_revision(conn).await;
  342. self.updated_at = Utc::now().naive_utc();
  343. db_run! { conn:
  344. sqlite, mysql {
  345. match diesel::replace_into(ciphers::table)
  346. .values(CipherDb::to_db(self))
  347. .execute(conn)
  348. {
  349. Ok(_) => Ok(()),
  350. // Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
  351. Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
  352. diesel::update(ciphers::table)
  353. .filter(ciphers::uuid.eq(&self.uuid))
  354. .set(CipherDb::to_db(self))
  355. .execute(conn)
  356. .map_res("Error saving cipher")
  357. }
  358. Err(e) => Err(e.into()),
  359. }.map_res("Error saving cipher")
  360. }
  361. postgresql {
  362. let value = CipherDb::to_db(self);
  363. diesel::insert_into(ciphers::table)
  364. .values(&value)
  365. .on_conflict(ciphers::uuid)
  366. .do_update()
  367. .set(&value)
  368. .execute(conn)
  369. .map_res("Error saving cipher")
  370. }
  371. }
  372. }
  373. pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult {
  374. self.update_users_revision(conn).await;
  375. FolderCipher::delete_all_by_cipher(&self.uuid, conn).await?;
  376. CollectionCipher::delete_all_by_cipher(&self.uuid, conn).await?;
  377. Attachment::delete_all_by_cipher(&self.uuid, conn).await?;
  378. Favorite::delete_all_by_cipher(&self.uuid, conn).await?;
  379. db_run! { conn: {
  380. diesel::delete(ciphers::table.filter(ciphers::uuid.eq(&self.uuid)))
  381. .execute(conn)
  382. .map_res("Error deleting cipher")
  383. }}
  384. }
  385. pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult {
  386. // TODO: Optimize this by executing a DELETE directly on the database, instead of first fetching.
  387. for cipher in Self::find_by_org(org_uuid, conn).await {
  388. cipher.delete(conn).await?;
  389. }
  390. Ok(())
  391. }
  392. pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
  393. for cipher in Self::find_owned_by_user(user_uuid, conn).await {
  394. cipher.delete(conn).await?;
  395. }
  396. Ok(())
  397. }
  398. /// Purge all ciphers that are old enough to be auto-deleted.
  399. pub async fn purge_trash(conn: &mut DbConn) {
  400. if let Some(auto_delete_days) = CONFIG.trash_auto_delete_days() {
  401. let now = Utc::now().naive_utc();
  402. let dt = now - TimeDelta::try_days(auto_delete_days).unwrap();
  403. for cipher in Self::find_deleted_before(&dt, conn).await {
  404. cipher.delete(conn).await.ok();
  405. }
  406. }
  407. }
  408. pub async fn move_to_folder(&self, folder_uuid: Option<String>, user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
  409. User::update_uuid_revision(user_uuid, conn).await;
  410. match (self.get_folder_uuid(user_uuid, conn).await, folder_uuid) {
  411. // No changes
  412. (None, None) => Ok(()),
  413. (Some(ref old), Some(ref new)) if old == new => Ok(()),
  414. // Add to folder
  415. (None, Some(new)) => FolderCipher::new(&new, &self.uuid).save(conn).await,
  416. // Remove from folder
  417. (Some(old), None) => match FolderCipher::find_by_folder_and_cipher(&old, &self.uuid, conn).await {
  418. Some(old) => old.delete(conn).await,
  419. None => err!("Couldn't move from previous folder"),
  420. },
  421. // Move to another folder
  422. (Some(old), Some(new)) => {
  423. if let Some(old) = FolderCipher::find_by_folder_and_cipher(&old, &self.uuid, conn).await {
  424. old.delete(conn).await?;
  425. }
  426. FolderCipher::new(&new, &self.uuid).save(conn).await
  427. }
  428. }
  429. }
  430. /// Returns whether this cipher is directly owned by the user.
  431. pub fn is_owned_by_user(&self, user_uuid: &str) -> bool {
  432. self.user_uuid.is_some() && self.user_uuid.as_ref().unwrap() == user_uuid
  433. }
  434. /// Returns whether this cipher is owned by an org in which the user has full access.
  435. async fn is_in_full_access_org(
  436. &self,
  437. user_uuid: &str,
  438. cipher_sync_data: Option<&CipherSyncData>,
  439. conn: &mut DbConn,
  440. ) -> bool {
  441. if let Some(ref org_uuid) = self.organization_uuid {
  442. if let Some(cipher_sync_data) = cipher_sync_data {
  443. if let Some(cached_user_org) = cipher_sync_data.user_organizations.get(org_uuid) {
  444. return cached_user_org.has_full_access();
  445. }
  446. } else if let Some(user_org) = UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn).await {
  447. return user_org.has_full_access();
  448. }
  449. }
  450. false
  451. }
  452. /// Returns whether this cipher is owned by an group in which the user has full access.
  453. async fn is_in_full_access_group(
  454. &self,
  455. user_uuid: &str,
  456. cipher_sync_data: Option<&CipherSyncData>,
  457. conn: &mut DbConn,
  458. ) -> bool {
  459. if !CONFIG.org_groups_enabled() {
  460. return false;
  461. }
  462. if let Some(ref org_uuid) = self.organization_uuid {
  463. if let Some(cipher_sync_data) = cipher_sync_data {
  464. return cipher_sync_data.user_group_full_access_for_organizations.contains(org_uuid);
  465. } else {
  466. return Group::is_in_full_access_group(user_uuid, org_uuid, conn).await;
  467. }
  468. }
  469. false
  470. }
  471. /// Returns the user's access restrictions to this cipher. A return value
  472. /// of None means that this cipher does not belong to the user, and is
  473. /// not in any collection the user has access to. Otherwise, the user has
  474. /// access to this cipher, and Some(read_only, hide_passwords) represents
  475. /// the access restrictions.
  476. pub async fn get_access_restrictions(
  477. &self,
  478. user_uuid: &str,
  479. cipher_sync_data: Option<&CipherSyncData>,
  480. conn: &mut DbConn,
  481. ) -> Option<(bool, bool)> {
  482. // Check whether this cipher is directly owned by the user, or is in
  483. // a collection that the user has full access to. If so, there are no
  484. // access restrictions.
  485. if self.is_owned_by_user(user_uuid)
  486. || self.is_in_full_access_org(user_uuid, cipher_sync_data, conn).await
  487. || self.is_in_full_access_group(user_uuid, cipher_sync_data, conn).await
  488. {
  489. return Some((false, false));
  490. }
  491. let rows = if let Some(cipher_sync_data) = cipher_sync_data {
  492. let mut rows: Vec<(bool, bool)> = Vec::new();
  493. if let Some(collections) = cipher_sync_data.cipher_collections.get(&self.uuid) {
  494. for collection in collections {
  495. //User permissions
  496. if let Some(uc) = cipher_sync_data.user_collections.get(collection) {
  497. rows.push((uc.read_only, uc.hide_passwords));
  498. }
  499. //Group permissions
  500. if let Some(cg) = cipher_sync_data.user_collections_groups.get(collection) {
  501. rows.push((cg.read_only, cg.hide_passwords));
  502. }
  503. }
  504. }
  505. rows
  506. } else {
  507. let mut access_flags = self.get_user_collections_access_flags(user_uuid, conn).await;
  508. access_flags.append(&mut self.get_group_collections_access_flags(user_uuid, conn).await);
  509. access_flags
  510. };
  511. if rows.is_empty() {
  512. // This cipher isn't in any collections accessible to the user.
  513. return None;
  514. }
  515. // A cipher can be in multiple collections with inconsistent access flags.
  516. // For example, a cipher could be in one collection where the user has
  517. // read-only access, but also in another collection where the user has
  518. // read/write access. For a flag to be in effect for a cipher, upstream
  519. // requires all collections the cipher is in to have that flag set.
  520. // Therefore, we do a boolean AND of all values in each of the `read_only`
  521. // and `hide_passwords` columns. This could ideally be done as part of the
  522. // query, but Diesel doesn't support a min() or bool_and() function on
  523. // booleans and this behavior isn't portable anyway.
  524. let mut read_only = true;
  525. let mut hide_passwords = true;
  526. for (ro, hp) in rows.iter() {
  527. read_only &= ro;
  528. hide_passwords &= hp;
  529. }
  530. Some((read_only, hide_passwords))
  531. }
  532. async fn get_user_collections_access_flags(&self, user_uuid: &str, conn: &mut DbConn) -> Vec<(bool, bool)> {
  533. db_run! {conn: {
  534. // Check whether this cipher is in any collections accessible to the
  535. // user. If so, retrieve the access flags for each collection.
  536. ciphers::table
  537. .filter(ciphers::uuid.eq(&self.uuid))
  538. .inner_join(ciphers_collections::table.on(
  539. ciphers::uuid.eq(ciphers_collections::cipher_uuid)))
  540. .inner_join(users_collections::table.on(
  541. ciphers_collections::collection_uuid.eq(users_collections::collection_uuid)
  542. .and(users_collections::user_uuid.eq(user_uuid))))
  543. .select((users_collections::read_only, users_collections::hide_passwords))
  544. .load::<(bool, bool)>(conn)
  545. .expect("Error getting user access restrictions")
  546. }}
  547. }
  548. async fn get_group_collections_access_flags(&self, user_uuid: &str, conn: &mut DbConn) -> Vec<(bool, bool)> {
  549. if !CONFIG.org_groups_enabled() {
  550. return Vec::new();
  551. }
  552. db_run! {conn: {
  553. ciphers::table
  554. .filter(ciphers::uuid.eq(&self.uuid))
  555. .inner_join(ciphers_collections::table.on(
  556. ciphers::uuid.eq(ciphers_collections::cipher_uuid)
  557. ))
  558. .inner_join(collections_groups::table.on(
  559. collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid)
  560. ))
  561. .inner_join(groups_users::table.on(
  562. groups_users::groups_uuid.eq(collections_groups::groups_uuid)
  563. ))
  564. .inner_join(users_organizations::table.on(
  565. users_organizations::uuid.eq(groups_users::users_organizations_uuid)
  566. ))
  567. .filter(users_organizations::user_uuid.eq(user_uuid))
  568. .select((collections_groups::read_only, collections_groups::hide_passwords))
  569. .load::<(bool, bool)>(conn)
  570. .expect("Error getting group access restrictions")
  571. }}
  572. }
  573. pub async fn is_write_accessible_to_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool {
  574. match self.get_access_restrictions(user_uuid, None, conn).await {
  575. Some((read_only, _hide_passwords)) => !read_only,
  576. None => false,
  577. }
  578. }
  579. pub async fn is_accessible_to_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool {
  580. self.get_access_restrictions(user_uuid, None, conn).await.is_some()
  581. }
  582. // Returns whether this cipher is a favorite of the specified user.
  583. pub async fn is_favorite(&self, user_uuid: &str, conn: &mut DbConn) -> bool {
  584. Favorite::is_favorite(&self.uuid, user_uuid, conn).await
  585. }
  586. // Sets whether this cipher is a favorite of the specified user.
  587. pub async fn set_favorite(&self, favorite: Option<bool>, user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
  588. match favorite {
  589. None => Ok(()), // No change requested.
  590. Some(status) => Favorite::set_favorite(status, &self.uuid, user_uuid, conn).await,
  591. }
  592. }
  593. pub async fn get_folder_uuid(&self, user_uuid: &str, conn: &mut DbConn) -> Option<String> {
  594. db_run! {conn: {
  595. folders_ciphers::table
  596. .inner_join(folders::table)
  597. .filter(folders::user_uuid.eq(&user_uuid))
  598. .filter(folders_ciphers::cipher_uuid.eq(&self.uuid))
  599. .select(folders_ciphers::folder_uuid)
  600. .first::<String>(conn)
  601. .ok()
  602. }}
  603. }
  604. pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option<Self> {
  605. db_run! {conn: {
  606. ciphers::table
  607. .filter(ciphers::uuid.eq(uuid))
  608. .first::<CipherDb>(conn)
  609. .ok()
  610. .from_db()
  611. }}
  612. }
  613. pub async fn find_by_uuid_and_org(cipher_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option<Self> {
  614. db_run! {conn: {
  615. ciphers::table
  616. .filter(ciphers::uuid.eq(cipher_uuid))
  617. .filter(ciphers::organization_uuid.eq(org_uuid))
  618. .first::<CipherDb>(conn)
  619. .ok()
  620. .from_db()
  621. }}
  622. }
  623. // Find all ciphers accessible or visible to the specified user.
  624. //
  625. // "Accessible" means the user has read access to the cipher, either via
  626. // direct ownership, collection or via group access.
  627. //
  628. // "Visible" usually means the same as accessible, except when an org
  629. // owner/admin sets their account or group to have access to only selected
  630. // collections in the org (presumably because they aren't interested in
  631. // the other collections in the org). In this case, if `visible_only` is
  632. // true, then the non-interesting ciphers will not be returned. As a
  633. // result, those ciphers will not appear in "My Vault" for the org
  634. // owner/admin, but they can still be accessed via the org vault view.
  635. pub async fn find_by_user(user_uuid: &str, visible_only: bool, conn: &mut DbConn) -> Vec<Self> {
  636. if CONFIG.org_groups_enabled() {
  637. db_run! {conn: {
  638. let mut query = ciphers::table
  639. .left_join(ciphers_collections::table.on(
  640. ciphers::uuid.eq(ciphers_collections::cipher_uuid)
  641. ))
  642. .left_join(users_organizations::table.on(
  643. ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())
  644. .and(users_organizations::user_uuid.eq(user_uuid))
  645. .and(users_organizations::status.eq(UserOrgStatus::Confirmed as i32))
  646. ))
  647. .left_join(users_collections::table.on(
  648. ciphers_collections::collection_uuid.eq(users_collections::collection_uuid)
  649. // Ensure that users_collections::user_uuid is NULL for unconfirmed users.
  650. .and(users_organizations::user_uuid.eq(users_collections::user_uuid))
  651. ))
  652. .left_join(groups_users::table.on(
  653. groups_users::users_organizations_uuid.eq(users_organizations::uuid)
  654. ))
  655. .left_join(groups::table.on(
  656. groups::uuid.eq(groups_users::groups_uuid)
  657. ))
  658. .left_join(collections_groups::table.on(
  659. collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid).and(
  660. collections_groups::groups_uuid.eq(groups::uuid)
  661. )
  662. ))
  663. .filter(ciphers::user_uuid.eq(user_uuid)) // Cipher owner
  664. .or_filter(users_organizations::access_all.eq(true)) // access_all in org
  665. .or_filter(users_collections::user_uuid.eq(user_uuid)) // Access to collection
  666. .or_filter(groups::access_all.eq(true)) // Access via groups
  667. .or_filter(collections_groups::collections_uuid.is_not_null()) // Access via groups
  668. .into_boxed();
  669. if !visible_only {
  670. query = query.or_filter(
  671. users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin/owner
  672. );
  673. }
  674. query
  675. .select(ciphers::all_columns)
  676. .distinct()
  677. .load::<CipherDb>(conn).expect("Error loading ciphers").from_db()
  678. }}
  679. } else {
  680. db_run! {conn: {
  681. let mut query = ciphers::table
  682. .left_join(ciphers_collections::table.on(
  683. ciphers::uuid.eq(ciphers_collections::cipher_uuid)
  684. ))
  685. .left_join(users_organizations::table.on(
  686. ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())
  687. .and(users_organizations::user_uuid.eq(user_uuid))
  688. .and(users_organizations::status.eq(UserOrgStatus::Confirmed as i32))
  689. ))
  690. .left_join(users_collections::table.on(
  691. ciphers_collections::collection_uuid.eq(users_collections::collection_uuid)
  692. // Ensure that users_collections::user_uuid is NULL for unconfirmed users.
  693. .and(users_organizations::user_uuid.eq(users_collections::user_uuid))
  694. ))
  695. .filter(ciphers::user_uuid.eq(user_uuid)) // Cipher owner
  696. .or_filter(users_organizations::access_all.eq(true)) // access_all in org
  697. .or_filter(users_collections::user_uuid.eq(user_uuid)) // Access to collection
  698. .into_boxed();
  699. if !visible_only {
  700. query = query.or_filter(
  701. users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin/owner
  702. );
  703. }
  704. query
  705. .select(ciphers::all_columns)
  706. .distinct()
  707. .load::<CipherDb>(conn).expect("Error loading ciphers").from_db()
  708. }}
  709. }
  710. }
  711. // Find all ciphers visible to the specified user.
  712. pub async fn find_by_user_visible(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
  713. Self::find_by_user(user_uuid, true, conn).await
  714. }
  715. // Find all ciphers directly owned by the specified user.
  716. pub async fn find_owned_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
  717. db_run! {conn: {
  718. ciphers::table
  719. .filter(
  720. ciphers::user_uuid.eq(user_uuid)
  721. .and(ciphers::organization_uuid.is_null())
  722. )
  723. .load::<CipherDb>(conn).expect("Error loading ciphers").from_db()
  724. }}
  725. }
  726. pub async fn count_owned_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 {
  727. db_run! {conn: {
  728. ciphers::table
  729. .filter(ciphers::user_uuid.eq(user_uuid))
  730. .count()
  731. .first::<i64>(conn)
  732. .ok()
  733. .unwrap_or(0)
  734. }}
  735. }
  736. pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
  737. db_run! {conn: {
  738. ciphers::table
  739. .filter(ciphers::organization_uuid.eq(org_uuid))
  740. .load::<CipherDb>(conn).expect("Error loading ciphers").from_db()
  741. }}
  742. }
  743. pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 {
  744. db_run! {conn: {
  745. ciphers::table
  746. .filter(ciphers::organization_uuid.eq(org_uuid))
  747. .count()
  748. .first::<i64>(conn)
  749. .ok()
  750. .unwrap_or(0)
  751. }}
  752. }
  753. pub async fn find_by_folder(folder_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
  754. db_run! {conn: {
  755. folders_ciphers::table.inner_join(ciphers::table)
  756. .filter(folders_ciphers::folder_uuid.eq(folder_uuid))
  757. .select(ciphers::all_columns)
  758. .load::<CipherDb>(conn).expect("Error loading ciphers").from_db()
  759. }}
  760. }
  761. /// Find all ciphers that were deleted before the specified datetime.
  762. pub async fn find_deleted_before(dt: &NaiveDateTime, conn: &mut DbConn) -> Vec<Self> {
  763. db_run! {conn: {
  764. ciphers::table
  765. .filter(ciphers::deleted_at.lt(dt))
  766. .load::<CipherDb>(conn).expect("Error loading ciphers").from_db()
  767. }}
  768. }
  769. pub async fn get_collections(&self, user_id: String, conn: &mut DbConn) -> Vec<String> {
  770. if CONFIG.org_groups_enabled() {
  771. db_run! {conn: {
  772. ciphers_collections::table
  773. .filter(ciphers_collections::cipher_uuid.eq(&self.uuid))
  774. .inner_join(collections::table.on(
  775. collections::uuid.eq(ciphers_collections::collection_uuid)
  776. ))
  777. .left_join(users_organizations::table.on(
  778. users_organizations::org_uuid.eq(collections::org_uuid)
  779. .and(users_organizations::user_uuid.eq(user_id.clone()))
  780. ))
  781. .left_join(users_collections::table.on(
  782. users_collections::collection_uuid.eq(ciphers_collections::collection_uuid)
  783. .and(users_collections::user_uuid.eq(user_id.clone()))
  784. ))
  785. .left_join(groups_users::table.on(
  786. groups_users::users_organizations_uuid.eq(users_organizations::uuid)
  787. ))
  788. .left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)))
  789. .left_join(collections_groups::table.on(
  790. collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid)
  791. .and(collections_groups::groups_uuid.eq(groups::uuid))
  792. ))
  793. .filter(users_organizations::access_all.eq(true) // User has access all
  794. .or(users_collections::user_uuid.eq(user_id) // User has access to collection
  795. .and(users_collections::read_only.eq(false)))
  796. .or(groups::access_all.eq(true)) // Access via groups
  797. .or(collections_groups::collections_uuid.is_not_null() // Access via groups
  798. .and(collections_groups::read_only.eq(false)))
  799. )
  800. .select(ciphers_collections::collection_uuid)
  801. .load::<String>(conn).unwrap_or_default()
  802. }}
  803. } else {
  804. db_run! {conn: {
  805. ciphers_collections::table
  806. .filter(ciphers_collections::cipher_uuid.eq(&self.uuid))
  807. .inner_join(collections::table.on(
  808. collections::uuid.eq(ciphers_collections::collection_uuid)
  809. ))
  810. .inner_join(users_organizations::table.on(
  811. users_organizations::org_uuid.eq(collections::org_uuid)
  812. .and(users_organizations::user_uuid.eq(user_id.clone()))
  813. ))
  814. .left_join(users_collections::table.on(
  815. users_collections::collection_uuid.eq(ciphers_collections::collection_uuid)
  816. .and(users_collections::user_uuid.eq(user_id.clone()))
  817. ))
  818. .filter(users_organizations::access_all.eq(true) // User has access all
  819. .or(users_collections::user_uuid.eq(user_id) // User has access to collection
  820. .and(users_collections::read_only.eq(false)))
  821. )
  822. .select(ciphers_collections::collection_uuid)
  823. .load::<String>(conn).unwrap_or_default()
  824. }}
  825. }
  826. }
  827. pub async fn get_admin_collections(&self, user_id: String, conn: &mut DbConn) -> Vec<String> {
  828. if CONFIG.org_groups_enabled() {
  829. db_run! {conn: {
  830. ciphers_collections::table
  831. .filter(ciphers_collections::cipher_uuid.eq(&self.uuid))
  832. .inner_join(collections::table.on(
  833. collections::uuid.eq(ciphers_collections::collection_uuid)
  834. ))
  835. .left_join(users_organizations::table.on(
  836. users_organizations::org_uuid.eq(collections::org_uuid)
  837. .and(users_organizations::user_uuid.eq(user_id.clone()))
  838. ))
  839. .left_join(users_collections::table.on(
  840. users_collections::collection_uuid.eq(ciphers_collections::collection_uuid)
  841. .and(users_collections::user_uuid.eq(user_id.clone()))
  842. ))
  843. .left_join(groups_users::table.on(
  844. groups_users::users_organizations_uuid.eq(users_organizations::uuid)
  845. ))
  846. .left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)))
  847. .left_join(collections_groups::table.on(
  848. collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid)
  849. .and(collections_groups::groups_uuid.eq(groups::uuid))
  850. ))
  851. .filter(users_organizations::access_all.eq(true) // User has access all
  852. .or(users_collections::user_uuid.eq(user_id) // User has access to collection
  853. .and(users_collections::read_only.eq(false)))
  854. .or(groups::access_all.eq(true)) // Access via groups
  855. .or(collections_groups::collections_uuid.is_not_null() // Access via groups
  856. .and(collections_groups::read_only.eq(false)))
  857. .or(users_organizations::atype.le(UserOrgType::Admin as i32)) // User is admin or owner
  858. )
  859. .select(ciphers_collections::collection_uuid)
  860. .load::<String>(conn).unwrap_or_default()
  861. }}
  862. } else {
  863. db_run! {conn: {
  864. ciphers_collections::table
  865. .filter(ciphers_collections::cipher_uuid.eq(&self.uuid))
  866. .inner_join(collections::table.on(
  867. collections::uuid.eq(ciphers_collections::collection_uuid)
  868. ))
  869. .inner_join(users_organizations::table.on(
  870. users_organizations::org_uuid.eq(collections::org_uuid)
  871. .and(users_organizations::user_uuid.eq(user_id.clone()))
  872. ))
  873. .left_join(users_collections::table.on(
  874. users_collections::collection_uuid.eq(ciphers_collections::collection_uuid)
  875. .and(users_collections::user_uuid.eq(user_id.clone()))
  876. ))
  877. .filter(users_organizations::access_all.eq(true) // User has access all
  878. .or(users_collections::user_uuid.eq(user_id) // User has access to collection
  879. .and(users_collections::read_only.eq(false)))
  880. .or(users_organizations::atype.le(UserOrgType::Admin as i32)) // User is admin or owner
  881. )
  882. .select(ciphers_collections::collection_uuid)
  883. .load::<String>(conn).unwrap_or_default()
  884. }}
  885. }
  886. }
  887. /// Return a Vec with (cipher_uuid, collection_uuid)
  888. /// This is used during a full sync so we only need one query for all collections accessible.
  889. pub async fn get_collections_with_cipher_by_user(user_id: String, conn: &mut DbConn) -> Vec<(String, String)> {
  890. db_run! {conn: {
  891. ciphers_collections::table
  892. .inner_join(collections::table.on(
  893. collections::uuid.eq(ciphers_collections::collection_uuid)
  894. ))
  895. .inner_join(users_organizations::table.on(
  896. users_organizations::org_uuid.eq(collections::org_uuid).and(
  897. users_organizations::user_uuid.eq(user_id.clone())
  898. )
  899. ))
  900. .left_join(users_collections::table.on(
  901. users_collections::collection_uuid.eq(ciphers_collections::collection_uuid).and(
  902. users_collections::user_uuid.eq(user_id.clone())
  903. )
  904. ))
  905. .left_join(groups_users::table.on(
  906. groups_users::users_organizations_uuid.eq(users_organizations::uuid)
  907. ))
  908. .left_join(groups::table.on(
  909. groups::uuid.eq(groups_users::groups_uuid)
  910. ))
  911. .left_join(collections_groups::table.on(
  912. collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid).and(
  913. collections_groups::groups_uuid.eq(groups::uuid)
  914. )
  915. ))
  916. .or_filter(users_collections::user_uuid.eq(user_id)) // User has access to collection
  917. .or_filter(users_organizations::access_all.eq(true)) // User has access all
  918. .or_filter(users_organizations::atype.le(UserOrgType::Admin as i32)) // User is admin or owner
  919. .or_filter(groups::access_all.eq(true)) //Access via group
  920. .or_filter(collections_groups::collections_uuid.is_not_null()) //Access via group
  921. .select(ciphers_collections::all_columns)
  922. .distinct()
  923. .load::<(String, String)>(conn).unwrap_or_default()
  924. }}
  925. }
  926. }