using System.IdentityModel.Tokens.Jwt; using System.Net.Mime; using System.Text.Json; using Kafka.Lib.Auth; using Kafka.Lib.Auth.Model; using MassTransit; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using OpenWarehouse.auth.api.Common.AuthTokenRepo; using OpenWarehouse.auth.api.Common.CustomIdentifyManager; using OpenWarehouse.auth.lib.Model; using OpenWarehouse.auth.lib.Model.Account; namespace OpenWarehouse.auth.api.Controller; [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [ApiController] [Route("[controller]/[action]")] public class Account( RoleManager roleManager, PrivilegeManager privilegeManager, UserManager userManager, AuthTokenRepo tokenRepo, ITopicProducer userChangeProducer, ITopicProducer userClaimChangeProducer, ILogger logger) : ControllerBase { [HttpDelete] [HasPrivilege(GlobalPrivilage.Privilege.GlobalDeleteUser)] [Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])] public async Task> DeleteUser([FromBody]DeleteUserRequestModel requestModel) { logger.LogInformation("DeleteUser called with UserId: {UserId}", requestModel.UserId); if (requestModel.UserId != null) { var user = await userManager.FindByIdAsync(requestModel.UserId); if (user != null) { logger.LogInformation("User found: {UserId}", user.Id); var tokenR = await tokenRepo.DeleteAllUserTokenAsync(user); var userR = await userManager.DeleteAsync(user); DeleteUserReturnModel returnModel = new DeleteUserReturnModel { UserId = requestModel.UserId, DedeltedTokenCount = tokenR, IsOperationSuccess = userR.Succeeded, OperationMessage = string.Join(";", userR.Errors) }; var topic = new UserChangeMessageType() { UserId = user.Id, Changes = new Dictionary { { "AccountChange", "Delete" } }, Id = HttpContext.TraceIdentifier, }; _ = userChangeProducer.Produce(topic); logger.LogInformation("User deleted: {UserId}", user.Id); return Ok(returnModel); } else { logger.LogWarning("User not found: {UserId}", requestModel.UserId); return BadRequest("User not found"); } } logger.LogWarning("DeleteUser request with empty UserId"); return BadRequest("User cannot be empty"); } [HttpPost] [HasPrivilege(GlobalPrivilage.Privilege.GlobalCreateUser)] [Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])] public async Task> CreateUser([FromBody] CreateUserModel newUser) { logger.LogInformation("CreateUser called with Username: {Username}, Email: {Email}", newUser.Username, newUser.Email); var userReturn = new CreateUserModelReturn() { Username = newUser.Username, Email = newUser.Email, Password = "*******", Roles = new List(), }; if (newUser is { Username: not null, Email: not null, Password: not null }) { var user = new ApplicationUser {UserName = newUser.Username, Email = newUser.Email, EmailConfirmed = true, DialCode = newUser.Dialcode, PhoneNumber = newUser.Phone}; var userResult = await userManager.CreateAsync(user, newUser.Password); if (userResult.Succeeded) { logger.LogInformation("User created: {UserId}", user.Id); var resultClaim = await userManager.AddClaimAsync(user, AccountType.User.Claim()); if (resultClaim.Succeeded && newUser.Roles != null) { userReturn.IsCreated = true; userReturn.NewUserId = user.Id; foreach (var role in newUser.Roles) { var appRole = await roleManager.FindByNameAsync(role); CreateUserModelReturn.RoleStatus roleStatus = new CreateUserModelReturn.RoleStatus(); var result = await userManager.AddToRoleAsync(user, role); roleStatus.Added = result.Succeeded; roleStatus.Name = role; roleStatus.Exist = appRole != null; roleStatus.Mess = JsonSerializer.Serialize(string.Join(";", result.Errors)); userReturn.Roles.Add(roleStatus); } var topicMess = new UserChangeMessageType { UserId = user.Id, Id = user.Id, Changes = { ["RoleChange"] = string .Join(";", userReturn.Roles .Select(status => $"{status.Name}:{status.Added}/{status.Exist}")), ["UserChange"] = "Created", ["Success"] = $"{userReturn.IsCreated}" } }; await userChangeProducer.Produce(topicMess); logger.LogInformation("User created successfully: {UserId}", user.Id); return Ok(userReturn); } else { userReturn.ReturnMess = string.Join(';', userResult.Errors.Select(q => $"{q.Code}:{q.Description}")); logger.LogWarning("Failed to add claims or roles to user: {UserId}", user.Id); return StatusCode(406, userReturn); } } else { userReturn.ReturnMess = JsonSerializer.Serialize(string.Join(";", userResult.Errors.Select(q => $"{q.Code}:{q.Description}"))); logger.LogWarning("Failed to create user: {Username}", newUser.Username); userReturn.IsCreated = false; return StatusCode(406, userReturn); } } else { logger.LogWarning("Invalid CreateUser request"); userReturn.IsCreated = false; userReturn.ReturnMess = "Invalid request"; } return StatusCode(406, userReturn); } [HttpPost] [Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])] public async Task> ChangePassword([FromBody] ChangePassword req) { logger.LogInformation("ChangePassword called for UserId: {UserId}", User.FindFirstValue(ClaimTypes.NameIdentifier)); var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var user = await userManager.FindByIdAsync(userId ?? ""); if ((user != null) && (req is { Password: not null, NewPassword1: not null, NewPassword2: not null })) { if (req.NewPassword1.Equals(req.NewPassword2)) { var result = await userManager.ChangePasswordAsync(user, req.Password, req.NewPassword1); if (result.Succeeded) { logger.LogInformation("Password changed successfully for UserId: {UserId}", user.Id); var count = 0; if (req.LogOutAllSession) count = await tokenRepo.LockAllUserTokenAsync(user); return Ok(new ChangePasswordReturn { Mess = "Operation success.", IsSuccess = true, LockedTokenCount = count}); } else { logger.LogWarning("Failed to change password for UserId: {UserId}", user.Id); return StatusCode(418, new ChangePasswordReturn {Mess = string.Join(";", result.Errors .Select(q => q.Description) ), IsSuccess = false}); } } else { logger.LogWarning("New passwords do not match for UserId: {UserId}", user.Id); return StatusCode(418, new ChangePasswordReturn {Mess = "New password not match.", IsSuccess = false}); } } else { logger.LogWarning("Invalid ChangePassword request for UserId: {UserId}", userId); return StatusCode(400, new ChangePasswordReturn {Mess = "Bad request", IsSuccess = false}); } } [HttpPost] [HasPrivilege(GlobalPrivilage.Privilege.GlobalDeactivateUserToken)] public async Task> DeactivateToken([FromBody] DeactiveTokenModel req) { logger.LogInformation("DeactivateToken called for Token: {Token}", req.Token); var i = await tokenRepo.LockTokenAsync(req.Token ?? throw new Exception("Token is null")); var resultMessage = i > 0 ? "Token lock success." : "Token Lock Fail."; logger.LogInformation(resultMessage); return new OkModel(resultMessage, i > 0); } [HttpPost] [HasPrivilege(GlobalPrivilage.Privilege.GlobalEditUser)] public async Task> AddClaimsToUser([FromBody] AddClaimsToUserModel req) { logger.LogInformation("AddClaimsToUser called for UserId: {UserId}", req.UserId); var userId = req.UserId; var user = await userManager.FindByIdAsync(userId) ?? throw new Exception("User not found"); var topic = new UserChangeClaimsMessageType() { UserId = userId, Id = HttpContext.TraceIdentifier, ClaimChages = new List() }; var actionResult = new AddClaimsToUserReturnModel() { UserId = userId }; var curClaims = await userManager.GetClaimsAsync(user); foreach (var newClaim in req.NewClaims) { var existingClaim = curClaims.FirstOrDefault(c => c.Type == newClaim.Key); topic.ClaimChages.Add(new ClaimChagesModel() { Old = existingClaim != null ? new ClaimModel { ClaimType = existingClaim.Type, ClaimValue = existingClaim.Value, ClaimIssuer = existingClaim.Issuer } : null, New = new ClaimModel { ClaimType = newClaim.Key, ClaimValue = newClaim.Value, ClaimIssuer = req.Issuer } }); } var resultAddClaims = await userManager.AddClaimsAsync(user, req.NewClaims.Select(pair => new Claim(type: pair.Key, value: pair.Value, null, req.Issuer)).ToList()); if (!resultAddClaims.Succeeded) { actionResult.IsSucess = false; actionResult.Error = string.Join(";", resultAddClaims.Errors.Select(error => error.Description)); topic.IsSuccess = false; topic.Errors = resultAddClaims.Errors.Select(error => new KeyValuePair(error.Code, error.Description)) .ToDictionary(); logger.LogWarning("Failed to add claims to user: {UserId}", userId); } else { actionResult.IsSucess = true; topic.IsSuccess = true; logger.LogInformation("Claims added successfully to UserId: {UserId}", userId); } _ = userClaimChangeProducer.Produce(topic); var allUserClaim = await userManager.GetClaimsAsync(user); foreach (var claim in allUserClaim) { actionResult.AllUserClaims[claim.Type] = claim.Value; } return Ok(actionResult); } [HttpGet] public async Task> GetAllClaimsUser() { var userId = User.Claims.Where(q => q.Type == ClaimTypes.NameIdentifier) .Select(q => q.Value).FirstOrDefault(); logger.LogInformation("GetAllClaimsUser called for UserId: {UserId}", userId); var user = await userManager.FindByIdAsync(userId ?? "") ?? throw new Exception("User not found"); var result = new AddClaimsToUserReturnModel() { UserId = userId, IsSucess = true, Error = null}; var allUserClaim = await userManager.GetClaimsAsync(user); foreach (var claim in allUserClaim) { result.AllUserClaims[claim.Type] = claim.Value; } return Ok(result); } [HttpPost] public async Task> UpdateUserEmail([FromBody] ChangeEmailRequest request) { logger.LogInformation("UpdateUserEmail called with NewEmail: {NewEmail}", request.NewEmail); var response = new ChangeEmailRespond { NewEmail = request.NewEmail }; var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); if (string.IsNullOrEmpty(userId)) { response.Problems.Add("UserId", "User ID not found in token."); logger.LogWarning("UserId not found in token"); return BadRequest(response); } var user = await userManager.FindByIdAsync(userId); if (user == null) { response.Problems.Add("User", "User not found."); logger.LogWarning("User not found: {UserId}", userId); return Unauthorized(response); } response.OldEmail = user.Email; if (string.IsNullOrEmpty(request.NewEmail)) { response.Problems.Add("NewEmail", "New email is required."); logger.LogWarning("New email is required for UserId: {UserId}", userId); return BadRequest(response); } user.Email = request.NewEmail; var result = await userManager.UpdateAsync(user); if (result.Succeeded) { response.ValSuccess = true; response.UpdateSuccess = true; logger.LogInformation("User email updated successfully: {UserId}", userId); return Ok(response); } else { response.ValSuccess = true; response.Problems = result.Errors.ToDictionary( error => error.Code, error => error.Description ); logger.LogWarning("Failed to update user email for UserId: {UserId}", userId); return StatusCode(418, response); } } [HttpPost] public async Task> UpdatePhoneNumber([FromBody] ChangePhoneNumberRequest request) { logger.LogInformation("UpdatePhoneNumber called with Prefix: {Prefix}, Number: {Number}", request.Prefix, request.Number); var response = new ChangePhoneNumberRespond { Prefix = request.Prefix, Number = request.Number }; var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); if (string.IsNullOrEmpty(userId)) { response.Problems.Add("UserId", "User ID not found in token."); logger.LogWarning("UserId not found in token"); return BadRequest(response); } var user = await userManager.FindByIdAsync(userId); if (user == null) { response.Problems.Add("User", "User not found."); logger.LogWarning("User not found: {UserId}", userId); return Unauthorized(response); } response.OldPrefix = user.DialCode; response.OldPhone = user.PhoneNumber; if (string.IsNullOrEmpty(request.Prefix) || string.IsNullOrEmpty(request.Number)) { response.Problems.Add("PhoneNumber", "Prefix and number are required."); logger.LogWarning("Prefix or number missing for UserId: {UserId}", userId); return BadRequest(response); } user.PhoneNumber = request.Number; user.DialCode = request.Prefix; var result = await userManager.UpdateAsync(user); if (result.Succeeded) { response.ValSuccess = true; response.UpdateSuccess = true; logger.LogInformation("User phone number updated successfully: {UserId}", userId); return Ok(response); } else { response.ValSuccess = true; response.Problems = result.Errors.ToDictionary( error => error.Code, error => error.Description ); logger.LogWarning("Failed to update phone number for UserId: {UserId}", userId); return StatusCode(418, response); } } [HttpPost] public async Task> GetMyDetail() { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var response = new AccountDetailRespond(); if (string.IsNullOrEmpty(userId)) { response.Problems.Add("UserId", "User ID not found in token."); logger.LogWarning("UserId not found in token"); return BadRequest(response); } var user = await userManager.FindByIdAsync(userId); if (user == null) { response.Problems.Add("User", "User not found."); logger.LogWarning("User not found: {UserId}", userId); return Unauthorized(response); } response.Id = user.Id; response.Email = user.Email; response.UserName = user.UserName; response.Phone = user.PhoneNumber; response.DialCode = user.DialCode; return Ok(response); } [HttpPost] [HasPrivilege(GlobalPrivilage.Privilege.GlobalEditUser, GlobalPrivilage.Privilege.GlobalDeleteUser)] public async Task> GetListOfUsers([FromBody] AccountListsRequest request) { if (request.Page < 1 || request.PageSize < 1) { return BadRequest("Page and page size must be greater than 0."); } var result = new AccountListsResponse { AccountsDetail = await userManager.Users .OrderBy(u => u.UserName) .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .Select(q =>new AccountDetailRespond() { UserName = q.UserName, DialCode = q.DialCode, Email = q.Email, Id = q.Id, Phone = q.PhoneNumber }).ToListAsync(), Page = request.Page, PageSize = request.PageSize, NextPageExist = await userManager.Users .OrderBy(u => u.UserName) .Skip((request.Page) * request.PageSize) .Take(request.PageSize).AnyAsync(), LastPageNum = (await userManager.Users.CountAsync()) / request.PageSize }; return result; } [HttpPost] [HasPrivilege(GlobalPrivilage.Privilege.GlobalEditUser)] public async Task> EditAccount([FromBody] EditAccountRequest request) { if (request is { UserId: null }) { return BadRequest(new EditAccountRespond() { Message = "Invalid request", Success = false }); } var user = await userManager.FindByIdAsync(request.UserId); if (user == null) { return BadRequest(new EditAccountRespond() { Message = "User not found", Success = false }); } var respond = new EditAccountRespond(); if (user.UserName != request.NewUserName) { respond.NewUserName = request.NewUserName; user.UserName = request.NewUserName; respond.UserNameEdited = true; } if (user.Email != request.NewEmail) { respond.NewEmail = request.NewEmail; user.Email = request.NewEmail; respond.EmailEdited = true; } if (user.DialCode != request.NewDialCode || user.PhoneNumber != request.NewPhone) { respond.NewDialCode = request.NewDialCode; respond.NewPhone = request.NewPhone; user.PhoneNumber = request.NewPhone; user.DialCode = request.NewDialCode; respond.PhoneEdited = true; } var result = await userManager.UpdateAsync(user); respond.Success = result.Succeeded; respond.Message = string.Join("\n",result.Errors.Select(error => $"{error.Code}:{error.Description}")); return respond; } }