Skip to content

Commit

Permalink
add endpoint to bulk delete collections
Browse files Browse the repository at this point in the history
  • Loading branch information
stefan0xC committed Mar 19, 2023
1 parent ee59f13 commit 37d57a0
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 19 deletions.
57 changes: 47 additions & 10 deletions src/api/core/organizations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use crate::{
ApiResult, EmptyResult, JsonResult, JsonUpcase, JsonUpcaseVec, JsonVec, Notify, NumberOrString, PasswordData,
UpdateType,
},
auth::{decode_invite, AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders},
auth::{
decode_invite, AdminHeaders, Headers, ManagerHeaders, ManagerHeadersBulk, ManagerHeadersLoose, OwnerHeaders,
},
db::{models::*, DbConn},
error::Error,
mail,
Expand Down Expand Up @@ -39,6 +41,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,
Expand Down Expand Up @@ -538,14 +541,13 @@ 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(
async fn _delete_organization_collection(
org_id: String,
col_id: String,
headers: ManagerHeaders,
mut conn: DbConn,
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 {
Expand All @@ -556,17 +558,27 @@ async fn delete_organization_collection(
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")
}
}
}
}

#[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 {
Expand All @@ -580,9 +592,34 @@ 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, &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: String,
headers: ManagerHeadersBulk,
data: JsonUpcase<BulkCollectionIds>,
mut conn: DbConn,
) -> EmptyResult {
delete_organization_collection(org_id, col_id, headers, conn).await
let data: BulkCollectionIds = data.into_inner().data;
assert!(org_id == data.OrganizationId);

let headers = ManagerHeaders::from(headers);

for col_id in data.Ids {
_delete_organization_collection(org_id.clone(), col_id, &headers, &mut conn).await?
}
Ok(())
}

#[get("/organizations/<org_id>/collections/<coll_id>/details")]
Expand Down
60 changes: 51 additions & 9 deletions src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,10 @@ fn get_col_id(request: &Request<'_>) -> Option<String> {
None
}

async fn can_access_collection(col_id: String, user: &UserOrganization, conn: &mut DbConn) -> bool {
user.has_full_access() || Collection::has_access_by_collection_and_user_uuid(&col_id, &user.user_uuid, conn).await
}

/// The ManagerHeaders are used to check if you are at least a Manager
/// and have access to the specific collection provided via the <col_id>/collections/collectionId.
/// This does strict checking on the collection_id, ManagerHeadersLoose does not.
Expand All @@ -590,22 +594,15 @@ impl<'r> FromRequest<'r> for ManagerHeaders {

async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let headers = try_outcome!(OrgHeaders::from_request(request).await);

if headers.org_user_type >= UserOrgType::Manager {
match get_col_id(request) {
Some(col_id) => {
let mut conn = match DbConn::from_request(request).await {
Outcome::Success(conn) => conn,
_ => 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(col_id, &headers.org_user, &mut conn).await {
err_handler!("The current user isn't a manager for this collection")
}
}
Expand Down Expand Up @@ -677,6 +674,51 @@ impl From<ManagerHeadersLoose> for Headers {
}
}

// ManagerHeadersBulk is used to bulk delete the given collections and can be converted to
// ManagerHeaders
pub struct ManagerHeadersBulk {
pub host: String,
pub device: Device,
pub user: User,
pub org_user_type: UserOrgType,
pub ip: ClientIp,
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for ManagerHeadersBulk {
type Error = &'static str;

async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let headers = try_outcome!(OrgHeaders::from_request(request).await);

if headers.org_user_type < UserOrgType::Manager {
err_handler!("You need to be a Manager, Admin or Owner to call this endpoint")
}

// TODO: implement guard to check if user has access to the given collections

Outcome::Success(Self {
host: headers.host,
device: headers.device,
user: headers.user,
org_user_type: headers.org_user_type,
ip: headers.ip,
})
}
}

impl From<ManagerHeadersBulk> for ManagerHeaders {
fn from(h: ManagerHeadersBulk) -> ManagerHeaders {
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,
pub device: Device,
Expand Down

0 comments on commit 37d57a0

Please sign in to comment.