From 7e0f0485fd0a46910fff7899bee593d6773d14f1 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 17 Mar 2026 15:47:03 +0700 Subject: [PATCH] Add TokenUserInfo class and extend ClaimsPrincipal with methods for user claims retrieval --- BMA.EHR.Domain/Common/BaseController.cs | 17 ++- BMA.EHR.Domain/Common/TokenUserInfo.cs | 39 ++++++ .../Extensions/ClaimsPrincipalExtensions.cs | 30 +++++ ...ombinedErrorHandlerAndLoggingMiddleware.cs | 119 ++++++++++++++++-- BMA.EHR.Leave/Controllers/LeaveController.cs | 8 +- 5 files changed, 201 insertions(+), 12 deletions(-) create mode 100644 BMA.EHR.Domain/Common/TokenUserInfo.cs create mode 100644 BMA.EHR.Domain/Extensions/ClaimsPrincipalExtensions.cs diff --git a/BMA.EHR.Domain/Common/BaseController.cs b/BMA.EHR.Domain/Common/BaseController.cs index 44d8dac0..26f71bf5 100644 --- a/BMA.EHR.Domain/Common/BaseController.cs +++ b/BMA.EHR.Domain/Common/BaseController.cs @@ -1,4 +1,5 @@ -using BMA.EHR.Domain.Shared; +using BMA.EHR.Domain.Extensions; +using BMA.EHR.Domain.Shared; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -81,6 +82,20 @@ namespace BMA.EHR.Domain.Common } + #endregion + + #region " Properties " + + protected string? EmpType => User.GetEmpType(); + protected Guid? OrgChild1DnaId => User.GetOrgChild1DnaId(); + protected Guid? OrgChild2DnaId => User.GetOrgChild2DnaId(); + protected Guid? OrgChild3DnaId => User.GetOrgChild3DnaId(); + protected Guid? OrgChild4DnaId => User.GetOrgChild4DnaId(); + protected Guid? OrgRootDnaId => User.GetOrgRootDnaId(); + protected Guid? ProfileId => User.GetProfileId(); + protected string? Prefix => User.GetPrefix(); + protected string? FullNameFromClaim => User.GetName(); + #endregion #endregion diff --git a/BMA.EHR.Domain/Common/TokenUserInfo.cs b/BMA.EHR.Domain/Common/TokenUserInfo.cs new file mode 100644 index 00000000..cdae6fb1 --- /dev/null +++ b/BMA.EHR.Domain/Common/TokenUserInfo.cs @@ -0,0 +1,39 @@ +namespace BMA.EHR.Domain.Common +{ + public class TokenUserInfo + { + // Existing properties + public string KeycloakId { get; set; } = string.Empty; + public string? PreferredUsername { get; set; } + public string? GivenName { get; set; } + public string? FamilyName { get; set; } + + // New properties to add + public string? EmpType { get; set; } + public Guid? OrgChild1DnaId { get; set; } + public Guid? OrgChild2DnaId { get; set; } + public Guid? OrgChild3DnaId { get; set; } + public Guid? OrgChild4DnaId { get; set; } + public Guid? OrgRootDnaId { get; set; } + public Guid? ProfileId { get; set; } + public string? Prefix { get; set; } + public string? Name { get; set; } + } + + // Claim type constants + public static class BmaClaimTypes + { + public const string EmpType = "empType"; + public const string OrgChild1DnaId = "orgChild1DnaId"; + public const string OrgChild2DnaId = "orgChild2DnaId"; + public const string OrgChild3DnaId = "orgChild3DnaId"; + public const string OrgChild4DnaId = "orgChild4DnaId"; + public const string OrgRootDnaId = "orgRootDnaId"; + public const string ProfileId = "profileId"; + public const string Prefix = "prefix"; + public const string Name = "name"; + public const string GivenName = "given_name"; + public const string FamilyName = "family_name"; + public const string PreferredUsername = "preferred_username"; + } +} diff --git a/BMA.EHR.Domain/Extensions/ClaimsPrincipalExtensions.cs b/BMA.EHR.Domain/Extensions/ClaimsPrincipalExtensions.cs new file mode 100644 index 00000000..26a7c189 --- /dev/null +++ b/BMA.EHR.Domain/Extensions/ClaimsPrincipalExtensions.cs @@ -0,0 +1,30 @@ +using BMA.EHR.Domain.Common; +using System.Security.Claims; + +namespace BMA.EHR.Domain.Extensions +{ + public static class ClaimsPrincipalExtensions + { + public static string? GetClaimValue(this ClaimsPrincipal user, string claimType) + { + return user?.FindFirst(claimType)?.Value; + } + + public static Guid? GetGuidClaim(this ClaimsPrincipal user, string claimType) + { + var value = user?.GetClaimValue(claimType); + return Guid.TryParse(value, out var guid) ? guid : null; + } + + // Convenience methods for common claims + public static string? GetEmpType(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.EmpType); + public static Guid? GetOrgChild1DnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgChild1DnaId); + public static Guid? GetOrgChild2DnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgChild2DnaId); + public static Guid? GetOrgChild3DnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgChild3DnaId); + public static Guid? GetOrgChild4DnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgChild4DnaId); + public static Guid? GetOrgRootDnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgRootDnaId); + public static Guid? GetProfileId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.ProfileId); + public static string? GetPrefix(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.Prefix); + public static string? GetName(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.Name); + } +} diff --git a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs index 15c88592..4216fa43 100644 --- a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs +++ b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs @@ -79,13 +79,39 @@ namespace BMA.EHR.Domain.Middlewares GetProfileByKeycloakIdLocal? pf = null; var tokenUserInfo = await ExtractTokenUserInfoAsync(token); + // Store tokenUserInfo in HttpContext.Items for controllers to use + context.Items["TokenUserInfo"] = tokenUserInfo; + // ดึง keycloakId จาก JWT token keycloakId = tokenUserInfo.KeycloakId; - // ดึง profile จาก cache หรือ API + // ดึง profile จาก claims หรือ cache หรือ API if (Guid.TryParse(keycloakId, out var parsedId) && parsedId != Guid.Empty) { - pf = await GetProfileWithCacheAsync(parsedId, token); + // Build profile from token claims if available + if (tokenUserInfo.OrgRootDnaId.HasValue && tokenUserInfo.ProfileId.HasValue) + { + pf = new GetProfileByKeycloakIdLocal + { + Id = tokenUserInfo.ProfileId.Value, + CitizenId = tokenUserInfo.PreferredUsername, + Prefix = tokenUserInfo.Prefix, + FirstName = tokenUserInfo.GivenName, + LastName = tokenUserInfo.FamilyName, + RootDnaId = tokenUserInfo.OrgRootDnaId, + Child1DnaId = tokenUserInfo.OrgChild1DnaId, + Child2DnaId = tokenUserInfo.OrgChild2DnaId, + Child3DnaId = tokenUserInfo.OrgChild3DnaId, + Child4DnaId = tokenUserInfo.OrgChild4DnaId, + }; + Console.WriteLine($"[INFO] Using claims for profile - OrgRootDnaId: {pf.RootDnaId}, ProfileId: {pf.Id}"); + } + else + { + // Fallback to API only if critical claims are missing + Console.WriteLine("[WARN] Critical claims missing, falling back to API call"); + pf = await GetProfileWithCacheAsync(parsedId, token); + } } try @@ -649,6 +675,87 @@ namespace BMA.EHR.Domain.Middlewares Console.WriteLine($"Extracted family_name: {result.FamilyName}"); } + // ดึง empType + if (jsonDoc.RootElement.TryGetProperty("empType", out var empTypeElement)) + { + result.EmpType = empTypeElement.GetString(); + Console.WriteLine($"Extracted empType: {result.EmpType}"); + } + + // ดึง orgChild1DnaId + if (jsonDoc.RootElement.TryGetProperty("orgChild1DnaId", out var orgChild1Element)) + { + if (Guid.TryParse(orgChild1Element.GetString(), out var orgChild1Guid)) + { + result.OrgChild1DnaId = orgChild1Guid; + Console.WriteLine($"Extracted orgChild1DnaId: {result.OrgChild1DnaId}"); + } + } + + // ดึง orgChild2DnaId + if (jsonDoc.RootElement.TryGetProperty("orgChild2DnaId", out var orgChild2Element)) + { + if (Guid.TryParse(orgChild2Element.GetString(), out var orgChild2Guid)) + { + result.OrgChild2DnaId = orgChild2Guid; + Console.WriteLine($"Extracted orgChild2DnaId: {result.OrgChild2DnaId}"); + } + } + + // ดึง orgChild3DnaId + if (jsonDoc.RootElement.TryGetProperty("orgChild3DnaId", out var orgChild3Element)) + { + if (Guid.TryParse(orgChild3Element.GetString(), out var orgChild3Guid)) + { + result.OrgChild3DnaId = orgChild3Guid; + Console.WriteLine($"Extracted orgChild3DnaId: {result.OrgChild3DnaId}"); + } + } + + // ดึง orgChild4DnaId + if (jsonDoc.RootElement.TryGetProperty("orgChild4DnaId", out var orgChild4Element)) + { + if (Guid.TryParse(orgChild4Element.GetString(), out var orgChild4Guid)) + { + result.OrgChild4DnaId = orgChild4Guid; + Console.WriteLine($"Extracted orgChild4DnaId: {result.OrgChild4DnaId}"); + } + } + + // ดึง orgRootDnaId + if (jsonDoc.RootElement.TryGetProperty("orgRootDnaId", out var orgRootElement)) + { + if (Guid.TryParse(orgRootElement.GetString(), out var orgRootGuid)) + { + result.OrgRootDnaId = orgRootGuid; + Console.WriteLine($"Extracted orgRootDnaId: {result.OrgRootDnaId}"); + } + } + + // ดึง profileId + if (jsonDoc.RootElement.TryGetProperty("profileId", out var profileIdElement)) + { + if (Guid.TryParse(profileIdElement.GetString(), out var profileIdGuid)) + { + result.ProfileId = profileIdGuid; + Console.WriteLine($"Extracted profileId: {result.ProfileId}"); + } + } + + // ดึง prefix + if (jsonDoc.RootElement.TryGetProperty("prefix", out var prefixElement)) + { + result.Prefix = prefixElement.GetString(); + Console.WriteLine($"Extracted prefix: {result.Prefix}"); + } + + // ดึง name + if (jsonDoc.RootElement.TryGetProperty("name", out var nameElement)) + { + result.Name = nameElement.GetString(); + Console.WriteLine($"Extracted name: {result.Name}"); + } + return result; } catch (Exception ex) @@ -767,14 +874,6 @@ namespace BMA.EHR.Domain.Middlewares } // Model classes - public class TokenUserInfo - { - public string KeycloakId { get; set; } = string.Empty; - public string? PreferredUsername { get; set; } - public string? GivenName { get; set; } - public string? FamilyName { get; set; } - } - public class GetProfileByKeycloakIdLocal { public Guid Id { get; set; } diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index d3907e66..acb10e8b 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -144,7 +144,13 @@ namespace BMA.EHR.Leave.Service.Controllers { get { - if (UserId != null || UserId != "") + // First try to get from claims + var ocIdFromClaims = OrgRootDnaId; + if (ocIdFromClaims.HasValue && ocIdFromClaims.Value != Guid.Empty) + return ocIdFromClaims.Value; + + // Fallback to API call for backward compatibility + if (UserId != null && UserId != "") return _userProfileRepository.GetUserOCId(Guid.Parse(UserId!), AccessToken); else return Guid.Empty;