From 2b2401be197e7a706e4669c9192d80fbcf16ffbb Mon Sep 17 00:00:00 2001
From: Miroslav Prasil <miroslav@prasil.info>
Date: Tue, 21 Aug 2018 17:31:01 +0100
Subject: [PATCH] Update affected users revision on cipher and folder change

---
 src/db/models/cipher.rs       | 23 ++++++++++++++++++++++-
 src/db/models/folder.rs       |  2 ++
 src/db/models/organization.rs | 23 +++++++++++++++++++++--
 3 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs
index 899e5dda..3faf1aea 100644
--- a/src/db/models/cipher.rs
+++ b/src/db/models/cipher.rs
@@ -3,7 +3,7 @@ use serde_json::Value as JsonValue;
 
 use uuid::Uuid;
 
-use super::{User, Organization, Attachment, FolderCipher, CollectionCipher, UserOrgType, UserOrgStatus};
+use super::{User, Organization, Attachment, FolderCipher, CollectionCipher, UserOrganization, UserOrgType, UserOrgStatus};
 
 #[derive(Debug, Identifiable, Queryable, Insertable, Associations)]
 #[table_name = "ciphers"]
@@ -122,7 +122,23 @@ impl Cipher {
         json_object
     }
 
+    pub fn update_users_revision(&self, conn: &DbConn) {
+        match self.user_uuid {
+            Some(ref user_uuid) => User::update_uuid_revision(&user_uuid, conn),
+            None => { // Belongs to Organization, need to update affected users
+                if let Some(ref org_uuid) = self.organization_uuid {
+                    UserOrganization::find_by_cipher_and_org(&self.uuid, &org_uuid, conn)
+                    .iter()
+                    .for_each(|user_org| {
+                        User::update_uuid_revision(&user_org.user_uuid, conn)
+                    });
+                }
+            }
+        };
+    }
+
     pub fn save(&mut self, conn: &DbConn) -> bool {
+        self.update_users_revision(conn);
         self.updated_at = Utc::now().naive_utc();
 
         match diesel::replace_into(ciphers::table)
@@ -134,6 +150,8 @@ impl Cipher {
     }
 
     pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
+        self.update_users_revision(conn);
+
         FolderCipher::delete_all_by_cipher(&self.uuid, &conn)?;
         CollectionCipher::delete_all_by_cipher(&self.uuid, &conn)?;
         Attachment::delete_all_by_cipher(&self.uuid, &conn)?;
@@ -157,6 +175,7 @@ impl Cipher {
             None => {
                 match folder_uuid {
                     Some(new_folder) => {
+                        self.update_users_revision(conn);
                         let folder_cipher = FolderCipher::new(&new_folder, &self.uuid);
                         folder_cipher.save(&conn).or(Err("Couldn't save folder setting"))
                     },
@@ -169,6 +188,7 @@ impl Cipher {
                         if current_folder == new_folder {
                             Ok(()) //nothing to do
                         } else {
+                            self.update_users_revision(conn);
                             match FolderCipher::find_by_folder_and_cipher(&current_folder, &self.uuid, &conn) {
                                 Some(current_folder) => {
                                     current_folder.delete(&conn).or(Err("Failed removing old folder mapping"))
@@ -181,6 +201,7 @@ impl Cipher {
                         }
                     },
                     None => {
+                        self.update_users_revision(conn);
                         match FolderCipher::find_by_folder_and_cipher(&current_folder, &self.uuid, &conn) {
                             Some(current_folder) => {
                                 current_folder.delete(&conn).or(Err("Failed removing old folder mapping"))
diff --git a/src/db/models/folder.rs b/src/db/models/folder.rs
index d9b90bf0..701a7da9 100644
--- a/src/db/models/folder.rs
+++ b/src/db/models/folder.rs
@@ -71,6 +71,7 @@ use db::schema::{folders, folders_ciphers};
 /// Database methods
 impl Folder {
     pub fn save(&mut self, conn: &DbConn) -> bool {
+        User::update_uuid_revision(&self.user_uuid, conn);
         self.updated_at = Utc::now().naive_utc();
 
         match diesel::replace_into(folders::table)
@@ -82,6 +83,7 @@ impl Folder {
     }
 
     pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
+        User::update_uuid_revision(&self.user_uuid, conn);
         FolderCipher::delete_all_by_folder(&self.uuid, &conn)?;
 
         diesel::delete(
diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs
index 78e381fa..d46d15da 100644
--- a/src/db/models/organization.rs
+++ b/src/db/models/organization.rs
@@ -109,8 +109,7 @@ impl UserOrganization {
 use diesel;
 use diesel::prelude::*;
 use db::DbConn;
-use db::schema::organizations;
-use db::schema::users_organizations;
+use db::schema::{organizations, users_organizations, users_collections, ciphers_collections};
 
 /// Database methods
 impl Organization {
@@ -297,6 +296,26 @@ impl UserOrganization {
             .filter(users_organizations::org_uuid.eq(org_uuid))
             .first::<Self>(&**conn).ok()
     }
+
+    pub fn find_by_cipher_and_org(cipher_uuid: &str, org_uuid: &str, conn: &DbConn) -> Vec<Self> {
+        users_organizations::table
+        .filter(users_organizations::org_uuid.eq(org_uuid))
+        .left_join(users_collections::table.on(
+            users_collections::user_uuid.eq(users_organizations::user_uuid)
+        ))
+        .left_join(ciphers_collections::table.on(
+            ciphers_collections::collection_uuid.eq(users_collections::collection_uuid).and(
+                ciphers_collections::cipher_uuid.eq(&cipher_uuid)
+            )
+        ))
+        .filter(
+            users_organizations::access_all.eq(true).or( // AccessAll..
+                ciphers_collections::cipher_uuid.eq(&cipher_uuid) // ..or access to collection with cipher
+            )
+        )
+        .select(users_organizations::all_columns)
+        .load::<Self>(&**conn).expect("Error loading user organizations")
+    }
 }