From 2e15d2511cd6f1e832c1ac703e42f0d300fc5712 Mon Sep 17 00:00:00 2001 From: Jinil Sung Date: Fri, 22 Nov 2024 12:18:40 -0800 Subject: [PATCH 1/4] GRAD2-3012: task is complete. GRAD2-3012: task is complete. --- .../graduation/config/RequestInterceptor.java | 17 +++- .../api/graduation/service/RESTService.java | 10 +- .../util/EducGraduationApiConstants.java | 1 + .../gov/educ/api/graduation/util/JwtUtil.java | 94 +++++++++++++++++++ .../graduation/util/ThreadLocalStateUtil.java | 23 +++++ 5 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 api/src/main/java/ca/bc/gov/educ/api/graduation/util/JwtUtil.java 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..bb67ed28 --- /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 token + * + * @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(); } } From bc1d15038f0ed2fa322d9f647b3d15b03b7271b8 Mon Sep 17 00:00:00 2001 From: Jinil Sung Date: Fri, 22 Nov 2024 12:50:06 -0800 Subject: [PATCH 2/4] Clean up comments. Clean up comments. --- .../main/java/ca/bc/gov/educ/api/graduation/util/JwtUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index bb67ed28..9e813cb7 100644 --- 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 @@ -58,7 +58,7 @@ public static String getName(Jwt jwt) { /** * Gets name string - * => If it is service account, get it from request header. Otherwise, get username from jwt token + * => If it is service account, get it from request header. Otherwise, get username from jwt * * @param jwt the JWT * @param request the Request Header From a708b8a7bdaa9a021e12f7d27cc04e249af0db72 Mon Sep 17 00:00:00 2001 From: githubmamatha <106563495+githubmamatha@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:40:12 -0800 Subject: [PATCH 3/4] ZAPscan version upgraded. updated dependabot github actions. (#553) --- .github/dependabot.yml | 4 ++++ .../workflows/build.from.developer.branch.deploy.to.dev.yml | 2 +- .github/workflows/build.from.main.branch.deploy.to.dev.yml | 2 +- .github/workflows/build.from.release.branch.deploy.to.dev.yml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0d28b300..85708f49 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,3 +10,7 @@ updates: schedule: interval: "daily" target-branch: "grad-release" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/build.from.developer.branch.deploy.to.dev.yml b/.github/workflows/build.from.developer.branch.deploy.to.dev.yml index 05df64c3..0b75d061 100644 --- a/.github/workflows/build.from.developer.branch.deploy.to.dev.yml +++ b/.github/workflows/build.from.developer.branch.deploy.to.dev.yml @@ -156,6 +156,6 @@ jobs: # now hit it with a zap scan - name: ZAP Scan - uses: zaproxy/action-api-scan@v0.7.0 + uses: zaproxy/action-api-scan@v0.9.0 with: target: 'https://${{ env.REPO_NAME }}-${{ env.OPENSHIFT_NAMESPACE }}.apps.silver.devops.gov.bc.ca/api/v1/api-docs' diff --git a/.github/workflows/build.from.main.branch.deploy.to.dev.yml b/.github/workflows/build.from.main.branch.deploy.to.dev.yml index 9ecfcaeb..cae13f81 100644 --- a/.github/workflows/build.from.main.branch.deploy.to.dev.yml +++ b/.github/workflows/build.from.main.branch.deploy.to.dev.yml @@ -138,6 +138,6 @@ jobs: # now hit it with a zap scan - name: ZAP Scan - uses: zaproxy/action-api-scan@v0.7.0 + uses: zaproxy/action-api-scan@v0.9.0 with: target: 'https://${{ env.REPO_NAME }}-${{ env.OPENSHIFT_NAMESPACE }}.apps.silver.devops.gov.bc.ca/api/v1/api-docs' diff --git a/.github/workflows/build.from.release.branch.deploy.to.dev.yml b/.github/workflows/build.from.release.branch.deploy.to.dev.yml index 0601a570..c0e6176f 100644 --- a/.github/workflows/build.from.release.branch.deploy.to.dev.yml +++ b/.github/workflows/build.from.release.branch.deploy.to.dev.yml @@ -145,6 +145,6 @@ jobs: # now hit it with a zap scan - name: ZAP Scan - uses: zaproxy/action-api-scan@v0.7.0 + uses: zaproxy/action-api-scan@v0.9.0 with: target: 'https://${{ env.REPO_NAME }}-${{ env.OPENSHIFT_NAMESPACE }}.apps.silver.devops.gov.bc.ca/api/v1/api-docs' \ No newline at end of file From 03e032ea54fe73099aa1d78486118a54917d5063 Mon Sep 17 00:00:00 2001 From: githubmamatha <106563495+githubmamatha@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:14:40 -0800 Subject: [PATCH 4/4] Update pom.xml --- api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/pom.xml b/api/pom.xml index fb7aa84d..8925768b 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -7,7 +7,7 @@ ca.bc.gov.educ educ-grad-graduation-api - 1.8.65 + 1.8.66 educ-grad-graduation-api Ministry of Education GRAD GRADUATION API