diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index bd4c1a77e1..83f2161879 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -10,7 +10,10 @@ use crate::{ core::{log_event, two_factor, CipherSyncData, CipherSyncType}, EmptyResult, JsonResult, Notify, PasswordOrOtpData, UpdateType, }, - auth::{decode_invite, AdminHeaders, ClientVersion, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders}, + auth::{ + decode_invite, AdminHeaders, ClientVersion, Headers, ManagerHeaders, ManagerHeadersLoose, OrgMemberHeaders, + OwnerHeaders, + }, db::{models::*, DbConn}, mail, util::{convert_json_key_lcase_first, NumberOrString}, @@ -213,6 +216,9 @@ async fn delete_organization( headers: OwnerHeaders, mut conn: DbConn, ) -> EmptyResult { + if org_id != headers.org_id { + err!("Organization not found", "Organization id's do not match"); + } let data: PasswordOrOtpData = data.into_inner(); data.validate(&headers.user, true, &mut conn).await?; @@ -261,7 +267,10 @@ async fn leave_organization(org_id: OrganizationId, headers: Headers, mut conn: } #[get("/organizations/")] -async fn get_organization(org_id: OrganizationId, _headers: OwnerHeaders, mut conn: DbConn) -> JsonResult { +async fn get_organization(org_id: OrganizationId, headers: OwnerHeaders, mut conn: DbConn) -> JsonResult { + if org_id != headers.org_id { + err!("Organization not found", "Organization id's do not match"); + } match Organization::find_by_uuid(&org_id, &mut conn).await { Some(organization) => Ok(Json(organization.to_json())), None => err!("Can't find organization details"), @@ -285,14 +294,18 @@ async fn post_organization( data: Json, mut conn: DbConn, ) -> JsonResult { + if org_id != headers.org_id { + err!("Organization not found", "Organization id's do not match"); + } + let data: OrganizationUpdateData = data.into_inner(); let Some(mut org) = Organization::find_by_uuid(&org_id, &mut conn).await else { - err!("Can't find organization details") + err!("Organization not found") }; org.name = data.name; - org.billing_email = data.billing_email; + org.billing_email = data.billing_email.to_lowercase(); org.save(&mut conn).await?; @@ -778,10 +791,9 @@ struct OrgIdData { } #[get("/ciphers/organization-details?")] -async fn get_org_details(data: OrgIdData, headers: Headers, mut conn: DbConn) -> JsonResult { - if Membership::find_confirmed_by_user_and_org(&headers.user.uuid, &data.organization_id, &mut conn).await.is_none() - { - err_code!("Resource not found.", rocket::http::Status::NotFound.code); +async fn get_org_details(data: OrgIdData, headers: OrgMemberHeaders, mut conn: DbConn) -> JsonResult { + if data.organization_id != headers.org_id { + err_code!("Resource not found.", "Organization id's do not match", rocket::http::Status::NotFound.code); } Ok(Json(json!({ diff --git a/src/auth.rs b/src/auth.rs index e1dd71f68b..92913a67ab 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -557,24 +557,17 @@ impl<'r> FromRequest<'r> for OrgHeaders { // but there are cases where it is a query value. // First check the path, if this is not a valid uuid, try the query values. let url_org_id: Option = { - let mut url_org_id = None; - if let Some(Ok(org_id)) = request.param::<&str>(1) { - if uuid::Uuid::parse_str(org_id).is_ok() { - url_org_id = Some(org_id.to_string().into()); - } - } - - if let Some(Ok(org_id)) = request.query_value::<&str>("organizationId") { - if uuid::Uuid::parse_str(org_id).is_ok() { - url_org_id = Some(org_id.to_string().into()); - } + if let Some(Ok(org_id)) = request.param::(1) { + Some(org_id.clone()) + } else if let Some(Ok(org_id)) = request.query_value::("organizationId") { + Some(org_id.clone()) + } else { + None } - - url_org_id }; match url_org_id { - Some(org_id) => { + Some(org_id) if uuid::Uuid::parse_str(&org_id).is_ok() => { let mut conn = match DbConn::from_request(request).await { Outcome::Success(conn) => conn, _ => err_handler!("Error getting DB"), @@ -794,6 +787,7 @@ pub struct OwnerHeaders { pub device: Device, pub user: User, pub ip: ClientIp, + pub org_id: OrganizationId, } #[rocket::async_trait] @@ -807,6 +801,7 @@ impl<'r> FromRequest<'r> for OwnerHeaders { device: headers.device, user: headers.user, ip: headers.ip, + org_id: headers.membership.org_uuid, }) } else { err_handler!("You need to be Owner to call this endpoint") @@ -814,6 +809,30 @@ impl<'r> FromRequest<'r> for OwnerHeaders { } } +pub struct OrgMemberHeaders { + pub host: String, + pub user: User, + pub org_id: OrganizationId, +} + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for OrgMemberHeaders { + type Error = &'static str; + + async fn from_request(request: &'r Request<'_>) -> Outcome { + let headers = try_outcome!(OrgHeaders::from_request(request).await); + if headers.membership_type >= MembershipType::User { + Outcome::Success(Self { + host: headers.host, + user: headers.user, + org_id: headers.membership.org_uuid, + }) + } else { + err_handler!("You need to be a Member of the Organization to call this endpoint") + } + } +} + // // Client IP address detection //