From f50289a7558b877737b94fa81179a59c87aed697 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 19:13:51 +0000 Subject: [PATCH] Harden archive organization scoping Validate ArchiveUser organization input against the loaded profile and\nuse the profile organization for owner checks and webhook emission.\n\nAlso disable both PersonPage destructive actions while either archive\nor remove mutation is pending to prevent double-submit races. Signed-off-by: Cursor Agent Co-authored-by: Bryan FRIMIN --- .../src/pages/iam/organizations/people/PersonPage.tsx | 5 +++-- pkg/iam/organization_service.go | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/console/src/pages/iam/organizations/people/PersonPage.tsx b/apps/console/src/pages/iam/organizations/people/PersonPage.tsx index 23498dc42..d8c800616 100644 --- a/apps/console/src/pages/iam/organizations/people/PersonPage.tsx +++ b/apps/console/src/pages/iam/organizations/people/PersonPage.tsx @@ -89,6 +89,7 @@ export function PersonPage(props: { queryRef: PreloadedQuery }) errorMessage: __("Failed to remove person"), }, ); + const isMutating = isArchiving || isRemoving; const handleArchive = () => { confirm( @@ -171,7 +172,7 @@ export function PersonPage(props: { queryRef: PreloadedQuery }) {__("Archive")} @@ -181,7 +182,7 @@ export function PersonPage(props: { queryRef: PreloadedQuery }) variant="danger" icon={IconTrashCan} onClick={handleRemove} - disabled={isRemoving} + disabled={isMutating} > {__("Remove")} diff --git a/pkg/iam/organization_service.go b/pkg/iam/organization_service.go index 3c065eb17..1d1020421 100644 --- a/pkg/iam/organization_service.go +++ b/pkg/iam/organization_service.go @@ -331,6 +331,10 @@ func (s *OrganizationService) RemoveUser( return NewUserManagedBySCIMError(profileID) } + if profile.OrganizationID != organizationID { + return NewProfileNotFoundError(profileID) + } + membership := &coredata.Membership{} if err := membership.LoadByIdentityIDAndOrganizationID(ctx, tx, scope, profile.IdentityID, profile.OrganizationID); err != nil { return fmt.Errorf("cannot load membership: %w", err) @@ -339,7 +343,7 @@ func (s *OrganizationService) RemoveUser( if membership.Role == coredata.MembershipRoleOwner && profile.State == coredata.ProfileStateActive { profiles := coredata.MembershipProfiles{} - count, err := profiles.CountActiveOwnerByOrganizationID(ctx, tx, scope, organizationID) + count, err := profiles.CountActiveOwnerByOrganizationID(ctx, tx, scope, profile.OrganizationID) if err != nil { return fmt.Errorf("cannot count active owners: %w", err) } @@ -437,7 +441,7 @@ func (s *OrganizationService) ArchiveUser( return fmt.Errorf("cannot update membership: %w", err) } - if err := webhook.InsertData(ctx, tx, scope, organizationID, coredata.WebhookEventTypeUserUpdated, webhooktypes.NewUser(&profile, membership)); err != nil { + if err := webhook.InsertData(ctx, tx, scope, profile.OrganizationID, coredata.WebhookEventTypeUserUpdated, webhooktypes.NewUser(&profile, membership)); err != nil { return fmt.Errorf("cannot insert webhook event: %w", err) }