add bulk-access endpoint for collections (#5542)

This commit is contained in:
Stefan Melmuk 2025-02-04 09:42:02 +01:00 committed by GitHub
parent d2b36642a6
commit 3b6bccde97
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -38,6 +38,7 @@ pub fn routes() -> Vec<Route> {
post_organization_collections,
delete_organization_collection_member,
post_organization_collection_delete_member,
post_bulk_access_collections,
post_organization_collection_update,
put_organization_collection_update,
delete_organization_collection,
@ -129,17 +130,17 @@ struct OrganizationUpdateData {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct NewCollectionData {
struct FullCollectionData {
name: String,
groups: Vec<NewCollectionGroupData>,
users: Vec<NewCollectionMemberData>,
groups: Vec<CollectionGroupData>,
users: Vec<CollectionMembershipData>,
id: Option<CollectionId>,
external_id: Option<String>,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct NewCollectionGroupData {
struct CollectionGroupData {
hide_passwords: bool,
id: GroupId,
read_only: bool,
@ -148,7 +149,7 @@ struct NewCollectionGroupData {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct NewCollectionMemberData {
struct CollectionMembershipData {
hide_passwords: bool,
id: MembershipId,
read_only: bool,
@ -429,13 +430,13 @@ async fn _get_org_collections(org_id: &OrganizationId, conn: &mut DbConn) -> Val
async fn post_organization_collections(
org_id: OrganizationId,
headers: ManagerHeadersLoose,
data: Json<NewCollectionData>,
data: Json<FullCollectionData>,
mut conn: DbConn,
) -> JsonResult {
if org_id != headers.membership.org_uuid {
err!("Organization not found", "Organization id's do not match");
}
let data: NewCollectionData = data.into_inner();
let data: FullCollectionData = data.into_inner();
let Some(org) = Organization::find_by_uuid(&org_id, &mut conn).await else {
err!("Can't find organization details")
@ -488,29 +489,104 @@ async fn post_organization_collections(
Ok(Json(collection.to_json_details(&headers.membership.user_uuid, None, &mut conn).await))
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct BulkCollectionAccessData {
collection_ids: Vec<CollectionId>,
groups: Vec<CollectionGroupData>,
users: Vec<CollectionMembershipData>,
}
#[post("/organizations/<org_id>/collections/bulk-access", data = "<data>", rank = 1)]
async fn post_bulk_access_collections(
org_id: OrganizationId,
headers: ManagerHeadersLoose,
data: Json<BulkCollectionAccessData>,
mut conn: DbConn,
) -> EmptyResult {
if org_id != headers.membership.org_uuid {
err!("Organization not found", "Organization id's do not match");
}
let data: BulkCollectionAccessData = data.into_inner();
if Organization::find_by_uuid(&org_id, &mut conn).await.is_none() {
err!("Can't find organization details")
};
for col_id in data.collection_ids {
let Some(collection) = Collection::find_by_uuid_and_org(&col_id, &org_id, &mut conn).await else {
err!("Collection not found")
};
// update collection modification date
collection.save(&mut conn).await?;
log_event(
EventType::CollectionUpdated as i32,
&collection.uuid,
&org_id,
&headers.user.uuid,
headers.device.atype,
&headers.ip.ip,
&mut conn,
)
.await;
CollectionGroup::delete_all_by_collection(&col_id, &mut conn).await?;
for group in &data.groups {
CollectionGroup::new(col_id.clone(), group.id.clone(), group.read_only, group.hide_passwords, group.manage)
.save(&mut conn)
.await?;
}
CollectionUser::delete_all_by_collection(&col_id, &mut conn).await?;
for user in &data.users {
let Some(member) = Membership::find_by_uuid_and_org(&user.id, &org_id, &mut conn).await else {
err!("User is not part of organization")
};
if member.access_all {
continue;
}
CollectionUser::save(
&member.user_uuid,
&col_id,
user.read_only,
user.hide_passwords,
user.manage,
&mut conn,
)
.await?;
}
}
Ok(())
}
#[put("/organizations/<org_id>/collections/<col_id>", data = "<data>")]
async fn put_organization_collection_update(
org_id: OrganizationId,
col_id: CollectionId,
headers: ManagerHeaders,
data: Json<NewCollectionData>,
data: Json<FullCollectionData>,
conn: DbConn,
) -> JsonResult {
post_organization_collection_update(org_id, col_id, headers, data, conn).await
}
#[post("/organizations/<org_id>/collections/<col_id>", data = "<data>")]
#[post("/organizations/<org_id>/collections/<col_id>", data = "<data>", rank = 2)]
async fn post_organization_collection_update(
org_id: OrganizationId,
col_id: CollectionId,
headers: ManagerHeaders,
data: Json<NewCollectionData>,
data: Json<FullCollectionData>,
mut conn: DbConn,
) -> JsonResult {
if org_id != headers.org_id {
err!("Organization not found", "Organization id's do not match");
}
let data: NewCollectionData = data.into_inner();
let data: FullCollectionData = data.into_inner();
if Organization::find_by_uuid(&org_id, &mut conn).await.is_none() {
err!("Can't find organization details")
@ -781,7 +857,7 @@ async fn get_collection_users(
async fn put_collection_users(
org_id: OrganizationId,
col_id: CollectionId,
data: Json<Vec<MembershipData>>,
data: Json<Vec<CollectionMembershipData>>,
headers: ManagerHeaders,
mut conn: DbConn,
) -> EmptyResult {
@ -913,24 +989,6 @@ async fn post_org_keys(
})))
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct CollectionData {
id: CollectionId,
read_only: bool,
hide_passwords: bool,
manage: bool,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct MembershipData {
id: MembershipId,
read_only: bool,
hide_passwords: bool,
manage: bool,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct InviteData {
@ -1754,7 +1812,7 @@ use super::ciphers::CipherData;
#[serde(rename_all = "camelCase")]
struct ImportData {
ciphers: Vec<CipherData>,
collections: Vec<NewCollectionData>,
collections: Vec<FullCollectionData>,
collection_relationships: Vec<RelationsData>,
}
@ -2549,7 +2607,7 @@ struct GroupRequest {
#[serde(default)]
access_all: bool,
external_id: Option<String>,
collections: Vec<SelectedCollection>,
collections: Vec<CollectionData>,
users: Vec<MembershipId>,
}
@ -2570,14 +2628,14 @@ impl GroupRequest {
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct SelectedCollection {
struct CollectionData {
id: CollectionId,
read_only: bool,
hide_passwords: bool,
manage: bool,
}
impl SelectedCollection {
impl CollectionData {
pub fn to_collection_group(&self, groups_uuid: GroupId) -> CollectionGroup {
CollectionGroup::new(self.id.clone(), groups_uuid, self.read_only, self.hide_passwords, self.manage)
}
@ -2660,7 +2718,7 @@ async fn put_group(
async fn add_update_group(
mut group: Group,
collections: Vec<SelectedCollection>,
collections: Vec<CollectionData>,
members: Vec<MembershipId>,
org_id: OrganizationId,
headers: &AdminHeaders,