diff --git a/server/svix-server/src/core/mod.rs b/server/svix-server/src/core/mod.rs index 6d33c2064..cf5097c49 100644 --- a/server/svix-server/src/core/mod.rs +++ b/server/svix-server/src/core/mod.rs @@ -7,6 +7,7 @@ pub mod idempotency; pub mod message_app; pub mod operational_webhooks; pub mod otel_spans; +pub mod permissions; pub mod run_with_retries; pub mod security; pub mod types; diff --git a/server/svix-server/src/core/permissions.rs b/server/svix-server/src/core/permissions.rs new file mode 100644 index 000000000..3fef0b9c6 --- /dev/null +++ b/server/svix-server/src/core/permissions.rs @@ -0,0 +1,107 @@ +use axum::{ + async_trait, + extract::{FromRequest, Path, RequestParts}, + Extension, +}; +use sea_orm::DatabaseConnection; + +use crate::{ + ctx, + db::models::application, + error::{Error, HttpError, Result}, +}; + +use super::{ + security::{permissions_from_bearer, AccessLevel}, + types::{ApplicationIdOrUid, OrganizationId}, +}; + +pub struct Organization { + pub org_id: OrganizationId, +} + +#[async_trait] +impl FromRequest for Organization +where + B: Send, +{ + type Rejection = Error; + + async fn from_request(req: &mut RequestParts) -> Result { + let permissions = permissions_from_bearer(req).await?; + + let org_id = match permissions.access_level { + AccessLevel::Organization(org_id) => org_id, + _ => return Err(HttpError::permission_denied(None, None).into()), + }; + + Ok(Self { org_id }) + } +} + +pub struct Application { + pub app: application::Model, +} + +#[async_trait] +impl FromRequest for Application +where + B: Send, +{ + type Rejection = Error; + + async fn from_request(req: &mut RequestParts) -> Result { + let permissions = permissions_from_bearer(req).await?; + + let Path(ApplicationPathParams { app_id }) = + ctx!(Path::::from_request(req).await)?; + let Extension(ref db) = ctx!(Extension::::from_request(req).await)?; + let app = ctx!( + application::Entity::secure_find_by_id_or_uid(permissions.org_id(), app_id.to_owned(),) + .one(db) + .await + )? + .ok_or_else(|| HttpError::not_found(None, None))?; + + if let Some(permitted_app_id) = permissions.app_id() { + if permitted_app_id != app.id { + return Err(HttpError::not_found(None, None).into()); + } + } + + Ok(Self { app }) + } +} + +// Organization level privileges, with the requested application +pub struct OrganizationWithApplication { + pub app: application::Model, +} + +#[async_trait] +impl FromRequest for OrganizationWithApplication +where + B: Send, +{ + type Rejection = Error; + + async fn from_request(req: &mut RequestParts) -> Result { + let Organization { org_id } = ctx!(Organization::from_request(req).await)?; + + let Path(ApplicationPathParams { app_id }) = + ctx!(Path::::from_request(req).await)?; + let Extension(ref db) = ctx!(Extension::::from_request(req).await)?; + let app = ctx!( + application::Entity::secure_find_by_id_or_uid(org_id, app_id.to_owned(),) + .one(db) + .await + )? + .ok_or_else(|| HttpError::not_found(None, None))?; + Ok(OrganizationWithApplication { app }) + } +} + +#[derive(serde::Deserialize)] +struct ApplicationPathParams { + app_id: ApplicationIdOrUid, +} diff --git a/server/svix-server/src/core/security.rs b/server/svix-server/src/core/security.rs index e07c0aff6..57360e707 100644 --- a/server/svix-server/src/core/security.rs +++ b/server/svix-server/src/core/security.rs @@ -4,23 +4,21 @@ use std::fmt::Debug; use axum::{ - async_trait, - extract::{Extension, FromRequest, Path, RequestParts, TypedHeader}, + extract::{Extension, FromRequest, RequestParts, TypedHeader}, headers::{authorization::Bearer, Authorization}, }; use jwt_simple::prelude::*; -use sea_orm::DatabaseConnection; + use validator::Validate; use crate::{ cfg::Configuration, ctx, - db::models::application, - error::{Error, HttpError, Result}, + error::{HttpError, Result}, }; -use super::types::{ApplicationId, ApplicationIdOrUid, OrganizationId}; +use super::types::{ApplicationId, OrganizationId}; /// The default org_id we use (useful for generating JWTs when testing). pub fn default_org_id() -> OrganizationId { @@ -32,16 +30,29 @@ pub fn management_org_id() -> OrganizationId { OrganizationId("org_00000000000SvixManagement00".to_owned()) } +pub enum AccessLevel { + Organization(OrganizationId), + Application(OrganizationId, ApplicationId), +} + pub struct Permissions { - pub type_: KeyType, - pub org_id: OrganizationId, - pub app_id: Option, + pub access_level: AccessLevel, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum KeyType { - Organization, - Application, +impl Permissions { + pub fn org_id(&self) -> OrganizationId { + match &self.access_level { + AccessLevel::Organization(org_id) => org_id.clone(), + AccessLevel::Application(org_id, _) => org_id.clone(), + } + } + + pub fn app_id(&self) -> Option { + match &self.access_level { + AccessLevel::Organization(_) => None, + AccessLevel::Application(_, app_id) => Some(app_id.clone()), + } + } } #[derive(Clone, Serialize, Deserialize)] @@ -50,18 +61,6 @@ pub struct CustomClaim { pub organization: Option, } -#[async_trait] -impl FromRequest for Permissions -where - B: Send, -{ - type Rejection = Error; - - async fn from_request(req: &mut RequestParts) -> Result { - permissions_from_bearer(req).await - } -} - pub async fn permissions_from_bearer(req: &mut RequestParts) -> Result { let Extension(ref cfg) = ctx!(Extension::::from_request(req).await)?; @@ -102,9 +101,7 @@ pub fn permissions_from_jwt(claims: JWTClaims) -> Result) -> Result) -> Result FromRequest for AuthenticatedOrganization -where - B: Send, -{ - type Rejection = Error; - - async fn from_request(req: &mut RequestParts) -> Result { - let permissions = Permissions::from_request(req).await?; - match permissions.type_ { - KeyType::Organization => {} - KeyType::Application => { - return Err(HttpError::permission_denied(None, None).into()); - } - } - - Ok(AuthenticatedOrganization { permissions }) - } -} - -#[derive(Deserialize)] -struct ApplicationPathParams { - app_id: ApplicationIdOrUid, -} - -pub struct AuthenticatedOrganizationWithApplication { - pub permissions: Permissions, - pub app: application::Model, -} - -#[async_trait] -impl FromRequest for AuthenticatedOrganizationWithApplication -where - B: Send, -{ - type Rejection = Error; - - async fn from_request(req: &mut RequestParts) -> Result { - let permissions = Permissions::from_request(req).await?; - - match permissions.type_ { - KeyType::Organization => {} - KeyType::Application => { - return Err(HttpError::permission_denied(None, None).into()); - } - } - - let Path(ApplicationPathParams { app_id }) = - ctx!(Path::::from_request(req).await)?; - let Extension(ref db) = ctx!(Extension::::from_request(req).await)?; - let app = ctx!( - application::Entity::secure_find_by_id_or_uid( - permissions.org_id.clone(), - app_id.to_owned(), - ) - .one(db) - .await - )? - .ok_or_else(|| HttpError::not_found(None, None))?; - Ok(AuthenticatedOrganizationWithApplication { permissions, app }) - } -} - -pub struct AuthenticatedApplication { - pub permissions: Permissions, - pub app: application::Model, -} - -#[async_trait] -impl FromRequest for AuthenticatedApplication -where - B: Send, -{ - type Rejection = Error; - - async fn from_request(req: &mut RequestParts) -> Result { - let permissions = Permissions::from_request(req).await?; - let Path(ApplicationPathParams { app_id }) = - ctx!(Path::::from_request(req).await)?; - let Extension(ref db) = ctx!(Extension::::from_request(req).await)?; - let app = ctx!( - application::Entity::secure_find_by_id_or_uid( - permissions.org_id.clone(), - app_id.to_owned(), - ) - .one(db) - .await - )? - .ok_or_else(|| HttpError::not_found(None, None))?; - - if let Some(permitted_app_id) = &permissions.app_id { - if permitted_app_id != &app.id { - return Err(HttpError::not_found(None, None).into()); - } - } - - Ok(AuthenticatedApplication { permissions, app }) - } -} - const JWT_ISSUER: &str = env!("CARGO_PKG_NAME"); pub fn generate_org_token(keys: &Keys, org_id: OrganizationId) -> Result { diff --git a/server/svix-server/src/v1/endpoints/application.rs b/server/svix-server/src/v1/endpoints/application.rs index 617021a19..16204fd56 100644 --- a/server/svix-server/src/v1/endpoints/application.rs +++ b/server/svix-server/src/v1/endpoints/application.rs @@ -3,15 +3,12 @@ use crate::{ core::{ - security::{ - AuthenticatedApplication, AuthenticatedOrganization, - AuthenticatedOrganizationWithApplication, - }, + permissions, types::{ApplicationId, ApplicationIdOrUid, ApplicationUid}, }, ctx, db::models::application, - error::{HttpError, Result}, + error::Result, v1::utils::{ patch::{ patch_field_non_nullable, patch_field_nullable, UnrequiredField, @@ -177,12 +174,12 @@ impl From for ApplicationOut { async fn list_applications( Extension(ref db): Extension, pagination: ValidatedQuery>, - AuthenticatedOrganization { permissions }: AuthenticatedOrganization, + permissions::Organization { org_id }: permissions::Organization, ) -> Result>> { let PaginationLimit(limit) = pagination.limit; let iterator = pagination.iterator.clone(); - let mut query = application::Entity::secure_find(permissions.org_id) + let mut query = application::Entity::secure_find(org_id) .order_by_asc(application::Column::Id) .limit(limit + 1); @@ -213,12 +210,12 @@ async fn create_application( Extension(ref db): Extension, ValidatedJson(data): ValidatedJson, query: ValidatedQuery, - AuthenticatedOrganization { permissions }: AuthenticatedOrganization, + permissions::Organization { org_id }: permissions::Organization, ) -> Result<(StatusCode, Json)> { if query.get_if_exists { if let Some(ref uid) = data.uid { let app = ctx!( - application::Entity::secure_find(permissions.org_id.clone()) + application::Entity::secure_find(org_id.clone()) .filter(application::Column::Uid.eq(uid.to_owned())) .one(db) .await @@ -230,7 +227,7 @@ async fn create_application( } let app = application::ActiveModel { - org_id: Set(permissions.org_id.clone()), + org_id: Set(org_id), ..data.into() }; let ret = ctx!(app.insert(db).await)?; @@ -238,15 +235,8 @@ async fn create_application( } async fn get_application( - Extension(ref db): Extension, - AuthenticatedApplication { app, permissions }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result> { - let app = ctx!( - application::Entity::secure_find_by_id(permissions.org_id, app.id) - .one(db) - .await - )? - .ok_or_else(|| HttpError::not_found(None, None))?; Ok(Json(app.into())) } @@ -254,10 +244,10 @@ async fn update_application( Extension(ref db): Extension, ValidatedJson(data): ValidatedJson, Path(app_id): Path, - AuthenticatedOrganization { permissions }: AuthenticatedOrganization, + permissions::Organization { org_id }: permissions::Organization, ) -> Result<(StatusCode, Json)> { let app = ctx!( - application::Entity::secure_find_by_id_or_uid(permissions.org_id.clone(), app_id) + application::Entity::secure_find_by_id_or_uid(org_id.clone(), app_id) .one(db) .await )?; @@ -273,7 +263,7 @@ async fn update_application( None => { let ret = ctx!( application::ActiveModel { - org_id: Set(permissions.org_id.clone()), + org_id: Set(org_id), ..data.into() } .insert(db) @@ -288,10 +278,7 @@ async fn update_application( async fn patch_application( Extension(ref db): Extension, ValidatedJson(data): ValidatedJson, - AuthenticatedOrganizationWithApplication { - permissions: _, - app, - }: AuthenticatedOrganizationWithApplication, + permissions::OrganizationWithApplication { app }: permissions::OrganizationWithApplication, ) -> Result> { let mut app: application::ActiveModel = app.into(); data.update_model(&mut app); @@ -302,10 +289,7 @@ async fn patch_application( async fn delete_application( Extension(ref db): Extension, - AuthenticatedOrganizationWithApplication { - permissions: _, - app, - }: AuthenticatedOrganizationWithApplication, + permissions::OrganizationWithApplication { app }: permissions::OrganizationWithApplication, ) -> Result<(StatusCode, Json)> { let mut app: application::ActiveModel = app.into(); app.deleted = Set(true); diff --git a/server/svix-server/src/v1/endpoints/attempt.rs b/server/svix-server/src/v1/endpoints/attempt.rs index 70c7ce4f2..bd4b66bae 100644 --- a/server/svix-server/src/v1/endpoints/attempt.rs +++ b/server/svix-server/src/v1/endpoints/attempt.rs @@ -3,7 +3,7 @@ use crate::{ core::{ - security::AuthenticatedApplication, + permissions, types::{ ApplicationIdOrUid, EndpointId, EndpointIdOrUid, EventChannel, EventTypeNameSet, MessageAttemptId, MessageAttemptTriggerType, MessageEndpointId, MessageId, @@ -126,10 +126,7 @@ async fn list_attempted_messages( after, }): ValidatedQuery, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result>> { let PaginationLimit(limit) = pagination.limit; let endp = ctx!( @@ -298,10 +295,7 @@ async fn list_attempts_by_endpoint( after, }): ValidatedQuery, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result>> { let PaginationLimit(limit) = pagination.limit; // Confirm endpoint ID belongs to the given application @@ -373,10 +367,7 @@ async fn list_attempts_by_msg( after, }): ValidatedQuery, Path((_app_id, msg_id)): Path<(ApplicationIdOrUid, MessageId)>, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result>> { let PaginationLimit(limit) = pagination.limit; // Confirm message ID belongs to the given application @@ -466,10 +457,7 @@ async fn list_attempted_destinations( Extension(ref db): Extension, ValidatedQuery(mut pagination): ValidatedQuery>, Path((_app_id, msg_id)): Path<(ApplicationIdOrUid, MessageIdOrUid)>, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result>> { let PaginationLimit(limit) = pagination.limit; let iterator = pagination.iterator.take(); @@ -532,7 +520,7 @@ async fn list_attempts_for_endpoint( }): ValidatedQuery, list_filter: MessageListFetchOptions, Path((app_id, msg_id, endp_id)): Path<(ApplicationIdOrUid, MessageIdOrUid, EndpointIdOrUid)>, - auth_app: AuthenticatedApplication, + auth_app: permissions::Application, ) -> Result>> { list_messageattempts( extension, @@ -574,10 +562,7 @@ async fn list_messageattempts( }): ValidatedQuery, list_filter: MessageListFetchOptions, Path((_app_id, msg_id)): Path<(ApplicationIdOrUid, MessageIdOrUid)>, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result>> { let PaginationLimit(limit) = pagination.limit; let msg = ctx!( @@ -641,10 +626,7 @@ async fn get_messageattempt( MessageIdOrUid, MessageAttemptId, )>, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result> { let msg = ctx!( message::Entity::secure_find_by_id_or_uid(app.id, msg_id) @@ -667,10 +649,7 @@ async fn resend_webhook( Extension(ref db): Extension, Extension(queue_tx): Extension, Path((_app_id, msg_id, endp_id)): Path<(ApplicationIdOrUid, MessageIdOrUid, EndpointIdOrUid)>, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result<(StatusCode, Json)> { let msg = ctx!( message::Entity::secure_find_by_id_or_uid(app.id.clone(), msg_id) diff --git a/server/svix-server/src/v1/endpoints/auth.rs b/server/svix-server/src/v1/endpoints/auth.rs index b358bb283..74c8e8903 100644 --- a/server/svix-server/src/v1/endpoints/auth.rs +++ b/server/svix-server/src/v1/endpoints/auth.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::{ cfg::Configuration, - core::security::{generate_app_token, AuthenticatedOrganizationWithApplication}, + core::{permissions, security::generate_app_token}, error::{HttpError, Result}, v1::utils::api_not_implemented, }; @@ -16,9 +16,9 @@ pub struct DashboardAccessOut { async fn dashboard_access( Extension(cfg): Extension, - AuthenticatedOrganizationWithApplication { permissions, app }: AuthenticatedOrganizationWithApplication, + permissions::OrganizationWithApplication { app }: permissions::OrganizationWithApplication, ) -> Result> { - let token = generate_app_token(&cfg.jwt_secret, permissions.org_id, app.id.clone())?; + let token = generate_app_token(&cfg.jwt_secret, app.org_id, app.id.clone())?; let login_key = serde_json::to_vec(&serde_json::json!({ "appId": app.id, diff --git a/server/svix-server/src/v1/endpoints/endpoint/crud.rs b/server/svix-server/src/v1/endpoints/endpoint/crud.rs index 709080e70..b00422dc7 100644 --- a/server/svix-server/src/v1/endpoints/endpoint/crud.rs +++ b/server/svix-server/src/v1/endpoints/endpoint/crud.rs @@ -14,7 +14,7 @@ use crate::{ cfg::Configuration, core::{ operational_webhooks::{EndpointEvent, OperationalWebhook, OperationalWebhookSender}, - security::AuthenticatedApplication, + permissions, types::{ ApplicationIdOrUid, EndpointId, EndpointIdOrUid, EndpointSecretInternal, EventTypeName, EventTypeNameSet, OrganizationId, @@ -34,10 +34,7 @@ use hack::EventTypeNameResult; pub(super) async fn list_endpoints( Extension(ref db): Extension, pagination: ValidatedQuery>, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result>> { let PaginationLimit(limit) = pagination.limit; let iterator = pagination.iterator.clone(); @@ -64,10 +61,10 @@ pub(super) async fn create_endpoint( Extension(cfg): Extension, Extension(op_webhooks): Extension, ValidatedJson(data): ValidatedJson, - AuthenticatedApplication { permissions, app }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result<(StatusCode, Json)> { if let Some(ref event_types_ids) = data.event_types_ids { - validate_event_types(db, event_types_ids, &permissions.org_id).await?; + validate_event_types(db, event_types_ids, &app.org_id).await?; } validate_endpoint_url(&data.url, cfg.endpoint_https_only)?; @@ -94,7 +91,7 @@ pub(super) async fn create_endpoint( op_webhooks .send_operational_webhook( - &permissions.org_id, + &app.org_id, OperationalWebhook::EndpointCreated(EndpointEvent { app_id: &ret.app_id, app_uid: app.uid.as_ref(), @@ -110,10 +107,7 @@ pub(super) async fn create_endpoint( pub(super) async fn get_endpoint( Extension(ref db): Extension, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result> { let endp = ctx!( endpoint::Entity::secure_find_by_id_or_uid(app.id, endp_id) @@ -130,7 +124,7 @@ pub(super) async fn update_endpoint( Extension(op_webhooks): Extension, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, ValidatedJson(data): ValidatedJson, - AuthenticatedApplication { permissions, app }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result<(StatusCode, Json)> { let endp = ctx!( endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endp_id) @@ -139,7 +133,7 @@ pub(super) async fn update_endpoint( )?; if let Some(ref event_types_ids) = data.event_types_ids { - validate_event_types(db, event_types_ids, &permissions.org_id).await?; + validate_event_types(db, event_types_ids, &app.org_id).await?; } validate_endpoint_url(&data.url, cfg.endpoint_https_only)?; @@ -153,7 +147,7 @@ pub(super) async fn update_endpoint( op_webhooks .send_operational_webhook( - &permissions.org_id, + &app.org_id, OperationalWebhook::EndpointUpdated(EndpointEvent { app_id: &ret.app_id, app_uid: app_uid.as_ref(), @@ -189,7 +183,7 @@ pub(super) async fn update_endpoint( op_webhooks .send_operational_webhook( - &permissions.org_id, + &app.org_id, OperationalWebhook::EndpointCreated(EndpointEvent { app_id: &ret.app_id, app_uid: app_uid.as_ref(), @@ -210,7 +204,7 @@ pub(super) async fn patch_endpoint( Extension(op_webhooks): Extension, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, ValidatedJson(data): ValidatedJson, - AuthenticatedApplication { permissions, app }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result> { let endp = ctx!( endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endp_id) @@ -220,7 +214,7 @@ pub(super) async fn patch_endpoint( .ok_or_else(|| HttpError::not_found(None, None))?; if let UnrequiredNullableField::Some(ref event_types_ids) = data.event_types_ids { - validate_event_types(db, event_types_ids, &permissions.org_id).await?; + validate_event_types(db, event_types_ids, &app.org_id).await?; } if let UnrequiredField::Some(url) = &data.url { validate_endpoint_url(url, cfg.endpoint_https_only)?; @@ -234,7 +228,7 @@ pub(super) async fn patch_endpoint( let app_uid = app.uid; op_webhooks .send_operational_webhook( - &permissions.org_id, + &app.org_id, OperationalWebhook::EndpointUpdated(EndpointEvent { app_id: &ret.app_id, app_uid: app_uid.as_ref(), @@ -251,7 +245,7 @@ pub(super) async fn delete_endpoint( Extension(ref db): Extension, Extension(op_webhooks): Extension, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, - AuthenticatedApplication { permissions, app }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result<(StatusCode, Json)> { let endp = ctx!( endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endp_id) @@ -271,7 +265,7 @@ pub(super) async fn delete_endpoint( op_webhooks .send_operational_webhook( - &permissions.org_id, + &app.org_id, OperationalWebhook::EndpointDeleted(EndpointEvent { app_id: &app.id, app_uid: app.uid.as_ref(), diff --git a/server/svix-server/src/v1/endpoints/endpoint/headers.rs b/server/svix-server/src/v1/endpoints/endpoint/headers.rs index 8a8116c57..cbf72943b 100644 --- a/server/svix-server/src/v1/endpoints/endpoint/headers.rs +++ b/server/svix-server/src/v1/endpoints/endpoint/headers.rs @@ -8,7 +8,7 @@ use sea_orm::{ActiveModelTrait, DatabaseConnection}; use super::{EndpointHeadersIn, EndpointHeadersOut, EndpointHeadersPatchIn}; use crate::{ core::{ - security::AuthenticatedApplication, + permissions, types::{ApplicationIdOrUid, EndpointIdOrUid}, }, ctx, @@ -20,10 +20,7 @@ use crate::{ pub(super) async fn get_endpoint_headers( Extension(ref db): Extension, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result> { let endp = ctx!( endpoint::Entity::secure_find_by_id_or_uid(app.id, endp_id) @@ -42,10 +39,7 @@ pub(super) async fn update_endpoint_headers( Extension(ref db): Extension, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, ValidatedJson(data): ValidatedJson, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result<(StatusCode, Json)> { let endp = ctx!( endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endp_id) @@ -65,10 +59,7 @@ pub(super) async fn patch_endpoint_headers( Extension(ref db): Extension, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, ValidatedJson(data): ValidatedJson, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result<(StatusCode, Json)> { let endp = ctx!( endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endp_id) diff --git a/server/svix-server/src/v1/endpoints/endpoint/mod.rs b/server/svix-server/src/v1/endpoints/endpoint/mod.rs index 2d2bd379f..eade99b7d 100644 --- a/server/svix-server/src/v1/endpoints/endpoint/mod.rs +++ b/server/svix-server/src/v1/endpoints/endpoint/mod.rs @@ -7,7 +7,7 @@ mod secrets; use crate::{ core::{ - security::AuthenticatedApplication, + permissions, types::{ ApplicationIdOrUid, BaseId, EndpointId, EndpointIdOrUid, EndpointUid, EventChannelSet, EventTypeNameSet, MessageEndpointId, MessageStatus, @@ -455,10 +455,7 @@ pub struct EndpointStatsQueryOut { async fn endpoint_stats( Extension(ref db): Extension, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> crate::error::Result> { let endpoint = ctx!( crate::db::models::endpoint::Entity::secure_find_by_id_or_uid(app.id, endp_id) diff --git a/server/svix-server/src/v1/endpoints/endpoint/recovery.rs b/server/svix-server/src/v1/endpoints/endpoint/recovery.rs index a2eba5266..94bb93903 100644 --- a/server/svix-server/src/v1/endpoints/endpoint/recovery.rs +++ b/server/svix-server/src/v1/endpoints/endpoint/recovery.rs @@ -10,7 +10,7 @@ use sea_orm::{DatabaseConnection, QuerySelect}; use super::RecoverIn; use crate::{ core::{ - security::AuthenticatedApplication, + permissions, types::{ ApplicationIdOrUid, BaseId, EndpointIdOrUid, MessageAttemptTriggerType, MessageEndpointId, MessageStatus, @@ -78,10 +78,7 @@ pub(super) async fn recover_failed_webhooks( Extension(queue_tx): Extension, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, ValidatedJson(data): ValidatedJson, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result<(StatusCode, Json)> { // Add five minutes so that people can easily just do `now() - two_weeks` without having to worry about clock sync let timeframe = chrono::Duration::days(14); diff --git a/server/svix-server/src/v1/endpoints/endpoint/secrets.rs b/server/svix-server/src/v1/endpoints/endpoint/secrets.rs index a138ec2d3..ffcee65ca 100644 --- a/server/svix-server/src/v1/endpoints/endpoint/secrets.rs +++ b/server/svix-server/src/v1/endpoints/endpoint/secrets.rs @@ -13,7 +13,7 @@ use crate::{ cfg::{Configuration, DefaultSignatureType}, core::{ cryptography::Encryption, - security::AuthenticatedApplication, + permissions, types::{ ApplicationIdOrUid, EndpointIdOrUid, EndpointSecretInternal, ExpiringSigningKey, ExpiringSigningKeys, @@ -39,10 +39,7 @@ pub(super) async fn get_endpoint_secret( Extension(ref db): Extension, Extension(cfg): Extension, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result> { let endp = ctx!( endpoint::Entity::secure_find_by_id_or_uid(app.id, endp_id) @@ -60,10 +57,7 @@ pub(super) async fn rotate_endpoint_secret( Extension(cfg): Extension, Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>, ValidatedJson(data): ValidatedJson, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result<(StatusCode, Json)> { let mut endp = ctx!( endpoint::Entity::secure_find_by_id_or_uid(app.id, endp_id) diff --git a/server/svix-server/src/v1/endpoints/event_type.rs b/server/svix-server/src/v1/endpoints/event_type.rs index 72adc1855..c6de47529 100644 --- a/server/svix-server/src/v1/endpoints/event_type.rs +++ b/server/svix-server/src/v1/endpoints/event_type.rs @@ -2,10 +2,7 @@ // SPDX-License-Identifier: MIT use crate::{ - core::{ - security::{AuthenticatedOrganization, Permissions}, - types::EventTypeName, - }, + core::{permissions, types::EventTypeName}, ctx, db::models::eventtype, error::{HttpError, Result}, @@ -173,12 +170,12 @@ async fn list_event_types( Extension(ref db): Extension, pagination: ValidatedQuery>, fetch_options: ValidatedQuery, - permissions: Permissions, + permissions::Organization { org_id }: permissions::Organization, ) -> Result>> { let PaginationLimit(limit) = pagination.limit; let iterator = pagination.iterator.clone(); - let mut query = eventtype::Entity::secure_find(permissions.org_id) + let mut query = eventtype::Entity::secure_find(org_id) .order_by_asc(eventtype::Column::Name) .limit(limit + 1); @@ -208,10 +205,10 @@ async fn list_event_types( async fn create_event_type( Extension(ref db): Extension, ValidatedJson(data): ValidatedJson, - AuthenticatedOrganization { permissions }: AuthenticatedOrganization, + permissions::Organization { org_id }: permissions::Organization, ) -> Result<(StatusCode, Json)> { let evtype = ctx!( - eventtype::Entity::secure_find_by_name(permissions.org_id.clone(), data.name.to_owned()) + eventtype::Entity::secure_find_by_name(org_id.clone(), data.name.to_owned()) .one(db) .await )?; @@ -232,7 +229,7 @@ async fn create_event_type( } None => { let evtype = eventtype::ActiveModel { - org_id: Set(permissions.org_id.clone()), + org_id: Set(org_id), ..data.into() }; ctx!(evtype.insert(db).await)? @@ -244,10 +241,10 @@ async fn create_event_type( async fn get_event_type( Extension(ref db): Extension, Path(evtype_name): Path, - AuthenticatedOrganization { permissions }: AuthenticatedOrganization, + permissions::Organization { org_id }: permissions::Organization, ) -> Result> { let evtype = ctx!( - eventtype::Entity::secure_find_by_name(permissions.org_id, evtype_name) + eventtype::Entity::secure_find_by_name(org_id, evtype_name) .one(db) .await )? @@ -259,10 +256,10 @@ async fn update_event_type( Extension(ref db): Extension, Path(evtype_name): Path, ValidatedJson(data): ValidatedJson, - AuthenticatedOrganization { permissions }: AuthenticatedOrganization, + permissions::Organization { org_id }: permissions::Organization, ) -> Result<(StatusCode, Json)> { let evtype = ctx!( - eventtype::Entity::secure_find_by_name(permissions.org_id.clone(), evtype_name.clone()) + eventtype::Entity::secure_find_by_name(org_id.clone(), evtype_name.clone()) .one(db) .await )?; @@ -278,7 +275,7 @@ async fn update_event_type( None => { let ret = ctx!( eventtype::ActiveModel { - org_id: Set(permissions.org_id.clone()), + org_id: Set(org_id), name: Set(evtype_name), ..data.into() } @@ -295,10 +292,10 @@ async fn patch_event_type( Extension(ref db): Extension, Path(evtype_name): Path, ValidatedJson(data): ValidatedJson, - AuthenticatedOrganization { permissions }: AuthenticatedOrganization, + permissions::Organization { org_id }: permissions::Organization, ) -> Result> { let evtype = ctx!( - eventtype::Entity::secure_find_by_name(permissions.org_id.clone(), evtype_name) + eventtype::Entity::secure_find_by_name(org_id, evtype_name) .one(db) .await )? @@ -314,10 +311,10 @@ async fn patch_event_type( async fn delete_event_type( Extension(ref db): Extension, Path(evtype_name): Path, - AuthenticatedOrganization { permissions }: AuthenticatedOrganization, + permissions::Organization { org_id }: permissions::Organization, ) -> Result<(StatusCode, Json)> { let evtype = ctx!( - eventtype::Entity::secure_find_by_name(permissions.org_id, evtype_name) + eventtype::Entity::secure_find_by_name(org_id, evtype_name) .one(db) .await )? diff --git a/server/svix-server/src/v1/endpoints/message.rs b/server/svix-server/src/v1/endpoints/message.rs index 247170d00..2ba7a7f9e 100644 --- a/server/svix-server/src/v1/endpoints/message.rs +++ b/server/svix-server/src/v1/endpoints/message.rs @@ -5,7 +5,7 @@ use crate::{ cache::Cache, core::{ message_app::CreateMessageApp, - security::{AuthenticatedApplication, AuthenticatedOrganizationWithApplication}, + permissions, types::{ ApplicationIdOrUid, EventChannel, EventChannelSet, EventTypeName, EventTypeNameSet, MessageAttemptTriggerType, MessageId, MessageIdOrUid, MessageUid, @@ -173,10 +173,7 @@ async fn list_messages( after, }): ValidatedQuery, list_filter: MessageListFetchOptions, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result>> { let PaginationLimit(limit) = pagination.limit; @@ -229,14 +226,14 @@ async fn create_message( CreateMessageQueryParams, >, ValidatedJson(data): ValidatedJson, - AuthenticatedOrganizationWithApplication { permissions, app }: AuthenticatedOrganizationWithApplication, + permissions::OrganizationWithApplication { app }: permissions::OrganizationWithApplication, ) -> Result<(StatusCode, Json)> { let create_message_app = CreateMessageApp::layered_fetch( cache, db, Some(app.clone()), app.id.clone(), - app.org_id, + app.org_id.clone(), std::time::Duration::from_secs(30), ) .await? @@ -245,7 +242,7 @@ async fn create_message( let msg = message::ActiveModel { app_id: Set(app.id.clone()), - org_id: Set(permissions.org_id), + org_id: Set(app.org_id), ..data.into() }; let msg = ctx!(msg.insert(db).await)?; @@ -285,10 +282,7 @@ async fn get_message( Extension(ref db): Extension, Path((_app_id, msg_id)): Path<(ApplicationIdOrUid, MessageIdOrUid)>, ValidatedQuery(GetMessageQueryParams { with_content }): ValidatedQuery, - AuthenticatedApplication { - permissions: _, - app, - }: AuthenticatedApplication, + permissions::Application { app }: permissions::Application, ) -> Result> { let msg = ctx!( message::Entity::secure_find_by_id_or_uid(app.id, msg_id)