From 1b2ef5c9c2f0bfe27f16c56d1452fbbe5f4ee8c0 Mon Sep 17 00:00:00 2001 From: sebastian Date: Thu, 2 Jul 2026 01:58:02 -0600 Subject: [PATCH] fix(sso): prevent provider update org reassignment --- crates/rustauth-sso/src/openapi.rs | 1 - .../src/routes/provider_update.rs | 23 +------------------ crates/rustauth-sso/src/store.rs | 5 ---- .../tests/sso/endpoints/provider_update.rs | 22 +++++------------- crates/rustauth-sso/tests/sso/openapi.rs | 10 ++++++++ 5 files changed, 17 insertions(+), 44 deletions(-) diff --git a/crates/rustauth-sso/src/openapi.rs b/crates/rustauth-sso/src/openapi.rs index e695ae88..3d5cf953 100644 --- a/crates/rustauth-sso/src/openapi.rs +++ b/crates/rustauth-sso/src/openapi.rs @@ -32,7 +32,6 @@ pub fn update_provider_body_schema() -> BodySchema { BodyField::new("providerId", JsonSchemaType::String), BodyField::optional("issuer", JsonSchemaType::String).format("uri"), BodyField::optional("domain", JsonSchemaType::String), - BodyField::optional("organizationId", JsonSchemaType::String), BodyField::optional("oidcConfig", JsonSchemaType::Object).description( "OIDC provider configuration. Manual skipDiscovery endpoints may be validated against trusted origins when strict_oidc_manual_endpoint_origins is enabled.", ), diff --git a/crates/rustauth-sso/src/routes/provider_update.rs b/crates/rustauth-sso/src/routes/provider_update.rs index 2d39d450..54bc90ee 100644 --- a/crates/rustauth-sso/src/routes/provider_update.rs +++ b/crates/rustauth-sso/src/routes/provider_update.rs @@ -19,7 +19,7 @@ use crate::options::{ OidcConfig, OidcMapping, SamlMapping, SsoAuditEvent, SsoAuditEventKind, SsoAuditSeverity, SsoOptions, TokenEndpointAuthentication, }; -use crate::org::{can_manage_provider, can_register_for_organization}; +use crate::org::can_manage_provider; use crate::store::{SsoProviderStore, UpdateSsoProviderInput}; use crate::utils; @@ -31,7 +31,6 @@ struct UpdateProviderBody { provider_id: String, issuer: Option, domain: Option, - organization_id: Option, oidc_config: Option, saml_config: Option, } @@ -40,7 +39,6 @@ impl UpdateProviderBody { fn has_update_fields(&self) -> bool { self.issuer.is_some() || self.domain.is_some() - || self.organization_id.is_some() || self.oidc_config.is_some() || self.saml_config.is_some() } @@ -157,24 +155,6 @@ pub(super) fn endpoint(options: Arc) -> AsyncAuthEndpoint { ); } } - if let Some(organization_id) = &body.organization_id { - if !can_register_for_organization( - &context, - adapter.as_ref(), - &user_id, - organization_id, - ) - .await? - { - return utils::json( - http::StatusCode::BAD_REQUEST, - &json!({ - "code": "ORGANIZATION_MEMBERSHIP_REQUIRED", - "message": "You are not a member of the organization" - }), - ); - } - } let mut oidc_trust_boundary_changed = false; let merged_oidc_config = if let Some(update) = body.oidc_config { #[cfg(not(feature = "oidc"))] @@ -294,7 +274,6 @@ pub(super) fn endpoint(options: Arc) -> AsyncAuthEndpoint { UpdateSsoProviderInput { issuer: body.issuer, domain: body.domain, - organization_id: body.organization_id, oidc_config: merged_oidc_config.map(Some), saml_config: merged_saml_config.map(Some), domain_verified: reset_domain_verified.then_some(false), diff --git a/crates/rustauth-sso/src/store.rs b/crates/rustauth-sso/src/store.rs index ed4fa813..0f6bc781 100644 --- a/crates/rustauth-sso/src/store.rs +++ b/crates/rustauth-sso/src/store.rs @@ -325,9 +325,6 @@ impl<'a> SsoProviderStore<'a> { if let Some(domain) = input.domain { query = query.data("domain", DbValue::String(domain)); } - if let Some(organization_id) = input.organization_id { - query = query.data("organization_id", DbValue::String(organization_id)); - } if let Some(oidc_config) = input.oidc_config { query = query.data("oidc_config", optional_string(oidc_config)); } @@ -405,8 +402,6 @@ pub struct UpdateSsoProviderInput { pub issuer: Option, /// Updated domains. pub domain: Option, - /// Updated organization id. - pub organization_id: Option, /// Updated serialized OIDC config; `Some(None)` clears it. pub oidc_config: Option>, /// Updated serialized SAML config; `Some(None)` clears it. diff --git a/crates/rustauth-sso/tests/sso/endpoints/provider_update.rs b/crates/rustauth-sso/tests/sso/endpoints/provider_update.rs index a85584fb..25c73970 100644 --- a/crates/rustauth-sso/tests/sso/endpoints/provider_update.rs +++ b/crates/rustauth-sso/tests/sso/endpoints/provider_update.rs @@ -1,7 +1,7 @@ use super::*; #[tokio::test] -async fn update_provider_applies_owner_scope_and_resets_domain_verification( +async fn update_provider_ignores_organization_id_and_resets_domain_verification( ) -> Result<(), Box> { let (adapter, router) = router_with_options(SsoOptions::default().domain_verification_enabled(true))?; @@ -29,7 +29,7 @@ async fn update_provider_applies_owner_scope_and_resets_domain_verification( assert_eq!(body["providerId"], "okta"); assert_eq!(body["issuer"], "https://login.example.com"); assert_eq!(body["domain"], "corp.example.com"); - assert_eq!(body["organizationId"], "org_1"); + assert!(body["organizationId"].is_null()); assert_eq!(body["domainVerified"], false); let records = adapter.records("sso_provider").await; @@ -37,23 +37,16 @@ async fn update_provider_applies_owner_scope_and_resets_domain_verification( records[0].get("domain"), Some(&DbValue::String("corp.example.com".to_owned())) ); - assert_eq!( - records[0].get("organization_id"), - Some(&DbValue::String("org_1".to_owned())) - ); + assert_eq!(records[0].get("organization_id"), Some(&DbValue::Null)); Ok(()) } #[tokio::test] -async fn update_provider_rejects_organization_id_without_membership( +async fn update_provider_rejects_organization_id_only_as_no_update_fields( ) -> Result<(), Box> { - let (adapter, router) = router_with_options_and_extra_plugins( - SsoOptions::default(), - vec![AuthPlugin::new("organization")], - )?; + let (adapter, router) = router_with_options(SsoOptions::default())?; let cookie = seed_session(&adapter).await?; - seed_organization(&adapter, "org_1", "acme").await?; router .handle_async(json_request( Method::POST, @@ -73,10 +66,7 @@ async fn update_provider_rejects_organization_id_without_membership( .await?; assert_eq!(response.status(), StatusCode::BAD_REQUEST); - assert_eq!( - json_body(response)?["code"], - "ORGANIZATION_MEMBERSHIP_REQUIRED" - ); + assert_eq!(json_body(response)?["code"], "NO_UPDATE_FIELDS"); let records = adapter.records("sso_provider").await; assert_eq!(records[0].get("organization_id"), Some(&DbValue::Null)); diff --git a/crates/rustauth-sso/tests/sso/openapi.rs b/crates/rustauth-sso/tests/sso/openapi.rs index c51156c3..5711fcb2 100644 --- a/crates/rustauth-sso/tests/sso/openapi.rs +++ b/crates/rustauth-sso/tests/sso/openapi.rs @@ -28,6 +28,11 @@ fn sso_openapi_exposes_public_route_metadata() -> Result<(), Box Result<(), Box