From dd8c3b5491b3cdded730da3ad1ab77a6b396cd52 Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Mon, 20 Apr 2026 11:59:59 +0200 Subject: [PATCH] Prevent last owner from demoting themselves The UpdateMembership path allowed the sole owner of an organization to change their role to a non-owner role, causing permanent lockout. Add the same active-owner count guard already used in RemoveUser. Closes #1071 Signed-off-by: Bryan Frimin --- pkg/iam/organization_service.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/iam/organization_service.go b/pkg/iam/organization_service.go index dca117ffa..d2b828e3a 100644 --- a/pkg/iam/organization_service.go +++ b/pkg/iam/organization_service.go @@ -266,6 +266,18 @@ func (s *OrganizationService) UpdateMempership( return NewMembershipNotFoundError(membership.ID) } + if membership.Role == coredata.MembershipRoleOwner && role != coredata.MembershipRoleOwner { + profiles := coredata.MembershipProfiles{} + count, err := profiles.CountActiveOwnerByOrganizationID(ctx, tx, scope, organizationID) + if err != nil { + return fmt.Errorf("cannot count active owners: %w", err) + } + + if count <= 1 { + return NewLastActiveOwnerError(membershipID) + } + } + membership.Role = role membership.UpdatedAt = time.Now()