Files
OpenWarehouse/OpenWarehouse.auth.api/Controller/Account.cs
T
2025-02-26 23:42:19 +01:00

552 lines
22 KiB
C#

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<ApplicationRole> roleManager,
PrivilegeManager privilegeManager,
UserManager<ApplicationUser> userManager,
AuthTokenRepo tokenRepo,
ITopicProducer<UserChangeMessageType> userChangeProducer,
ITopicProducer<UserChangeClaimsMessageType> userClaimChangeProducer,
ILogger<Account> logger) : ControllerBase
{
[HttpDelete]
[HasPrivilege(GlobalPrivilage.Privilege.GlobalDeleteUser)]
[Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])]
public async Task<ActionResult<DeleteUserReturnModel>> 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<string, string> { { "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<ActionResult<CreateUserModelReturn>> 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<CreateUserModelReturn.RoleStatus>(),
};
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<ActionResult<ChangePasswordReturn>> 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<ActionResult<OkModel>> 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<ActionResult<AddClaimsToUserReturnModel>> 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<ClaimChagesModel>()
};
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<string, string>(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<ActionResult<AddClaimsToUserReturnModel>> 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<ActionResult<ChangeEmailRespond>> 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<ActionResult<ChangePhoneNumberRespond>> 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<ActionResult<AccountDetailRespond>> 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<ActionResult<AccountListsResponse>> 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<ActionResult<EditAccountRespond>> 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;
}
}