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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user