diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index f3d39849..7041d3cb 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -320,9 +320,37 @@ async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose, None => err!("User is not part of organization"), }; + // get all collection memberships for the current organization let coll_users = CollectionUser::find_by_organization(org_id, &mut conn).await; + // check if current user has full access to the organization (either directly or via any group) + let has_full_access_to_org = user_org.access_all + || (CONFIG.org_groups_enabled() + && GroupUser::has_full_access_by_member(org_id, &user_org.uuid, &mut conn).await); + for col in Collection::find_by_organization(org_id, &mut conn).await { + // assigned indicates whether the current user has access to the given collection + let mut assigned = has_full_access_to_org; + + // get the users assigned directly to the given collection + let users: Vec = coll_users + .iter() + .filter(|collection_user| collection_user.collection_uuid == col.uuid) + .map(|collection_user| { + // check if the current user is assigned to this collection directly + if collection_user.user_uuid == user_org.uuid { + assigned = true; + } + SelectionReadOnly::to_collection_user_details_read_only(collection_user).to_json() + }) + .collect(); + + // check if the current user has access to the given collection via a group + if !assigned && CONFIG.org_groups_enabled() { + assigned = GroupUser::has_access_to_collection_by_member(&col.uuid, &user_org.uuid, &mut conn).await; + } + + // get the group details for the given collection let groups: Vec = if CONFIG.org_groups_enabled() { CollectionGroup::find_by_collection(&col.uuid, &mut conn) .await @@ -332,29 +360,9 @@ async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose, }) .collect() } else { - // The Bitwarden clients seem to call this API regardless of whether groups are enabled, - // so just act as if there are no groups. Vec::with_capacity(0) }; - let mut assigned = false; - let users: Vec = coll_users - .iter() - .filter(|collection_user| collection_user.collection_uuid == col.uuid) - .map(|collection_user| { - // Remember `user_uuid` is swapped here with the `user_org.uuid` with a join during the `CollectionUser::find_by_organization` call. - // We check here if the current user is assigned to this collection or not. - if collection_user.user_uuid == user_org.uuid { - assigned = true; - } - SelectionReadOnly::to_collection_user_details_read_only(collection_user).to_json() - }) - .collect(); - - if user_org.access_all { - assigned = true; - } - let mut json_object = col.to_json(); json_object["Assigned"] = json!(assigned); json_object["Users"] = json!(users); diff --git a/src/db/models/group.rs b/src/db/models/group.rs index 670e3114..e50853e2 100644 --- a/src/db/models/group.rs +++ b/src/db/models/group.rs @@ -486,6 +486,39 @@ impl GroupUser { }} } + pub async fn has_access_to_collection_by_member( + collection_uuid: &str, + member_uuid: &str, + conn: &mut DbConn, + ) -> bool { + db_run! { conn: { + groups_users::table + .inner_join(collections_groups::table.on( + collections_groups::groups_uuid.eq(groups_users::groups_uuid) + )) + .filter(collections_groups::collections_uuid.eq(collection_uuid)) + .filter(groups_users::users_organizations_uuid.eq(member_uuid)) + .count() + .first::(conn) + .unwrap_or(0) != 0 + }} + } + + pub async fn has_full_access_by_member(org_uuid: &str, member_uuid: &str, conn: &mut DbConn) -> bool { + db_run! { conn: { + groups_users::table + .inner_join(groups::table.on( + groups::uuid.eq(groups_users::groups_uuid) + )) + .filter(groups::organizations_uuid.eq(org_uuid)) + .filter(groups::access_all.eq(true)) + .filter(groups_users::users_organizations_uuid.eq(member_uuid)) + .count() + .first::(conn) + .unwrap_or(0) != 0 + }} + } + pub async fn update_user_revision(&self, conn: &mut DbConn) { match UserOrganization::find_by_uuid(&self.users_organizations_uuid, conn).await { Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await,