Browse Source

cipher readonly, hide_passwords

pull/2667/head
MFijak 3 years ago
committed by Maximilian Fijak
parent
commit
064a1e368e
  1. 7
      src/api/core/ciphers.rs
  2. 81
      src/db/models/cipher.rs
  3. 51
      src/db/models/group.rs

7
src/api/core/ciphers.rs

@ -1497,6 +1497,8 @@ pub struct CipherSyncData {
pub cipher_collections: HashMap<String, Vec<String>>,
pub user_organizations: HashMap<String, UserOrganization>,
pub user_collections: HashMap<String, CollectionUser>,
pub user_collections_groups: Vec<CollectionGroup>,
pub user_groups: Vec<Group>,
}
pub enum CipherSyncType {
@ -1552,6 +1554,9 @@ impl CipherSyncData {
.collect()
.await;
let user_collections_groups = CollectionGroup::find_by_user(user_uuid, conn).await;
let user_groups = Group::find_by_user(user_uuid, conn).await;
Self {
cipher_attachments,
cipher_folders,
@ -1559,6 +1564,8 @@ impl CipherSyncData {
cipher_collections,
user_organizations,
user_collections,
user_collections_groups,
user_groups
}
}
}

81
src/db/models/cipher.rs

@ -2,7 +2,7 @@ use crate::CONFIG;
use chrono::{Duration, NaiveDateTime, Utc};
use serde_json::Value;
use super::{Attachment, CollectionCipher, Favorite, FolderCipher, User, UserOrgStatus, UserOrgType, UserOrganization};
use super::{Attachment, CollectionCipher, Favorite, FolderCipher, User, UserOrgStatus, UserOrgType, UserOrganization, Group};
use crate::api::core::CipherSyncData;
@ -368,7 +368,7 @@ impl Cipher {
// Check whether this cipher is directly owned by the user, or is in
// a collection that the user has full access to. If so, there are no
// access restrictions.
if self.is_owned_by_user(user_uuid) || self.is_in_full_access_org(user_uuid, cipher_sync_data, conn).await {
if self.is_owned_by_user(user_uuid) || self.is_in_full_access_org(user_uuid, cipher_sync_data, conn).await || Group::is_in_full_access_group(user_uuid, conn).await {
return Some((false, false));
}
@ -376,14 +376,30 @@ impl Cipher {
let mut rows: Vec<(bool, bool)> = Vec::new();
if let Some(collections) = cipher_sync_data.cipher_collections.get(&self.uuid) {
for collection in collections {
//User permissions
if let Some(uc) = cipher_sync_data.user_collections.get(collection) {
rows.push((uc.read_only, uc.hide_passwords));
}
//Group permissions
if let Some(gc) = cipher_sync_data.user_collections_groups
.iter()
.find(|collection_group| collection_group.collections_uuid == collection.clone()) {
rows.push((gc.read_only, gc.hide_passwords));
}
}
}
rows
} else {
self.get_collections_access_flags(user_uuid, conn).await
let mut user_collections_access_flags = self.get_user_collections_access_flags(user_uuid, conn).await;
let mut group_collections_access_flags = self.get_group_collections_access_flags(user_uuid, conn).await;
let mut result = Vec::new();
result.append(&mut user_collections_access_flags);
result.append(&mut group_collections_access_flags);
result
};
if rows.is_empty() {
@ -410,7 +426,7 @@ impl Cipher {
Some((read_only, hide_passwords))
}
pub async fn get_collections_access_flags(&self, user_uuid: &str, conn: &DbConn) -> Vec<(bool, bool)> {
async fn get_user_collections_access_flags(&self, user_uuid: &str, conn: &DbConn) -> Vec<(bool, bool)> {
db_run! {conn: {
// Check whether this cipher is in any collections accessible to the
// user. If so, retrieve the access flags for each collection.
@ -423,7 +439,30 @@ impl Cipher {
.and(users_collections::user_uuid.eq(user_uuid))))
.select((users_collections::read_only, users_collections::hide_passwords))
.load::<(bool, bool)>(conn)
.expect("Error getting access restrictions")
.expect("Error getting user access restrictions")
}}
}
async fn get_group_collections_access_flags(&self, user_uuid: &str, conn: &DbConn) -> Vec<(bool, bool)> {
db_run! {conn: {
ciphers::table
.filter(ciphers::uuid.eq(&self.uuid))
.inner_join(ciphers_collections::table.on(
ciphers::uuid.eq(ciphers_collections::cipher_uuid)
))
.inner_join(collection_groups::table.on(
collection_groups::collections_uuid.eq(ciphers_collections::collection_uuid)
))
.inner_join(groups_users::table.on(
groups_users::groups_uuid.eq(collection_groups::groups_uuid)
))
.inner_join(users_organizations::table.on(
users_organizations::uuid.eq(groups_users::users_organizations_uuid)
))
.filter(users_organizations::user_uuid.eq(user_uuid))
.select((collection_groups::read_only, collection_groups::hide_passwords))
.load::<(bool, bool)>(conn)
.expect("Error getting group access restrictions")
}}
}
@ -476,10 +515,10 @@ impl Cipher {
// Find all ciphers accessible or visible to the specified user.
//
// "Accessible" means the user has read access to the cipher, either via
// direct ownership or via collection access.
// direct ownership, collection or via group access.
//
// "Visible" usually means the same as accessible, except when an org
// owner/admin sets their account to have access to only selected
// owner/admin sets their account or group to have access to only selected
// collections in the org (presumably because they aren't interested in
// the other collections in the org). In this case, if `visible_only` is
// true, then the non-interesting ciphers will not be returned. As a
@ -501,9 +540,20 @@ impl Cipher {
// Ensure that users_collections::user_uuid is NULL for unconfirmed users.
.and(users_organizations::user_uuid.eq(users_collections::user_uuid))
))
.left_join(groups_users::table.on(
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
))
.left_join(groups::table.on(
groups::uuid.eq(groups_users::groups_uuid)
))
.left_join(collection_groups::table.on(
collection_groups::collections_uuid.eq(ciphers_collections::collection_uuid)
))
.filter(ciphers::user_uuid.eq(user_uuid)) // Cipher owner
.or_filter(users_organizations::access_all.eq(true)) // access_all in org
.or_filter(users_collections::user_uuid.eq(user_uuid)) // Access to collection
.or_filter(groups::access_all.eq(true)) // Access via groups
.or_filter(collection_groups::collections_uuid.is_not_null()) // Access via groups
.into_boxed();
if !visible_only {
@ -629,11 +679,20 @@ impl Cipher {
users_collections::user_uuid.eq(user_id)
)
))
.filter(users_collections::user_uuid.eq(user_id).or( // User has access to collection
users_organizations::access_all.eq(true).or( // User has access all
users_organizations::atype.le(UserOrgType::Admin as i32) // User is admin or owner
)
.left_join(groups_users::table.on(
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
))
.left_join(groups::table.on(
groups::uuid.eq(groups_users::groups_uuid)
))
.left_join(collection_groups::table.on(
collection_groups::collections_uuid.eq(ciphers_collections::collection_uuid)
))
.or_filter(users_collections::user_uuid.eq(user_id)) // User has access to collection
.or_filter(users_organizations::access_all.eq(true)) // User has access all
.or_filter(users_organizations::atype.le(UserOrgType::Admin as i32)) // User is admin or owner
.or_filter(groups::access_all.eq(true)) //Access via group
.or_filter(collection_groups::collections_uuid.is_not_null()) //Access via group
.select(ciphers_collections::all_columns)
.load::<(String, String)>(conn).unwrap_or_default()
}}

51
src/db/models/group.rs

@ -148,6 +148,40 @@ impl Group {
}}
}
pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
db_run! { conn: {
groups::table
.inner_join(groups_users::table.on(
groups_users::groups_uuid.eq(groups::uuid)
))
.inner_join(users_organizations::table.on(
users_organizations::uuid.eq(groups_users::users_organizations_uuid)
))
.filter(users_organizations::user_uuid.eq(user_uuid))
.select(groups::all_columns)
.load::<GroupDb>(conn)
.expect("Error loading user groups")
.from_db()
}}
}
pub async fn is_in_full_access_group(user_uuid: &str, conn: &DbConn) -> bool {
db_run! { conn: {
groups::table
.inner_join(groups_users::table.on(
groups_users::groups_uuid.eq(groups::uuid)
))
.inner_join(users_organizations::table.on(
users_organizations::uuid.eq(groups_users::users_organizations_uuid)
))
.filter(users_organizations::user_uuid.eq(user_uuid))
.filter(groups::access_all.eq(true))
.select(groups::access_all)
.first::<bool>(conn)
.unwrap_or_default()
}}
}
pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
CollectionGroup::delete_all_by_group(&self.uuid, &conn).await?;
GroupUser::delete_all_by_group(&self.uuid, &conn).await?;
@ -243,6 +277,23 @@ impl CollectionGroup {
}}
}
pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
db_run! { conn: {
collection_groups::table
.inner_join(groups_users::table.on(
groups_users::groups_uuid.eq(collection_groups::groups_uuid)
))
.inner_join(users_organizations::table.on(
users_organizations::uuid.eq(groups_users::users_organizations_uuid)
))
.filter(users_organizations::user_uuid.eq(user_uuid))
.select(collection_groups::all_columns)
.load::<CollectionGroupDb>(conn)
.expect("Error loading user collection groups")
.from_db()
}}
}
pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
let group_users = GroupUser::find_by_group(&self.groups_uuid, conn).await;
for group_user in group_users {

Loading…
Cancel
Save