feat(account): add deletion lifecycle
CI and Deploy / test (pull_request) Successful in 5m18s
CI and Deploy / deploy (pull_request) Has been skipped

This commit is contained in:
cesnimda
2026-08-15 19:03:54 +02:00
parent 1ec9dd037e
commit 842e793f69
28 changed files with 4418 additions and 129 deletions
+13 -6
View File
@@ -19,13 +19,15 @@ public sealed class UsersController : ControllerBase
private readonly IAppEmailSender _email;
private readonly ExternalOrigin _externalOrigin;
private readonly ILogger<UsersController> _logger;
public UsersController(UserManager<ApplicationUser> users, RoleManager<IdentityRole> roles, IAppEmailSender email, IConfiguration cfg, ILogger<UsersController> logger, ExternalOrigin? externalOrigin = null)
private readonly AccountDeletionService? _deletions;
public UsersController(UserManager<ApplicationUser> users, RoleManager<IdentityRole> roles, IAppEmailSender email, IConfiguration cfg, ILogger<UsersController> logger, ExternalOrigin? externalOrigin = null, AccountDeletionService? deletions = null)
{
_users = users;
_roles = roles;
_email = email;
_externalOrigin = externalOrigin ?? ExternalOrigin.FromConfiguration(cfg);
_logger = logger;
_deletions = deletions;
}
public sealed record UserDto(
@@ -163,11 +165,16 @@ public sealed class UsersController : ControllerBase
});
}
var res = await _users.DeleteAsync(u);
if (!res.Succeeded)
return BadRequest(string.Join("; ", res.Errors.Select(e => e.Description)));
return NoContent();
if (_deletions is null || !_deletions.CanAcceptRequests)
return Problem(statusCode: StatusCodes.Status503ServiceUnavailable, title: "Account deletion unavailable", detail: "Account deletion remains disabled until retention and restore safeguards are approved.");
if (!string.Equals(Request.Headers["X-Confirm-Account-Deletion"].ToString(), u.Email, StringComparison.OrdinalIgnoreCase))
return BadRequest("Confirm the exact account email before deletion.");
var requestedBy = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub");
if (string.IsNullOrWhiteSpace(requestedBy)) return Unauthorized();
var request = await _deletions.RequestAsync(u.Id, requestedBy, cancellationToken);
return request is null
? Problem(statusCode: StatusCodes.Status503ServiceUnavailable, title: "Account deletion unavailable")
: Accepted(new { requestId = request.RequestId, status = request.Status, stage = request.Stage });
}
[HttpPost("{id}/send-password-reset")]