From f15936dcdfa2989234ab0430f52e2d2f9e4e3731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Sibiril?= <81782+aureliensibiril@users.noreply.github.com> Date: Thu, 16 Apr 2026 14:14:02 +0200 Subject: [PATCH] Preserve shared connector on SCIM disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeleteSCIMConfiguration unconditionally deleted the underlying OAuth2 connector together with the SCIM bridge and config. When the same connector was also referenced from access_sources -- which happens when Google Workspace is used for both SCIM and access reviews -- the access_sources.connector_id foreign key (NO ACTION) rejected the DELETE, aborting the whole transaction. Nothing was deleted and the resolver returned an INTERNAL error. Check the access_sources reference count before deleting the connector and skip the connector delete when it is still in use. The bridge's own connector_id FK is ON DELETE SET NULL, so dropping the bridge alone is sufficient to unbind SCIM; leaving the connector untouched keeps the access source working. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com> --- pkg/iam/organization_service.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/pkg/iam/organization_service.go b/pkg/iam/organization_service.go index 0c2f632d3..dca117ffa 100644 --- a/pkg/iam/organization_service.go +++ b/pkg/iam/organization_service.go @@ -1580,12 +1580,23 @@ func (s OrganizationService) DeleteSCIMConfiguration( } if err == nil { - // Bridge exists, delete connector if it has one + // Bridge exists. Only delete the underlying connector if nothing + // else references it (e.g. access_sources). Otherwise leave it in + // place — the bridge's FK is ON DELETE SET NULL, so deleting the + // bridge alone is sufficient to unbind SCIM from the connector. if bridge.ConnectorID != nil { - connector := &coredata.Connector{ID: *bridge.ConnectorID} - err = connector.Delete(ctx, tx, scope) - if err != nil && err != coredata.ErrResourceNotFound { - return fmt.Errorf("cannot delete connector: %w", err) + accessSources := &coredata.AccessSources{} + count, err := accessSources.CountByConnectorID(ctx, tx, scope, *bridge.ConnectorID) + if err != nil { + return fmt.Errorf("cannot count access sources for connector: %w", err) + } + + if count == 0 { + connector := &coredata.Connector{ID: *bridge.ConnectorID} + err = connector.Delete(ctx, tx, scope) + if err != nil && err != coredata.ErrResourceNotFound { + return fmt.Errorf("cannot delete connector: %w", err) + } } }