diff --git a/api/src/main/java/ca/bc/gov/educ/api/graduation/config/RequestInterceptor.java b/api/src/main/java/ca/bc/gov/educ/api/graduation/config/RequestInterceptor.java index a7b9dfd2..9390b780 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/graduation/config/RequestInterceptor.java +++ b/api/src/main/java/ca/bc/gov/educ/api/graduation/config/RequestInterceptor.java @@ -1,14 +1,15 @@ package ca.bc.gov.educ.api.graduation.config; -import ca.bc.gov.educ.api.graduation.util.EducGraduationApiConstants; -import ca.bc.gov.educ.api.graduation.util.GradValidation; -import ca.bc.gov.educ.api.graduation.util.LogHelper; -import ca.bc.gov.educ.api.graduation.util.ThreadLocalStateUtil; +import ca.bc.gov.educ.api.graduation.util.*; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.val; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.NonNull; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.stereotype.Component; import org.springframework.web.servlet.AsyncHandlerInterceptor; @@ -42,6 +43,14 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons if (correlationID != null) { ThreadLocalStateUtil.setCorrelationID(correlationID); } + + // username + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth instanceof JwtAuthenticationToken authenticationToken) { + Jwt jwt = (Jwt) authenticationToken.getCredentials(); + String username = JwtUtil.getName(jwt, request); + ThreadLocalStateUtil.setCurrentUser(username); + } return true; } diff --git a/api/src/main/java/ca/bc/gov/educ/api/graduation/service/RESTService.java b/api/src/main/java/ca/bc/gov/educ/api/graduation/service/RESTService.java index cd1a28e7..bb7eea45 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/graduation/service/RESTService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/graduation/service/RESTService.java @@ -77,7 +77,10 @@ public T get(String url, Class clazz) { obj = graduationServiceWebClient .get() .uri(url) - .headers(h -> h.set(EducGraduationApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID())) + .headers(h -> { + h.set(EducGraduationApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()); + h.set(EducGraduationApiConstants.USERNAME, ThreadLocalStateUtil.getCurrentUser()); + }) .retrieve() // if 5xx errors, throw Service error .onStatus(HttpStatusCode::is5xxServerError, @@ -141,7 +144,10 @@ public T post(String url, Object body, Class clazz) { try { obj = graduationServiceWebClient.post() .uri(url) - .headers(h -> h.set(EducGraduationApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID())) + .headers(h -> { + h.set(EducGraduationApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()); + h.set(EducGraduationApiConstants.USERNAME, ThreadLocalStateUtil.getCurrentUser()); + }) .body(BodyInserters.fromValue(body)) .retrieve() .onStatus(HttpStatusCode::is5xxServerError, diff --git a/api/src/main/java/ca/bc/gov/educ/api/graduation/util/EducGraduationApiConstants.java b/api/src/main/java/ca/bc/gov/educ/api/graduation/util/EducGraduationApiConstants.java index cd95174c..8ff98771 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/graduation/util/EducGraduationApiConstants.java +++ b/api/src/main/java/ca/bc/gov/educ/api/graduation/util/EducGraduationApiConstants.java @@ -13,6 +13,7 @@ public class EducGraduationApiConstants { public static final String CORRELATION_ID = "correlationID"; + public static final String USERNAME = "username"; //API end-point Mapping constants public static final String API_ROOT_MAPPING = ""; diff --git a/api/src/main/java/ca/bc/gov/educ/api/graduation/util/JwtUtil.java b/api/src/main/java/ca/bc/gov/educ/api/graduation/util/JwtUtil.java new file mode 100644 index 00000000..9e813cb7 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/graduation/util/JwtUtil.java @@ -0,0 +1,94 @@ +package ca.bc.gov.educ.api.graduation.util; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.val; +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.oauth2.jwt.Jwt; + +import java.util.Map; + +/** + * The type JWT util. + */ +public class JwtUtil { + + private JwtUtil() { + } + + /** + * Gets username string from object. + * + * @param jwt the JWT + * @return the username string from jwt + */ + public static String getUsername(Jwt jwt) { + return (String) jwt.getClaims().get("preferred_username"); + } + + /** + * Gets email string from object. + * + * @param jwt the JWT + * @return the username string from jwt + */ + public static String getEmail(Jwt jwt) { + return (String) jwt.getClaims().get("email"); + } + + /** + * Gets name string from object. + * + * @param jwt the JWT + * @return the username string from jwt + */ + public static String getName(Jwt jwt) { + StringBuilder sb = new StringBuilder(); + if (isServiceAccount(jwt.getClaims())) { + sb.append("Batch Process"); + } else { + String givenName = (String) jwt.getClaims().get("given_name"); + if (StringUtils.isNotBlank(givenName)) { + sb.append(givenName.charAt(0)); + } + String familyName = (String) jwt.getClaims().get("family_name"); + sb.append(familyName); + } + return sb.toString(); + } + + /** + * Gets name string + * => If it is service account, get it from request header. Otherwise, get username from jwt + * + * @param jwt the JWT + * @param request the Request Header + * @return the username string + */ + public static String getName(Jwt jwt, HttpServletRequest request) { + StringBuilder sb = new StringBuilder(); + if (isServiceAccount(jwt.getClaims())) { + sb.append(getUserNameString(request)); + } else { + String givenName = (String) jwt.getClaims().get("given_name"); + if (StringUtils.isNotBlank(givenName)) { + sb.append(givenName.charAt(0)); + } + String familyName = (String) jwt.getClaims().get("family_name"); + sb.append(familyName); + } + return sb.toString(); + } + + private static String getUserNameString(HttpServletRequest request) { + val username = request.getHeader(EducGraduationApiConstants.USERNAME); + if (StringUtils.isNotBlank(username)) { + return username; + } else { + return "Batch Process"; + } + } + + public static boolean isServiceAccount(Map claims) { + return !claims.containsKey("family_name"); + } +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/graduation/util/ThreadLocalStateUtil.java b/api/src/main/java/ca/bc/gov/educ/api/graduation/util/ThreadLocalStateUtil.java index fa0a8209..d1c5b824 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/graduation/util/ThreadLocalStateUtil.java +++ b/api/src/main/java/ca/bc/gov/educ/api/graduation/util/ThreadLocalStateUtil.java @@ -1,8 +1,12 @@ package ca.bc.gov.educ.api.graduation.util; +import java.util.Objects; + public class ThreadLocalStateUtil { private static ThreadLocal transaction = new ThreadLocal<>(); + private static ThreadLocal user = new ThreadLocal<>(); + /** * Set the current correlationID for this thread * @@ -21,7 +25,26 @@ public static String getCorrelationID() { return transaction.get(); } + /** + * Set the current user for this thread + * + * @param currentUser + */ + public static void setCurrentUser(String currentUser){ + user.set(currentUser); + } + + /** + * Get the current user for this thread + * + * @return the username of the current user, or null if it is unknown. + */ + public static String getCurrentUser() { + return Objects.requireNonNullElse(user.get(), "GRAD"); + } + public static void clear() { transaction.remove(); + user.remove(); } }