feat(api): return 403 when attempting to remove last owner
Handle edge case where removing a member would leave an organization without any owners. Service layer raises ValueError for this scenario, which the API endpoint catches and converts to a forbidden response with actionable error message about transferring ownership.
This commit is contained in:
@@ -56,7 +56,10 @@ def add_organization_member(org_id):
|
|||||||
@full_access_required
|
@full_access_required
|
||||||
def remove_organization_member(org_id, user_id):
|
def remove_organization_member(org_id, user_id):
|
||||||
org = OrganizationService.get_organization_by_id(org_id)
|
org = OrganizationService.get_organization_by_id(org_id)
|
||||||
|
try:
|
||||||
OrganizationService.remove_member(org=org, user_id=user_id, remover_id=g.current_user.id)
|
OrganizationService.remove_member(org=org, user_id=user_id, remover_id=g.current_user.id)
|
||||||
|
except ValueError as e:
|
||||||
|
return api_response(success=False, message=str(e), status=403, error_type="OWNER_PROTECTION")
|
||||||
return api_response(message="Member removed successfully")
|
return api_response(message="Member removed successfully")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -379,6 +379,15 @@ class OrganizationService:
|
|||||||
logger.debug(f"[Org] Member removal: org_id={org.id}, user_id={user_id}, found={member is not None}")
|
logger.debug(f"[Org] Member removal: org_id={org.id}, user_id={user_id}, found={member is not None}")
|
||||||
|
|
||||||
if member:
|
if member:
|
||||||
|
if member.role == OrganizationRole.OWNER:
|
||||||
|
owner_count = OrganizationMember.query.filter(
|
||||||
|
OrganizationMember.organization_id == org.id,
|
||||||
|
OrganizationMember.role == OrganizationRole.OWNER,
|
||||||
|
OrganizationMember.deleted_at.is_(None),
|
||||||
|
OrganizationMember.user_id != user_id,
|
||||||
|
).count()
|
||||||
|
if owner_count < 1:
|
||||||
|
raise ValueError("Cannot remove the only owner from an organization. Transfer ownership first.")
|
||||||
member.delete(soft=True)
|
member.delete(soft=True)
|
||||||
|
|
||||||
# Log member removal
|
# Log member removal
|
||||||
|
|||||||
Reference in New Issue
Block a user