diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 5c11b265..2c582529 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -472,7 +472,7 @@ fn send_invite(org_id: String, data: JsonUpcase, headers: AdminHeade None => err!("Invalid type"), }; - if new_type != UserOrgType::User && headers.org_user_type != UserOrgType::Owner { + if new_type != UserOrgType::User && !headers.claims.is_organization_owner(&org_id) { err!("Only Owners can invite Managers, Admins or Owners") } @@ -662,7 +662,7 @@ fn confirm_invite( None => err!("The specified user isn't a member of the organization"), }; - if user_to_confirm.atype != UserOrgType::User && headers.org_user_type != UserOrgType::Owner { + if user_to_confirm.atype != UserOrgType::User && !headers.claims.is_organization_owner(&org_id) { err!("Only Owners can confirm Managers, Admins or Owners") } @@ -742,12 +742,12 @@ fn edit_user( if new_type != user_to_edit.atype && (user_to_edit.atype >= UserOrgType::Admin || new_type >= UserOrgType::Admin) - && headers.org_user_type != UserOrgType::Owner + && !headers.claims.is_organization_owner(&org_id) { err!("Only Owners can grant and remove Admin or Owner privileges") } - if user_to_edit.atype == UserOrgType::Owner && headers.org_user_type != UserOrgType::Owner { + if user_to_edit.atype == UserOrgType::Owner && !headers.claims.is_organization_owner(&org_id) { err!("Only Owners can edit Owner users") } @@ -790,7 +790,7 @@ fn delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn: None => err!("User to delete isn't member of the organization"), }; - if user_to_delete.atype != UserOrgType::User && headers.org_user_type != UserOrgType::Owner { + if user_to_delete.atype != UserOrgType::User && !headers.claims.is_organization_owner(&org_id) { err!("Only Owners can delete Admins or Owners") } diff --git a/src/auth.rs b/src/auth.rs index 38425306..a80cb07b 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -4,7 +4,6 @@ use crate::util::read_file; use chrono::{Duration, Utc}; use once_cell::sync::Lazy; -use num_traits::FromPrimitive; use jsonwebtoken::{self, Algorithm, Header, EncodingKey, DecodingKey}; use serde::de::DeserializeOwned; @@ -76,7 +75,7 @@ pub fn decode_admin(token: &str) -> Result { decode_jwt(token, JWT_ADMIN_ISSUER.to_string()) } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct LoginJWTClaims { // Not before pub nbf: i64, @@ -107,6 +106,42 @@ pub struct LoginJWTClaims { pub amr: Vec, } +impl LoginJWTClaims { + pub fn is_organization_owner(&self, org_uuid: &str) -> bool { + if self.orgowner.contains(&org_uuid.to_string()) { + return true; + } + + false + } + + pub fn is_organization_admin(&self, org_uuid: &str) -> bool { + if self.orgadmin.contains(&org_uuid.to_string()) { + return true; + } + + self.is_organization_owner(&org_uuid) + } + + #[allow(dead_code)] + pub fn is_organization_manager(&self, org_uuid: &str) -> bool { + if self.orgmanager.contains(&org_uuid.to_string()) { + return true; + } + + self.is_organization_admin(&org_uuid) + } + + #[allow(dead_code)] + pub fn is_organization_user(&self, org_uuid: &str) -> bool { + if self.orguser.contains(&org_uuid.to_string()) { + return true; + } + + self.is_organization_manager(&org_uuid) + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct InviteJWTClaims { // Not before @@ -216,13 +251,14 @@ pub fn generate_admin_claims() -> AdminJWTClaims { use rocket::request::{self, FromRequest, Request}; use rocket::Outcome; -use crate::db::models::{Device, User, UserOrgStatus, UserOrgType, UserOrganization}; +use crate::db::models::{Device, User, UserOrgStatus, UserOrganization}; use crate::db::DbConn; pub struct Headers { pub host: String, pub device: Device, pub user: User, + pub claims: LoginJWTClaims, } impl<'a, 'r> FromRequest<'a, 'r> for Headers { @@ -273,9 +309,10 @@ impl<'a, 'r> FromRequest<'a, 'r> for Headers { Ok(claims) => claims, Err(_) => err_handler!("Invalid claim"), }; + let claim = claims.clone(); - let device_uuid = claims.device; - let user_uuid = claims.sub; + let device_uuid = claim.device; + let user_uuid = claim.sub; let conn = match request.guard::() { Outcome::Success(conn) => conn, @@ -292,11 +329,11 @@ impl<'a, 'r> FromRequest<'a, 'r> for Headers { None => err_handler!("Device has no user associated"), }; - if user.security_stamp != claims.sstamp { + if user.security_stamp != claim.sstamp { err_handler!("Invalid security stamp") } - Outcome::Success(Headers { host, device, user }) + Outcome::Success(Headers { host, device, user, claims }) } } @@ -304,7 +341,8 @@ pub struct OrgHeaders { pub host: String, pub device: Device, pub user: User, - pub org_user_type: UserOrgType, + pub claims: LoginJWTClaims, + pub context_org_uuid: String, } // org_id is usually the second param ("/organizations/") @@ -336,13 +374,19 @@ impl<'a, 'r> FromRequest<'a, 'r> for OrgHeaders { Outcome::Success(headers) => { match get_org_id(request) { Some(org_id) => { + // This check would have been sufficient if the claims would be updated. + // Since this is not the case, we keep the database check here and disable this check for now. + // if !headers.claims.is_organization_user(&org_id) { + // err_handler!("The current user isn't member of the organization") + // } + let conn = match request.guard::() { Outcome::Success(conn) => conn, _ => err_handler!("Error getting DB"), }; let user = headers.user; - let org_user = match UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &conn) { + match UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &conn) { Some(user) => { if user.status == UserOrgStatus::Confirmed as i32 { user @@ -357,14 +401,8 @@ impl<'a, 'r> FromRequest<'a, 'r> for OrgHeaders { host: headers.host, device: headers.device, user, - org_user_type: { - if let Some(org_usr_type) = UserOrgType::from_i32(org_user.atype) { - org_usr_type - } else { - // This should only happen if the DB is corrupted - err_handler!("Unknown user type in the database") - } - }, + claims: headers.claims, + context_org_uuid: org_id, }) }, _ => err_handler!("Error getting the organization id"), @@ -378,7 +416,8 @@ pub struct AdminHeaders { pub host: String, pub device: Device, pub user: User, - pub org_user_type: UserOrgType, + pub claims: LoginJWTClaims, + pub context_org_uuid: String, } impl<'a, 'r> FromRequest<'a, 'r> for AdminHeaders { @@ -389,12 +428,13 @@ impl<'a, 'r> FromRequest<'a, 'r> for AdminHeaders { Outcome::Forward(_) => Outcome::Forward(()), Outcome::Failure(f) => Outcome::Failure(f), Outcome::Success(headers) => { - if headers.org_user_type >= UserOrgType::Admin { + if headers.claims.is_organization_admin(&headers.context_org_uuid) { Outcome::Success(Self { host: headers.host, device: headers.device, user: headers.user, - org_user_type: headers.org_user_type, + claims: headers.claims, + context_org_uuid: headers.context_org_uuid, }) } else { err_handler!("You need to be Admin or Owner to call this endpoint") @@ -409,7 +449,8 @@ impl Into for AdminHeaders { Headers { host: self.host, device: self.device, - user: self.user + user: self.user, + claims: self.claims } } } @@ -418,6 +459,8 @@ pub struct OwnerHeaders { pub host: String, pub device: Device, pub user: User, + pub claims: LoginJWTClaims, + pub context_org_uuid: String, } impl<'a, 'r> FromRequest<'a, 'r> for OwnerHeaders { @@ -428,11 +471,13 @@ impl<'a, 'r> FromRequest<'a, 'r> for OwnerHeaders { Outcome::Forward(_) => Outcome::Forward(()), Outcome::Failure(f) => Outcome::Failure(f), Outcome::Success(headers) => { - if headers.org_user_type == UserOrgType::Owner { + if headers.claims.is_organization_owner(&headers.context_org_uuid) { Outcome::Success(Self { host: headers.host, device: headers.device, user: headers.user, + claims: headers.claims, + context_org_uuid: headers.context_org_uuid, }) } else { err_handler!("You need to be Owner to call this endpoint")