security: scrub credentials when Mail profiles are deactivated

This commit is contained in:
2026-07-21 17:12:49 +02:00
parent 88d5012dea
commit 4a9201aeea
2 changed files with 177 additions and 11 deletions

View File

@@ -53,6 +53,7 @@ from govoplan_mail.backend.mailbox_index import (
from govoplan_mail.backend.mail_profiles import (
MailProfileError,
create_mail_server_profile,
delete_mail_profile_credentials,
effective_mail_profile_policy_for_scope,
get_mail_profile_policy,
get_mail_server_profile,
@@ -700,7 +701,7 @@ def create_profile(
resource_id=profile.id,
operation="created",
principal=principal,
tenant_id=profile.tenant_id or principal.tenant_id,
tenant_id=profile.tenant_id,
payload={"scope_type": profile.scope_type, "scope_id": profile.scope_id},
)
session.commit()
@@ -751,6 +752,7 @@ def update_profile(
profile,
tenant_id=principal.tenant_id,
user_id=principal.user.id,
api_key_id=principal.api_key_id,
name=payload.name,
slug=payload.slug,
description=payload.description if "description" in payload.model_fields_set else None,
@@ -766,7 +768,7 @@ def update_profile(
resource_id=profile.id,
operation="updated",
principal=principal,
tenant_id=profile.tenant_id or principal.tenant_id,
tenant_id=profile.tenant_id,
payload={"scope_type": profile.scope_type, "scope_id": profile.scope_id},
)
session.commit()
@@ -775,6 +777,9 @@ def update_profile(
except MailProfileError as exc:
session.rollback()
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail=str(exc)) from exc
except Exception:
session.rollback()
raise
@router.delete("/profiles/{profile_id}", response_model=MailServerProfileResponse)
@@ -786,24 +791,42 @@ def deactivate_profile(
try:
profile = get_mail_server_profile(session, tenant_id=principal.tenant_id, profile_id=profile_id)
_require_profile_write_scope(principal, profile.scope_type or "tenant")
_require_profile_credentials_scope(principal, profile.scope_type or "tenant")
was_active = bool(profile.is_active)
profile.is_active = False
session.add(profile)
_record_mail_change(
deleted_protocols = delete_mail_profile_credentials(
session,
collection=MAIL_PROFILES_COLLECTION,
resource_type=MAIL_PROFILE_RESOURCE,
resource_id=profile.id,
operation="deleted",
principal=principal,
tenant_id=profile.tenant_id or principal.tenant_id,
payload={"scope_type": profile.scope_type, "scope_id": profile.scope_id},
profile=profile,
deletion_reason="profile_deactivated",
user_id=principal.user.id,
api_key_id=principal.api_key_id,
)
session.add(profile)
if was_active or deleted_protocols:
_record_mail_change(
session,
collection=MAIL_PROFILES_COLLECTION,
resource_type=MAIL_PROFILE_RESOURCE,
resource_id=profile.id,
operation="deleted",
principal=principal,
tenant_id=profile.tenant_id,
payload={
"scope_type": profile.scope_type,
"scope_id": profile.scope_id,
"credentials_deleted": bool(deleted_protocols),
"deleted_credential_protocols": list(deleted_protocols),
},
)
session.commit()
session.refresh(profile)
return _profile_response(profile)
except MailProfileError as exc:
session.rollback()
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
except Exception:
session.rollback()
raise
@router.get("/policies/{scope_type}", response_model=MailProfilePolicyResponse)