using EntitlementBLL.Auth; using EntitlementSL.Common; using FastEndpoints; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Framework.ResponseStandard; using GB5Shared.FastEndPoint; using ErrorResponse = GB5Shared.Resource.Response.ErrorResponse; using static GB5Shared.GB5Constant.Constant; namespace EntitlementSL.Endpoints.ClientAuth; // Authenticated (CLIENT_ADMIN or CLIENT_USER — either role may change their own password, no // further role gate needed beyond "is some client user"). ClientUserId comes from verified JWT // claims via ClientCallerContext, never the request body — a caller must never be able to specify // whose password to change. // // Real framework-level gate as of item 9: AuthSchemes("ClientJwtBearer") + Roles(ClientAdmin, // ClientUser) — see CreateClientUser.cs's comment for the full explanation of the named-scheme/ // RoleClaimType wiring. Roles() with multiple values is OR semantics (either role suffices), // matching standard ASP.NET Core authorization behavior. The manual caller.ClientUserId check in // ExecuteAsync below is KEPT as genuine defense-in-depth (catches a missing/malformed // ClientUserId claim, which Roles() alone would not). public class ChangePassword : BaseEndpoint> { private const string ClientJwtSchemeName = "ClientJwtBearer"; private readonly IClientAuthBLL _ClientAuthBLL; public ChangePassword(IClientAuthBLL clientAuthBLL) => _ClientAuthBLL = clientAuthBLL; public override void Configure() { Post("/lic/ClientAuth.svc/ChangePassword"); AuthSchemes(ClientJwtSchemeName); Roles(ClientRoleCodes.ClientAdmin, ClientRoleCodes.ClientUser); } public record Body(string CurrentPassword, string NewPassword); public record Params( [property: FromHeader] string Login, [property: FromBody] Body RequestBody ); protected override string? GetCacheKey(Params req, LoginDTO loginDTO) => null; protected override async Task> ExecuteAsync(Params req, LoginDTO loginDTO, CancellationToken ct) { var caller = ClientCallerContext.FromClaims(HttpContext.User.Claims); if (caller.ClientUserId <= 0) return await GB5Shared.ResponseStandard.Response.CreateErrorResponse( ErrorResponse.AccessDeniedMessage, CacheKeyLevel.NOT_REQUIRED, loginDTO, statusCode: 403); var result = await _ClientAuthBLL.ChangePasswordAsync( caller.ClientUserId, req.RequestBody.CurrentPassword, req.RequestBody.NewPassword, loginDTO, ct); if (result.Status != ClientChangePasswordResultStatus.Success) return await GB5Shared.ResponseStandard.Response.CreateErrorResponse( result.Message, CacheKeyLevel.NOT_REQUIRED, loginDTO, statusCode: 400); return await GB5Shared.ResponseStandard.Response.CreateSuccessResponse(result.Message, CacheKeyLevel.NOT_REQUIRED, loginDTO); } }