first commit

This commit is contained in:
2025-02-26 23:42:19 +01:00
commit 777ea98be1
283 changed files with 88266 additions and 0 deletions
@@ -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;
}
}
+226
View File
@@ -0,0 +1,226 @@
using System.Net.Mime;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using OpenWarehouse.auth.api.Common.AuthTokenRepo;
using OpenWarehouse.auth.api.Common.CustomIdentifyManager;
using OpenWarehouse.auth.api.Common.RegisterTokenManager;
using OpenWarehouse.auth.lib.Model.Account;
using OpenWarehouse.auth.lib.Model.Auth;
namespace OpenWarehouse.auth.api.Controller;
[ApiController]
[Route("[action]")]
public class Auth(
SignInManager<ApplicationUser> signInManager, UserManager<ApplicationUser> userManager,
AuthTokenRepo tokenRepo, ApplicationDbContext applicationDbContext,
RegisterTokenManager registerTokenManager,
ILogger<Auth> logger) : ControllerBase
{
[HttpPost]
[Consumes(MediaTypeNames.Application.Json, MediaTypeNames.Application.Xml)]
public async Task<ActionResult<LoginModel>> Login([FromBody] LoginRequestModel requestModel)
{
logger.LogInformation("Login request received for user: {Login}", requestModel.Login);
if (requestModel.Pass == null || requestModel.Login == null)
{
logger.LogWarning("Invalid login request model.");
return BadRequest("Invalid request model.");
}
var loginModel = new LoginModel();
if (requestModel.Pass == null)
{
loginModel.AccountProblem = "Login and password are required.";
logger.LogWarning("Password is missing in the login request for user: {Login}", requestModel.Login);
return BadRequest(loginModel);
}
var applicationUser = await userManager.FindByNameAsync(requestModel.Login)
?? await userManager.FindByEmailAsync(requestModel.Login);
if (applicationUser == null)
{
logger.LogWarning("User not found for login: {Login}", requestModel.Login);
loginModel.Message = "Incorrect login or password.";
return Unauthorized(loginModel);
}
loginModel.Login = applicationUser.UserName;
loginModel.Email = applicationUser.Email;
var claims = await userManager.GetClaimsAsync(applicationUser);
if (claims.Any(claim => claim.Type.Equals(AccountType.Service.GetValue())))
{
logger.LogWarning("Service account attempted to login: {Login}", requestModel.Login);
return Forbid("This is a service account.");
}
if (applicationUser.LockoutEnabled)
{
logger.LogWarning("Locked account attempted to login: {Login}", requestModel.Login);
return Forbid("Account is locked.");
}
var signInResult = await signInManager.CheckPasswordSignInAsync(applicationUser, requestModel.Pass, false);
if (signInResult.Succeeded)
{
// var expiration = DateTime.UtcNow.AddHours(1);
var expiration = DateTime.UtcNow.AddMinutes(30);
loginModel.Token = await tokenRepo.CreateJwtToken(applicationUser, expiration, null);
loginModel.Exp = expiration.ToBinary();
loginModel.IsLogged = true;
logger.LogInformation("User logged in successfully: {Login}", requestModel.Login);
return Ok(loginModel);
}
if (signInResult.RequiresTwoFactor)
{
logger.LogInformation("Two-factor authentication required for user: {Login}", requestModel.Login);
loginModel.Message = "Two-factor authentication is required.";
loginModel.RequiresTwoFactor = true;
return Ok(loginModel);
}
logger.LogWarning("Incorrect login or password for user: {Login}", requestModel.Login);
loginModel.Message = "Incorrect login or password.";
return Unauthorized(loginModel);
}
[HttpPost]
[Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])]
public async Task<ActionResult<RegisterReturnModel>> Register([FromBody]RegisterModel registerModel)
{
logger.LogInformation("Registration request received for user: {Username}", registerModel.Username);
var registerReturnModel = new RegisterReturnModel();
registerReturnModel.IsCreated = true;
if (registerModel.Username != null)
{
var user = new ApplicationUser(registerModel.Username);
user.Email = registerModel.Email;
if (registerModel.RegisterToken != null)
{
var regToken = await registerTokenManager.GetToken(registerModel.RegisterToken);
if (regToken == null)
{
logger.LogWarning("Registration token not found: {RegisterToken}", registerModel.RegisterToken);
registerReturnModel.IsCreated = false;
registerReturnModel.ProblemWithToken = "Token Not found;";
return Unauthorized(registerReturnModel);
}
if (regToken.IsUsed)
{
logger.LogWarning("Registration token already used: {RegisterToken}", registerModel.RegisterToken);
registerReturnModel.IsCreated = false;
registerReturnModel.ProblemWithToken = "Token is alleredy used;";
}
if (( registerModel.Password ?? "" ).Length > 6)
{
registerReturnModel.IsCreated = false;
}
if (!registerReturnModel.IsCreated) return Unauthorized(registerReturnModel);
if (registerModel.Password != null)
{
var result = await userManager.CreateAsync(user, registerModel.Password);
var resultClaim = await userManager.AddClaimAsync(user, AccountType.User.Claim());
if (result.Succeeded && resultClaim.Succeeded)
{
regToken.IsUsed = true;
await applicationDbContext.SaveChangesAsync();
registerReturnModel.AuthToken =
await tokenRepo.CreateJwtToken(user, DateTime.UtcNow.AddHours(1), null);
logger.LogInformation("User registered successfully: {Username}", registerModel.Username);
}
else
{
foreach (var error in result.Errors)
{
logger.LogError("Error during user registration: {Code} - {Description}", error.Code, error.Description);
registerReturnModel.OtherWithOtherProblem += error.Code + "-" + error.Description + ";";
return Unauthorized(registerReturnModel);
}
}
}
}
else
{
logger.LogWarning("Registration token is required but missing for user: {Username}", registerModel.Username);
return BadRequest("Token are required.");
}
}
return Ok(registerReturnModel);
}
[HttpGet]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public async Task<LogOutModel> LogOut()
{
logger.LogInformation("Logout request received.");
if (Request.Headers.TryGetValue("Authorization", out var token))
{
var tokenStr = token.ToString();
string?[] tokenSplit = tokenStr.Split(" ");
if (tokenSplit.Length > 0) tokenStr = tokenSplit[1];
if (await tokenRepo.LockTokenAsync(tokenStr) > 0)
{
logger.LogInformation("User logged out successfully.");
return new LogOutModel(true, "Logout successful");
}
}
logger.LogWarning("Logout failed due to missing or invalid token.");
return new LogOutModel(false, "Logout Fail");
}
[HttpGet]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public async Task<ActionResult<RegeneratedTokenModel>> RegenerateToken()
{
logger.LogInformation("Token regeneration request received.");
Request.Headers.TryGetValue("Authorization", out var token);
token = token.ToString().Split(" ")[1];
var newTokenStr = await tokenRepo.RegenerateJwtToken(token, DateTime.UtcNow.AddMinutes(30));
logger.LogInformation("Token regenerated successfully");
var respond = new RegeneratedTokenModel()
{
Mess = newTokenStr != null ? "Regeneration success" : "Regeneration fail",
Token = newTokenStr
};
return Ok(respond);
}
[HttpDelete]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HasPrivilege(GlobalPrivilage.Privilege.GlobalDeleteExpiredToken)]
public async Task<ActionResult<int>> DeleteExpiredToken()
{
logger.LogInformation("Delete expired tokens request received.");
return Ok(await tokenRepo.DeleteInactiveToken());
}
[HttpPost]
public async Task<ActionResult<bool>> TokenIsLock([FromBody] TokenAskIfIsLockModel req)
{
logger.LogInformation("Check if token is locked request received.");
return Ok(await tokenRepo.IsTokenLockAsync(req.Token));
}
}
@@ -0,0 +1,65 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using OpenWarehouse.auth.lib.Model.Privilage;
namespace OpenWarehouse.auth.api.Controller;
[ApiController]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Route("[controller]/[action]")]
public class Privilege(UserManager<ApplicationUser> userManager,
PrivilegeManager privilegeManager, ILogger<Privilege> logger) : ControllerBase
{
[HttpPost]
public async Task<ActionResult<List<string>>> GetUserPrivilege()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
logger.LogInformation("GetUserPrivilege request received for user: {UserId}", userId);
if (userId == null)
{
logger.LogWarning("User ID not found in claims");
return BadRequest();
}
var user = await userManager.FindByIdAsync(userId);
if (user == null)
{
logger.LogWarning("User not found for ID: {UserId}", userId);
return Unauthorized("User not found");
}
var privilege = await privilegeManager.GetUserPrivilage(user);
logger.LogInformation("Privileges retrieved for user {UserId}", userId);
return Ok(privilege);
}
[HttpPost]
[HasPrivilege(GlobalPrivilage.Privilege.GlobalEditRole)]
public async Task<ActionResult> CreatePrivilage([FromBody] CreatePrivilageModel privilage)
{
logger.LogInformation("CreatePrivilege request received for privilege: {Privilege}", privilage.Privilage);
if (privilage is { Privilage: null })
{
logger.LogWarning("Invalid privilege model provided");
return BadRequest();
}
var ok = await privilegeManager.CreatePrivageAsync(privilage.Privilage);
if (ok.IsSuccessed)
{
logger.LogInformation("Privilege created successfully");
return Created();
}
else
{
logger.LogWarning("Failed to create privilege");
return BadRequest();
}
}
}
+230
View File
@@ -0,0 +1,230 @@
using System.Net.Mime;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using OpenWarehouse.auth.lib.Model;
using OpenWarehouse.auth.lib.Model.Account;
using OpenWarehouse.auth.lib.Model.Role;
namespace OpenWarehouse.auth.api.Controller;
[ApiController]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Route("[controller]/[action]")]
public class Role(
UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager,
PrivilegeManager privilegeManager) : ControllerBase
{
[HttpPost]
public async Task<ActionResult<RoleListModel>> GetRoles()
{
var roles = await roleManager.Roles.ToListAsync();
var roleList = new RoleListModel();
foreach (var role in roles)
{
var roleC = new RoleModel() { RoleName = role.Name ?? "", IsGlobalAdmin = role.IsGlobalAdmin };
var privilage = await privilegeManager.GetRolePrivilage(role);
if (privilage != null)
roleC.Privilage = privilage;
roleList.Roles?.Add(roleC);
}
return roleList;
}
[HttpPost]
[Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])]
public async Task<ActionResult<UserRoleModel>> GetUserRoles([FromBody] UserRoleReqestModel req)
{
if (req is { UserIdentify: not null })
{
var user = (await userManager.FindByNameAsync(req.UserIdentify) )
?? (await userManager.FindByEmailAsync(req.UserIdentify))
?? (await userManager.FindByIdAsync(req.UserIdentify));
if (user == null) return StatusCode(500, new ErrorModel(){StatusCode = 500, Message = "User not found"});
var rolesNames = await userManager.GetRolesAsync(user);
var userRoleModel = new UserRoleModel() { UserId = user.Id, Username = user.UserName, Roles = new List<RoleModel>()};
foreach (var roleName in rolesNames)
{
var role = await roleManager.FindByNameAsync(roleName);
if (role != null)
{
var roleC = new RoleModel
{
RoleName = role.Name ?? "", IsGlobalAdmin = role.IsGlobalAdmin,
Privilage = await privilegeManager.GetRolePrivilage(role)
};
userRoleModel.Roles.Add(roleC);
}
}
return Ok(userRoleModel);
}
else
{
return StatusCode(500, new ErrorModel(){StatusCode = 500, Message = "Invalid reqest"});
}
}
[HttpPost]
[HasPrivilege(GlobalPrivilage.Privilege.GlobalCreateRole)]
[Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])]
public async Task<ActionResult<OkModel>> CreateRole([FromBody] CreateRoleModel req)
{
if (req is { Name: not null })
{
var okModel = new OkModel();
var role = new ApplicationRole() { Name = req.Name, IsGlobalAdmin = false};
if (req is { Privilage: not null })
{
foreach (var privilageName in req.Privilage)
{
var privilage = req.ForcePrivilage
? await privilegeManager.GetPrivilageByName(privilageName)
: await privilegeManager.GetPrivilageByNameIfDontExistCreateAsync(privilageName);
if (privilage != null)
{
role.Privilege.Add(privilage);
}
else
{
okModel.Message += $"Privilage {privilageName} not found;";
}
}
}
var result = await roleManager.CreateAsync(role);
if (result.Succeeded)
{
okModel.IsSuccess = true;
return Ok(okModel);
}
else
{
okModel.Message += string.Join(";", result.Errors.Select(error => error.Description));
okModel.IsSuccess = false;
return StatusCode(500, okModel);
}
}
else
{
return StatusCode(500, new ErrorModel(){StatusCode = 500, Message = "Invalid reqest"});
}
}
[HttpPost]
[HasPrivilege(GlobalPrivilage.Privilege.GlobalEditUser)]
[Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])]
public async Task<ActionResult<OkModel>> AddRoleToUser([FromBody] AddRoleToUserModel req)
{
if (req is { UserIdentify: not null, RoleIdentify: not null })
{
var user = (await userManager.FindByNameAsync(req.UserIdentify))
?? (await userManager.FindByEmailAsync(req.UserIdentify))
?? (await userManager.FindByIdAsync(req.UserIdentify));
var role = (await roleManager.FindByIdAsync(req.RoleIdentify))
?? (await roleManager.FindByNameAsync(req.RoleIdentify));
if (user is null || role is null)
{
return Problem("User or Role not found");
}
var result = await userManager.AddToRoleAsync(user, role.Name);
if (result.Succeeded)
{
var rolePriv = await privilegeManager.GetRolePrivilage(role);
var roleModel = new RoleModel()
{ IsGlobalAdmin = role.IsGlobalAdmin, RoleName = role.Name, Privilage = rolePriv };
return Ok(new OkModel() { Message = "Ok", IsSuccess = true, Value = roleModel });
}
else
{
return Ok(new OkModel()
{ Message = string.Join(";", result.Errors.Select(error => error.Description)), IsSuccess = true });
}
}else
{
return Problem("Invalid request");
}
}
[HttpPost]
[HasPrivilege(GlobalPrivilage.Privilege.GlobalEditRole)]
[Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])]
public async Task<ActionResult<RoleModel>> EditRolePrivilege([FromBody]EditRoleModel req)
{
var role = (await roleManager.FindByIdAsync(req.RoleIdentify))
?? (await roleManager.FindByNameAsync(req.RoleIdentify));
if (role == null) Ok("Not found!");
foreach (var addPriv in req.AddPrivilage)
{
RolePrivilage? priv;
if (req.ForceAddedPrivilage)
{
priv = await privilegeManager.GetPrivilageByNameIfDontExistCreateAsync(addPriv);
}
else
{
priv = await privilegeManager.GetPrivilageByName(addPriv);
}
if (priv is {Privilage: not null} && role != null) await privilegeManager.AddRolePrivilage(role, priv.Privilage);
}
if (req.DeletePrivilage != null)
foreach (var delPriv in req.DeletePrivilage)
{
await privilegeManager.DeleteRolePrivilage(role, delPriv);
}
if (req.NewName != null && req.NewName != "string")
{
if (role != null) role.Name = req.NewName;
}
if (role != null)
{
var privilage = await privilegeManager.GetRolePrivilage(role);
var roleModel = new RoleModel()
{ RoleName = role.Name, IsGlobalAdmin = role.IsGlobalAdmin, Privilage = privilage };
return Ok(roleModel);
}
return Problem();
}
[HttpDelete]
[HasPrivilege(GlobalPrivilage.Privilege.GlobalDeleteRole)]
[Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])]
public async Task<ActionResult<OkModel>> DeleteRole([FromBody] DeleteRoleModel req)
{
if (req.RoleIdentify != null)
{
var role = (await roleManager.FindByIdAsync(req.RoleIdentify))
?? (await roleManager.FindByNameAsync(req.RoleIdentify));
if (role != null)
{
var result = await roleManager.DeleteAsync(role);
if (result.Succeeded)
{
return Ok(new OkModel() { Message = "", IsSuccess = result.Succeeded });
}
else
{
return Ok(new OkModel() { Message = string.Join(";", result.Errors.Select(error => error.Description)), IsSuccess = false });
}
}
}
return Ok(new OkModel() { Message = "Role not found.", IsSuccess = false });
}
}
@@ -0,0 +1,104 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography;
using System.Text;
using Kafka.Lib.Auth;
using MassTransit;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using OpenWarehouse.auth.api.Common.AuthTokenRepo;
using OpenWarehouse.auth.api.Common.CustomIdentifyManager;
using OpenWarehouse.auth.api.Common.ServiceManager;
using OpenWarehouse.auth.lib.Model.Service;
namespace OpenWarehouse.auth.api.Controller;
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
[Route("[controller]/[action]")]
public class Service(
UserManager<ApplicationUser> userManager,
IConfiguration configuration, AuthTokenRepo tokenRepo,
IServiceManager serviceManager, ILogger<Service> logger,
ITopicProducer<RegisterNewServiceMessageType> serviceTopic) : ControllerBase
{
[HttpPost]
[HasPrivilege(GlobalPrivilage.Privilege.GlobalAdmin)]
public async Task<ActionResult<CreateServiceTokenReturnModel>> GetTokenForService([FromBody] CreateServiceTokenModel req)
{
if (req is { ServiceName: not null, ServiceRoles: not null, ServicePrivilages: not null })
{
var result = new CreateServiceTokenReturnModel(req.ServiceName)
{
ServiceUsername = $"Service-{req.ServiceName}"
};
var userExist = await serviceManager.CheckIfServiceUserExist(req.ServiceName);
if (!userExist)
{
var userRes = await serviceManager.CreateServiceUserResult(req.ServiceName, true);
result.UserAllereadyExist = false;
result.IsSuccess = userRes;
}
else
{
result.UserAllereadyExist = true;
}
var user = await userManager.FindByNameAsync($"Service-{req.ServiceName}");
if (user == null)
{
return BadRequest();
}
result.PrivilageResults = await serviceManager.CreateServicesPrivilage(req.ServicePrivilages);
result.RoleResults = await serviceManager.CreateServiceRoles(req.ServiceRoles);
result.ServiceToken =
await tokenRepo.CreateJwtToken(user, null, new List<Claim>() { AccountType.Service.Claim() });
var topic = new RegisterNewServiceMessageType()
{
ServiceName = req.ServiceName,
ServiceAccountUserName = user.UserName,
ServicePrivilage = req.ServicePrivilages
};
_ = serviceTopic.Produce(topic);
logger.LogInformation($"New service {req.ServiceName} with service account {user.UserName} " +
$"and privileges {req.ServicePrivilages} register success.");
return Ok(result);
}
else
{
return BadRequest();
}
}
[HttpPost]
[HasPrivilege(GlobalPrivilage.Privilege.GlobalAdmin)]
public async Task<ActionResult<bool>> TestServiceToken()
{
var userId = HttpContext.User.Claims.FirstOrDefault(q => q.Type == ClaimTypes.NameIdentifier)?.Value;
if (userId == null) return BadRequest();
var user = await userManager.FindByIdAsync(userId);
if (user == null) return Ok(false);
var userClaim = await userManager.GetClaimsAsync(user);
var serviceClaim = AccountType.Service.Claim();
if(! userClaim.Any(q=> q.Type ==serviceClaim.Type && q.Value == serviceClaim.Value))
{
return Ok(false);
}
logger.LogInformation($"Regenerated token for service account: {user.UserName}");
return Ok(true);
}
}
@@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Authorization;
using OpenWarehouse.auth.lib.Model.Service;
namespace OpenWarehouse.auth.api.Controller;
[ApiController]
[Route("[action]")]
public class Tools(
ILogger<Tools> logger) : ControllerBase
{
[HttpGet]
public Task<ActionResult<PingModel>> Ping()
{
logger.LogInformation("Ping request received.");
return Task.FromResult<ActionResult<PingModel>>(Ok(new PingModel()));
}
[HttpGet]
public Task<ActionResult<bool>> TestAuthStatus()
{
logger.LogInformation($"Test auth status request received for user: {User.Identity?.Name ?? "Unknown"}.");
return Task.FromResult<ActionResult<bool>>(User.Identity?.IsAuthenticated);
}
}
@@ -0,0 +1,62 @@
using System.Security.Cryptography;
using Microsoft.IdentityModel.Tokens;
using OpenWarehouse.auth.lib.Model.WellKnow;
namespace OpenWarehouse.auth.api.Controller;
[ApiController]
public class WellKnown(IConfiguration configuration, ILogger<WellKnown> logger) : ControllerBase
{
[HttpGet(".well-known/openid-configuration")]
public ActionResult<OpenIdConfiguration> GetOpenIdConfiguration()
{
var issuer = configuration["Jwt:Issuer"] ?? "https://your-oidc-server.com";
var oidcConfig = new OpenIdConfiguration
{
Issuer = issuer,
AuthorizationEndpoint = $"{issuer}/auth/login",
TokenEndpoint = $"{issuer}/auth/token",
JwksUri = $"{issuer}/.well-known/jwks",
ResponseTypesSupported = new[] { "code", "id_token", "token" },
SubjectTypesSupported = new[] { "public" },
IdTokenSigningAlgValuesSupported = new[] { "RS256" }
};
logger.LogInformation("Served OpenID configuration.");
return Ok(oidcConfig);
}
[HttpGet(".well-known/jwks")]
public ActionResult<JwksResponse> GetJwks()
{
var rsaKeyPath = Path.Combine(configuration["contentRoot"] ?? throw new Exception("content root not found"), "private_key_pkcs8.pem");
if (!System.IO.File.Exists(rsaKeyPath))
{
logger.LogError("RSA key file not found at path: {Path}", rsaKeyPath);
return Problem("RSA key not found.", statusCode: 500);
}
using var rsa = RSA.Create();
rsa.ImportFromPem(System.IO.File.ReadAllText(rsaKeyPath));
var parameters = rsa.ExportParameters(false);
var jwk = new Jwk
{
Kty = "RSA",
E = Base64UrlEncoder.Encode(parameters.Exponent),
N = Base64UrlEncoder.Encode(parameters.Modulus),
Use = "sig",
Alg = "RS256",
Kid = "1"
};
var jwksResponse = new JwksResponse
{
Keys = new[] { jwk }
};
logger.LogInformation("JWKS configuration served.");
return Ok(jwksResponse);
}
}