fix: prevent ghost memberships from soft-deleted users
This commit is contained in:
@@ -212,6 +212,48 @@ class TestAdminUserManagement:
|
||||
integration_client.auth.login(email=victim["email"], password="VictimPass123!")
|
||||
assert exc_info.value.status_code in (400, 401)
|
||||
|
||||
def test_admin_soft_delete_cascades_org_membership(self, integration_app, integration_client, create_test_user, create_test_org, create_test_membership):
|
||||
"""TEST: ADMIN-12 — Admin soft-delete cascades to org memberships.
|
||||
|
||||
WHAT: Admin soft-deletes a user in an org. The membership row
|
||||
and the user row both get soft-deleted.
|
||||
WHY: Ghost memberships (membership active but user deleted) would
|
||||
make Organization.is_member() return True and break lookups.
|
||||
EXPECTED: 200 OK. OrganizationMember.deleted_at and User.deleted_at
|
||||
are set. Organization.is_member() returns False.
|
||||
"""
|
||||
from gatehouse_app.models.organization.organization_member import OrganizationMember
|
||||
from gatehouse_app.models.organization.organization import Organization
|
||||
from gatehouse_app.models.user.user import User
|
||||
from gatehouse_app.extensions import db
|
||||
|
||||
admin = create_test_user(password="AdminPass123!")
|
||||
victim = create_test_user(password="VictimPass123!")
|
||||
org = create_test_org()
|
||||
create_test_membership(admin["id"], org["id"], OrganizationRole.OWNER)
|
||||
create_test_membership(victim["id"], org["id"], OrganizationRole.MEMBER)
|
||||
|
||||
integration_client.auth.login(email=admin["email"], password="AdminPass123!")
|
||||
result = integration_client.admin.hard_delete_user(victim["id"], confirm=True)
|
||||
assert_success(result)
|
||||
|
||||
with integration_app.app_context():
|
||||
# Membership row still exists but is soft-deleted
|
||||
membership = OrganizationMember.query.filter_by(
|
||||
user_id=victim["id"], organization_id=org["id"]
|
||||
).first()
|
||||
assert membership is not None
|
||||
assert membership.deleted_at is not None
|
||||
|
||||
# User row is soft-deleted
|
||||
user = User.query.get(victim["id"])
|
||||
assert user is not None
|
||||
assert user.deleted_at is not None
|
||||
|
||||
# Organization.is_member() returns False (defense in depth)
|
||||
org_obj = Organization.query.get(org["id"])
|
||||
assert org_obj.is_member(victim["id"]) is False
|
||||
|
||||
|
||||
class TestAdminSSHCertificates:
|
||||
"""Test admin SSH certificate listing endpoints."""
|
||||
|
||||
Reference in New Issue
Block a user