Add superadmin routes to API

This commit is contained in:
2026-04-21 17:11:03 +09:30
parent aaec6af6ad
commit 1778dd85d5
33 changed files with 4831 additions and 31 deletions
+13 -29
View File
@@ -283,7 +283,8 @@ def admin_verify_user_email(user_id):
if was_inactive:
target.status = UserStatus.ACTIVE
EmailVerificationToken.query.filter_by(user_id=target.id, used_at=None).delete()
now = datetime.now(timezone.utc)
EmailVerificationToken.query.filter_by(user_id=target.id, used_at=None).filter(EmailVerificationToken.deleted_at == None).update({"deleted_at": now}, synchronize_session=False)
_db.session.commit()
AuditService.log_action(
@@ -300,14 +301,12 @@ def admin_verify_user_email(user_id):
@api_v1_bp.route("/admin/users/<user_id>/delete", methods=["POST"])
@login_required
@full_access_required
def admin_hard_delete_user(user_id):
def admin_delete_user(user_id):
from gatehouse_app.models.organization.organization_member import OrganizationMember
from gatehouse_app.models.user.user import User as _User
from gatehouse_app.models.ssh_ca.ssh_key import SSHKey
from gatehouse_app.models.ssh_ca.ssh_certificate import SSHCertificate
from gatehouse_app.models.ssh_ca.certificate_audit_log import CertificateAuditLog
from gatehouse_app.models.auth.authentication_method import OAuthState
from gatehouse_app.models.security.organization_security_policy import OrganizationSecurityPolicy
from gatehouse_app.extensions import db as _db
from gatehouse_app.utils.constants import AuditAction, OrganizationRole
from gatehouse_app.services.audit_service import AuditService
@@ -373,44 +372,29 @@ def admin_hard_delete_user(user_id):
target_email = target.email
target_id_str = str(target.id)
now = datetime.now(timezone.utc)
try:
# NULL out FK references that don't cascade on delete so the
# session.delete() below doesn't hit FK constraint violations.
# Soft delete the user — set deleted_at timestamp.
target.deleted_at = now
# org_invite_tokens.invited_by_id — SET NULL is already on the FK column,
# but OrganizationMember.invited_by_id has no ondelete clause.
_db.session.execute(
_db.text("UPDATE organization_members SET invited_by_id = NULL WHERE invited_by_id = :uid"),
{"uid": target_id_str},
# Soft delete associated OAuthState records.
OAuthState.query.filter_by(user_id=target_id_str).filter(OAuthState.deleted_at == None).update(
{"deleted_at": now}, synchronize_session=False
)
# certificate_audit_logs.user_id — nullable, no ondelete clause.
CertificateAuditLog.query.filter_by(user_id=target_id_str).update(
{"user_id": None}, synchronize_session=False
)
# organization_security_policies.updated_by_user_id — nullable, no ondelete.
OrganizationSecurityPolicy.query.filter_by(updated_by_user_id=target_id_str).update(
{"updated_by_user_id": None}, synchronize_session=False
)
# oauth_states.user_id — nullable, no ondelete.
OAuthState.query.filter_by(user_id=target_id_str).delete(synchronize_session=False)
_db.session.delete(target)
_db.session.flush()
except Exception as exc:
_db.session.rollback()
_logger.error(f"Hard delete failed for {target_id_str}: {exc}")
_logger.error(f"Soft delete failed for {target_id_str}: {exc}")
return api_response(success=False, message="Failed to delete user account. Please try again.", status=500, error_type="SERVER_ERROR")
AuditService.log_action(
action=AuditAction.USER_HARD_DELETE,
action=AuditAction.USER_DELETE,
user_id=caller.id,
organization_id=admin_in_shared_org.organization_id,
resource_type="user", resource_id=target_id_str,
description=f"Admin permanently deleted user account: {target_email}",
description=f"Admin deleted user account: {target_email}",
metadata={
"deleted_user_id": target_id_str, "deleted_user_email": target_email,
"ssh_keys_deleted": ssh_key_count, "certs_revoked": active_cert_count,
@@ -419,7 +403,7 @@ def admin_hard_delete_user(user_id):
_db.session.commit()
return api_response(
message=f"User account {target_email} has been permanently deleted.",
message=f"User account {target_email} has been deleted.",
data={"deleted_user_id": target_id_str, "deleted_user_email": target_email,
"ssh_keys_deleted": ssh_key_count, "certs_revoked": active_cert_count},
)