Merge pull request #1125 from jjlin/org-cipher-visibility

Hide ciphers from non-selected collections for org owners/admins
This commit is contained in:
Daniel García 2020-09-10 23:19:14 +02:00 committed by GitHub
commit a0d92a167c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 36 deletions

View File

@ -82,7 +82,7 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult {
let policies = OrgPolicy::find_by_user(&headers.user.uuid, &conn); let policies = OrgPolicy::find_by_user(&headers.user.uuid, &conn);
let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect(); let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn); let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn);
let ciphers_json: Vec<Value> = ciphers let ciphers_json: Vec<Value> = ciphers
.iter() .iter()
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)) .map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn))
@ -107,7 +107,7 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult {
#[get("/ciphers")] #[get("/ciphers")]
fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult { fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult {
let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn); let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn);
let ciphers_json: Vec<Value> = ciphers let ciphers_json: Vec<Value> = ciphers
.iter() .iter()

View File

@ -382,39 +382,58 @@ impl Cipher {
}} }}
} }
// Find all ciphers accessible to user // Find all ciphers accessible or visible to the specified user.
pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> { //
// "Accessible" means the user has read access to the cipher, either via
// direct ownership or via collection access.
//
// "Visible" usually means the same as accessible, except when an org
// owner/admin sets their account 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
// result, those ciphers will not appear in "My Vault" for the org
// owner/admin, but they can still be accessed via the org vault view.
pub fn find_by_user(user_uuid: &str, visible_only: bool, conn: &DbConn) -> Vec<Self> {
db_run! {conn: { db_run! {conn: {
ciphers::table let mut query = ciphers::table
.left_join(users_organizations::table.on(
ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable()).and(
users_organizations::user_uuid.eq(user_uuid).and(
users_organizations::status.eq(UserOrgStatus::Confirmed as i32)
)
)
))
.left_join(ciphers_collections::table.on( .left_join(ciphers_collections::table.on(
ciphers::uuid.eq(ciphers_collections::cipher_uuid) ciphers::uuid.eq(ciphers_collections::cipher_uuid)
)) ))
.left_join(users_organizations::table.on(
ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())
.and(users_organizations::user_uuid.eq(user_uuid))
.and(users_organizations::status.eq(UserOrgStatus::Confirmed as i32))
))
.left_join(users_collections::table.on( .left_join(users_collections::table.on(
ciphers_collections::collection_uuid.eq(users_collections::collection_uuid) ciphers_collections::collection_uuid.eq(users_collections::collection_uuid)
// Ensure that users_collections::user_uuid is NULL for unconfirmed users.
.and(users_organizations::user_uuid.eq(users_collections::user_uuid))
)) ))
.filter(ciphers::user_uuid.eq(user_uuid).or( // Cipher owner .filter(ciphers::user_uuid.eq(user_uuid)) // Cipher owner
users_organizations::access_all.eq(true).or( // access_all in Organization .or_filter(users_organizations::access_all.eq(true)) // access_all in org
users_organizations::atype.le(UserOrgType::Admin as i32).or( // Org admin or owner .or_filter(users_collections::user_uuid.eq(user_uuid)) // Access to collection
users_collections::user_uuid.eq(user_uuid).and( // Access to Collection .into_boxed();
users_organizations::status.eq(UserOrgStatus::Confirmed as i32)
) if !visible_only {
) query = query.or_filter(
) users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin/owner
)) );
}
query
.select(ciphers::all_columns) .select(ciphers::all_columns)
.distinct() .distinct()
.load::<CipherDb>(conn).expect("Error loading ciphers").from_db() .load::<CipherDb>(conn).expect("Error loading ciphers").from_db()
}} }}
} }
// Find all ciphers directly owned by user // Find all ciphers visible to the specified user.
pub fn find_by_user_visible(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
Self::find_by_user(user_uuid, true, conn)
}
// Find all ciphers directly owned by the specified user.
pub fn find_owned_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> { pub fn find_owned_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
db_run! {conn: { db_run! {conn: {
ciphers::table ciphers::table

View File

@ -208,21 +208,20 @@ impl Collection {
match UserOrganization::find_by_user_and_org(&user_uuid, &self.org_uuid, &conn) { match UserOrganization::find_by_user_and_org(&user_uuid, &self.org_uuid, &conn) {
None => false, // Not in Org None => false, // Not in Org
Some(user_org) => { Some(user_org) => {
if user_org.access_all { if user_org.has_full_access() {
true return true;
} else {
db_run! { conn: {
users_collections::table
.inner_join(collections::table)
.filter(users_collections::collection_uuid.eq(&self.uuid))
.filter(users_collections::user_uuid.eq(&user_uuid))
.filter(users_collections::read_only.eq(false))
.select(collections::all_columns)
.first::<CollectionDb>(conn)
.ok()
.is_some() // Read only or no access to collection
}}
} }
db_run! { conn: {
users_collections::table
.filter(users_collections::collection_uuid.eq(&self.uuid))
.filter(users_collections::user_uuid.eq(user_uuid))
.filter(users_collections::read_only.eq(false))
.count()
.first::<i64>(conn)
.ok()
.unwrap_or(0) != 0
}}
} }
} }
} }