commit a428f05e772d811f7e23fe0cb997e20ea93db8e2
parent 5800aceb2d065a23899d277f91865f1d5b9ef297
Author: Daniel GarcĂa <dani-garcia@users.noreply.github.com>
Date: Fri, 24 Mar 2023 17:09:56 +0100
Merge pull request #3354 from stefan0xC/bulk-delete-endpoints
add endpoints to bulk delete collections/groups
Diffstat:
2 files changed, 112 insertions(+), 25 deletions(-)
diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs
@@ -39,6 +39,7 @@ pub fn routes() -> Vec<Route> {
put_organization_collection_update,
delete_organization_collection,
post_organization_collection_delete,
+ bulk_delete_organization_collections,
get_org_details,
get_org_users,
send_invite,
@@ -81,6 +82,7 @@ pub fn routes() -> Vec<Route> {
get_group_details,
delete_group,
post_delete_group,
+ bulk_delete_groups,
get_group_users,
put_group_users,
get_user_groups,
@@ -537,28 +539,27 @@ async fn post_organization_collection_delete_user(
delete_organization_collection_user(org_id, col_id, org_user_id, headers, conn).await
}
-#[delete("/organizations/<org_id>/collections/<col_id>")]
-async fn delete_organization_collection(
- org_id: String,
- col_id: String,
- headers: ManagerHeaders,
- mut conn: DbConn,
+async fn _delete_organization_collection(
+ org_id: &str,
+ col_id: &str,
+ headers: &ManagerHeaders,
+ conn: &mut DbConn,
) -> EmptyResult {
- match Collection::find_by_uuid(&col_id, &mut conn).await {
+ match Collection::find_by_uuid(col_id, conn).await {
None => err!("Collection not found"),
Some(collection) => {
if collection.org_uuid == org_id {
log_event(
EventType::CollectionDeleted as i32,
&collection.uuid,
- org_id,
+ org_id.to_string(),
headers.user.uuid.clone(),
headers.device.atype,
&headers.ip.ip,
- &mut conn,
+ conn,
)
.await;
- collection.delete(&mut conn).await
+ collection.delete(conn).await
} else {
err!("Collection and Organization id do not match")
}
@@ -566,6 +567,16 @@ async fn delete_organization_collection(
}
}
+#[delete("/organizations/<org_id>/collections/<col_id>")]
+async fn delete_organization_collection(
+ org_id: String,
+ col_id: String,
+ headers: ManagerHeaders,
+ mut conn: DbConn,
+) -> EmptyResult {
+ _delete_organization_collection(&org_id, &col_id, &headers, &mut conn).await
+}
+
#[derive(Deserialize, Debug)]
#[allow(non_snake_case, dead_code)]
struct DeleteCollectionData {
@@ -579,9 +590,38 @@ async fn post_organization_collection_delete(
col_id: String,
headers: ManagerHeaders,
_data: JsonUpcase<DeleteCollectionData>,
- conn: DbConn,
+ mut conn: DbConn,
) -> EmptyResult {
- delete_organization_collection(org_id, col_id, headers, conn).await
+ _delete_organization_collection(&org_id, &col_id, &headers, &mut conn).await
+}
+
+#[derive(Deserialize, Debug)]
+#[allow(non_snake_case)]
+struct BulkCollectionIds {
+ Ids: Vec<String>,
+ OrganizationId: String,
+}
+
+#[delete("/organizations/<org_id>/collections", data = "<data>")]
+async fn bulk_delete_organization_collections(
+ org_id: &str,
+ headers: ManagerHeadersLoose,
+ data: JsonUpcase<BulkCollectionIds>,
+ mut conn: DbConn,
+) -> EmptyResult {
+ let data: BulkCollectionIds = data.into_inner().data;
+ if org_id != data.OrganizationId {
+ err!("OrganizationId mismatch");
+ }
+
+ let collections = data.Ids;
+
+ let headers = ManagerHeaders::from_loose(headers, &collections, &mut conn).await?;
+
+ for col_id in collections {
+ _delete_organization_collection(org_id, &col_id, &headers, &mut conn).await?
+ }
+ Ok(())
}
#[get("/organizations/<org_id>/collections/<coll_id>/details")]
@@ -2363,17 +2403,21 @@ async fn get_group_details(_org_id: String, group_id: String, _headers: AdminHea
}
#[post("/organizations/<org_id>/groups/<group_id>/delete")]
-async fn post_delete_group(org_id: String, group_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
- delete_group(org_id, group_id, headers, conn).await
+async fn post_delete_group(org_id: String, group_id: String, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult {
+ _delete_group(org_id, group_id, &headers, &mut conn).await
}
#[delete("/organizations/<org_id>/groups/<group_id>")]
async fn delete_group(org_id: String, group_id: String, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult {
+ _delete_group(org_id, group_id, &headers, &mut conn).await
+}
+
+async fn _delete_group(org_id: String, group_id: String, headers: &AdminHeaders, conn: &mut DbConn) -> EmptyResult {
if !CONFIG.org_groups_enabled() {
err!("Group support is disabled");
}
- let group = match Group::find_by_uuid(&group_id, &mut conn).await {
+ let group = match Group::find_by_uuid(&group_id, conn).await {
Some(group) => group,
_ => err!("Group not found"),
};
@@ -2385,11 +2429,30 @@ async fn delete_group(org_id: String, group_id: String, headers: AdminHeaders, m
headers.user.uuid.clone(),
headers.device.atype,
&headers.ip.ip,
- &mut conn,
+ conn,
)
.await;
- group.delete(&mut conn).await
+ group.delete(conn).await
+}
+
+#[delete("/organizations/<org_id>/groups", data = "<data>")]
+async fn bulk_delete_groups(
+ org_id: String,
+ data: JsonUpcase<OrgBulkIds>,
+ headers: AdminHeaders,
+ mut conn: DbConn,
+) -> EmptyResult {
+ if !CONFIG.org_groups_enabled() {
+ err!("Group support is disabled");
+ }
+
+ let data: OrgBulkIds = data.into_inner().data;
+
+ for group_id in data.Ids {
+ _delete_group(org_id.clone(), group_id, &headers, &mut conn).await?
+ }
+ Ok(())
}
#[get("/organizations/<_org_id>/groups/<group_id>")]
diff --git a/src/auth.rs b/src/auth.rs
@@ -598,14 +598,7 @@ impl<'r> FromRequest<'r> for ManagerHeaders {
_ => err_handler!("Error getting DB"),
};
- if !headers.org_user.has_full_access()
- && !Collection::has_access_by_collection_and_user_uuid(
- &col_id,
- &headers.org_user.user_uuid,
- &mut conn,
- )
- .await
- {
+ if !can_access_collection(&headers.org_user, &col_id, &mut conn).await {
err_handler!("The current user isn't a manager for this collection")
}
}
@@ -642,6 +635,7 @@ pub struct ManagerHeadersLoose {
pub host: String,
pub device: Device,
pub user: User,
+ pub org_user: UserOrganization,
pub org_user_type: UserOrgType,
pub ip: ClientIp,
}
@@ -657,6 +651,7 @@ impl<'r> FromRequest<'r> for ManagerHeadersLoose {
host: headers.host,
device: headers.device,
user: headers.user,
+ org_user: headers.org_user,
org_user_type: headers.org_user_type,
ip: headers.ip,
})
@@ -676,6 +671,35 @@ impl From<ManagerHeadersLoose> for Headers {
}
}
}
+async fn can_access_collection(org_user: &UserOrganization, col_id: &str, conn: &mut DbConn) -> bool {
+ org_user.has_full_access()
+ || Collection::has_access_by_collection_and_user_uuid(col_id, &org_user.user_uuid, conn).await
+}
+
+impl ManagerHeaders {
+ pub async fn from_loose(
+ h: ManagerHeadersLoose,
+ collections: &Vec<String>,
+ conn: &mut DbConn,
+ ) -> Result<ManagerHeaders, Error> {
+ for col_id in collections {
+ if uuid::Uuid::parse_str(col_id).is_err() {
+ err!("Collection Id is malformed!");
+ }
+ if !can_access_collection(&h.org_user, col_id, conn).await {
+ err!("You don't have access to all collections!");
+ }
+ }
+
+ Ok(ManagerHeaders {
+ host: h.host,
+ device: h.device,
+ user: h.user,
+ org_user_type: h.org_user_type,
+ ip: h.ip,
+ })
+ }
+}
pub struct OwnerHeaders {
pub host: String,