using System.Security.Claims; using System.Text.Json; using MesaFabApproval.Shared.Models; using Microsoft.Extensions.Caching.Memory; namespace MesaFabApproval.Client.Services; public interface IUserService { ClaimsPrincipal GetClaimsPrincipalFromUser(User user); string GetLoginIdFromClaimsPrincipal(ClaimsPrincipal claimsPrincipal); Task GetUserFromClaimsPrincipal(ClaimsPrincipal claimsPrincipal); Task GetUserByUserId(int userId); Task GetUserByLoginId(string loginId); Task> GetAllActiveUsers(); Task> GetApproverUserIdsBySubRoleCategoryItem(string item); } public class UserService : IUserService { private readonly IMemoryCache _cache; private readonly IHttpClientFactory _httpClientFactory; public UserService(IMemoryCache cache, IHttpClientFactory httpClientFactory) { _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException("IHttpClientFactory not injected"); } public ClaimsPrincipal GetClaimsPrincipalFromUser(User user) { if (user is null) throw new ArgumentNullException("user cannot be null"); List claims = new() { new Claim(nameof(user.LoginID), user.LoginID) }; if (user.IsManager) claims.Add(new Claim(ClaimTypes.Role, "manager")); if (user.IsAdmin) claims.Add(new Claim(ClaimTypes.Role, "admin")); ClaimsIdentity identity = new ClaimsIdentity(claims, "MesaFabApprovalWasm"); return new ClaimsPrincipal(identity); } public async Task GetUserByUserId(int userId) { if (userId <= 0) throw new ArgumentException($"{userId} is not a valid user ID"); User? user = _cache.Get($"user{userId}"); if (user is null) user = _cache.Get>("allActiveUsers")?.FirstOrDefault(u => u.UserID == userId); if (user is null) { HttpClient httpClient = _httpClientFactory.CreateClient("API"); HttpRequestMessage requestMessage = new(HttpMethod.Get, $"user/userId?userId={userId}"); HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); if (responseMessage.IsSuccessStatusCode) { string responseContent = await responseMessage.Content.ReadAsStringAsync(); JsonSerializerOptions jsonSerializerOptions = new() { PropertyNameCaseInsensitive = true }; user = JsonSerializer.Deserialize(responseContent, jsonSerializerOptions) ?? throw new Exception("Unable to parse user from API response"); _cache.Set($"user{userId}", user, DateTimeOffset.Now.AddDays(1)); } else { throw new Exception($"GetUserByUserId failed for user {userId}, because {responseMessage.ReasonPhrase}"); } } if (user is null) throw new Exception($"User for userId {userId} not found"); return user; } public async Task GetUserByLoginId(string loginId) { if (string.IsNullOrWhiteSpace(loginId)) throw new ArgumentNullException("loginId cannot be null or empty"); User? user = _cache.Get($"user{loginId}"); if (user is null) user = _cache.Get>("allActiveUsers")?.FirstOrDefault(u => u.LoginID == loginId); if (user is null) { HttpClient httpClient = _httpClientFactory.CreateClient("API"); HttpRequestMessage requestMessage = new(HttpMethod.Get, $"user/loginId?loginId={loginId}"); HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); if (responseMessage.IsSuccessStatusCode) { string responseContent = await responseMessage.Content.ReadAsStringAsync(); JsonSerializerOptions jsonSerializerOptions = new() { PropertyNameCaseInsensitive = true }; user = JsonSerializer.Deserialize(responseContent, jsonSerializerOptions) ?? throw new Exception("Unable to parse user from API response"); _cache.Set($"user{loginId}", user, DateTimeOffset.Now.AddDays(1)); } else { throw new Exception($"GetUserByLoginId failed for {loginId}, because {responseMessage.ReasonPhrase}"); } } if (user is null) throw new Exception($"User for loginId {loginId} not found"); return user; } public async Task> GetAllActiveUsers() { IEnumerable? activeUsers = _cache.Get>("allActiveUsers"); if (activeUsers is null) { HttpClient httpClient = _httpClientFactory.CreateClient("API"); HttpRequestMessage requestMessage = new(HttpMethod.Get, $"users/active"); HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); if (responseMessage.IsSuccessStatusCode) { string responseContent = await responseMessage.Content.ReadAsStringAsync(); JsonSerializerOptions jsonSerializerOptions = new() { PropertyNameCaseInsensitive = true }; activeUsers = JsonSerializer.Deserialize>(responseContent, jsonSerializerOptions) ?? throw new Exception("Unable to parse user from API response"); _cache.Set("allActiveUsers", activeUsers, DateTimeOffset.Now.AddHours(1)); } else { throw new Exception($"Cannot get all active users, because {responseMessage.ReasonPhrase}"); } } if (activeUsers is null) activeUsers = new List(); return activeUsers; } public string GetLoginIdFromClaimsPrincipal(ClaimsPrincipal principal) { if (principal is null) throw new ArgumentNullException("Principal cannot be null"); Claim loginIdClaim = principal.FindFirst("LoginID") ?? throw new Exception("LoginID claim not found in principal"); string loginId = loginIdClaim.Value; return loginId; } public async Task GetUserFromClaimsPrincipal(ClaimsPrincipal claimsPrincipal) { if (claimsPrincipal is null) throw new ArgumentNullException("ClaimsPrincipal cannot be null"); Claim loginIdClaim = claimsPrincipal.FindFirst("LoginID") ?? throw new Exception("LoginID claim not found in principal"); string loginId = loginIdClaim.Value ?? throw new Exception("LoginID claim value is null"); User user = await GetUserByLoginId(loginId) ?? throw new Exception($"User for loginId {loginId} not found"); return user; } public async Task> GetApproverUserIdsBySubRoleCategoryItem(string item) { if (string.IsNullOrWhiteSpace(item)) throw new ArgumentException("SubRoleCategoryItem cannot be null or empty"); IEnumerable? approverUserIds = _cache.Get>($"approvers{item}"); if (approverUserIds is null) { HttpClient httpClient = _httpClientFactory.CreateClient("API"); HttpRequestMessage requestMessage = new(HttpMethod.Get, $"approver?subRoleCategoryItem={item}"); HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); if (responseMessage.IsSuccessStatusCode) { string responseContent = await responseMessage.Content.ReadAsStringAsync(); JsonSerializerOptions jsonSerializerOptions = new() { PropertyNameCaseInsensitive = true }; approverUserIds = JsonSerializer.Deserialize>(responseContent, jsonSerializerOptions) ?? throw new Exception("Unable to parse user from API response"); _cache.Set($"approvers{item}", approverUserIds, DateTimeOffset.Now.AddDays(1)); } else { throw new Exception($"Unable to get approvers for SubRoleCategoryItem {item}, because {responseMessage.ReasonPhrase}"); } } if (approverUserIds is null) throw new Exception($"Approvers for SubRoleCategoryItem {item} not found"); return approverUserIds; } }