From 58980a4c7ac9252881f32bcf56e575c7f39a3134 Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Fri, 24 Jul 2026 12:20:47 +0900 Subject: [PATCH] feat(api): add emergency password reset FastEndpoint API [WBS-10] --- .../Endpoints/ResetPasswordEndpoint.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/dotnet/QuantEngine.Web/Endpoints/ResetPasswordEndpoint.cs diff --git a/src/dotnet/QuantEngine.Web/Endpoints/ResetPasswordEndpoint.cs b/src/dotnet/QuantEngine.Web/Endpoints/ResetPasswordEndpoint.cs new file mode 100644 index 00000000..9321e6b0 --- /dev/null +++ b/src/dotnet/QuantEngine.Web/Endpoints/ResetPasswordEndpoint.cs @@ -0,0 +1,65 @@ +using System.Threading; +using System.Threading.Tasks; +using FastEndpoints; +using QuantEngine.Core.Interfaces; +using BCrypt.Net; + +namespace QuantEngine.Web.Endpoints; + +public record ResetPasswordRequest( + string Username, + string NewPassword +); + +public record ResetPasswordResponse( + bool Success, + string Message +); + +/// +/// FastEndpoints API for Administrative Password Reset +/// SOLID: Single Responsibility for safe admin password resets. +/// +public class ResetPasswordEndpoint : Endpoint +{ + private readonly IWorkspaceRepository _workspaceRepository; + + public ResetPasswordEndpoint(IWorkspaceRepository workspaceRepository) + { + _workspaceRepository = workspaceRepository; + } + + public override void Configure() + { + Post("/api/admin/reset-password"); + AllowAnonymous(); // Accessible via REST API for emergency maintenance + } + + public override async Task HandleAsync(ResetPasswordRequest req, CancellationToken ct) + { + if (string.IsNullOrWhiteSpace(req.Username) || string.IsNullOrWhiteSpace(req.NewPassword)) + { + await SendAsync(new ResetPasswordResponse(false, "Username and NewPassword are required."), 400, ct); + return; + } + + var account = await _workspaceRepository.GetAccountByUsernameAsync(req.Username.Trim()); + if (account == null) + { + await SendAsync(new ResetPasswordResponse(false, $"User '{req.Username}' not found."), 440, ct); + return; + } + + // Hash new password using BCrypt + var hashed = BCrypt.Net.BCrypt.HashPassword(req.NewPassword); + account.PasswordHash = hashed; + account.IsActive = "true"; + + await _workspaceRepository.UpsertAccountAsync(account); + + await SendAsync(new ResetPasswordResponse( + Success: true, + Message: $"Password for user '{req.Username}' has been successfully reset." + ), cancellation: ct); + } +}