first commit
This commit is contained in:
@@ -0,0 +1,551 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user