using EntitlementBLL.Auth; using EntitlementSL.Common; using FastEndpoints; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Framework.ResponseStandard; using GB5Shared.FastEndPoint; using Microsoft.AspNetCore.RateLimiting; using static GB5Shared.GB5Constant.Constant; using GB5Shared.Authorization; namespace EntitlementSL.Endpoints.ClientAuth; // GOODBOOKS_ADMIN-only: creates the very first CLIENT_ADMIN for a client. Per the program's // account-creation decision, Entitlement client accounts are NOT open self-registration — a // GOODBOOKS_ADMIN always creates the first admin for a new client (later, that CLIENT_ADMIN // provisions their own CLIENT_USER accounts via CreateClientUser.cs, scoped to their own ClientId). // Uses the module's standard BaseEndpoint + Roles(), exactly like every other // GOODBOOKS_ADMIN-gated Entitlement endpoint (e.g. BetaClient/SaveBetaClient.cs) — the caller here // really is an internal ERP admin authenticated via the existing internal auth scheme, not a // client-jwt caller, so there is no reason to deviate from this module's normal endpoint base. [MenuRights("entdashboard", RightOperation.Update)] public class CreateClientAdmin : BaseEndpoint> { private readonly IClientAuthBLL _ClientAuthBLL; public CreateClientAdmin(IClientAuthBLL clientAuthBLL) => _ClientAuthBLL = clientAuthBLL; public override void Configure() { Post("/lic/ClientAuth.svc/CreateClientAdmin"); AllowAnonymous(); // Tighter per-IP rate limit than Login/RefreshToken — see ClientAuthRateLimitOptions. // GOODBOOKS_ADMIN-only, but still worth protecting: a compromised/misused admin token // shouldn't be able to hammer this endpoint unbounded. Options(x => x.RequireRateLimiting(ClientAuthRateLimitPolicies.CreateClientAdmin)); } public record Body(int ClientId, string Email, string FullName); 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 result = await _ClientAuthBLL.CreateClientAdminAsync( req.RequestBody.ClientId, req.RequestBody.Email, req.RequestBody.FullName, loginDTO, ct); // result.TemporaryPassword is the one and only time the plaintext value exists outside // this process's memory — the calling GOODBOOKS_ADMIN sees it here, once, in this // response, and must relay it to the client admin out-of-band. It is never logged and // never persisted anywhere except as its PasswordHasher hash (see ClientAuthBLL). return await GB5Shared.ResponseStandard.Response.CreateSuccessResponse(result, CacheKeyLevel.NOT_REQUIRED, loginDTO); } }