From 3a783d554674d85a9853b4aa80fd706e2b22d5f5 Mon Sep 17 00:00:00 2001 From: clean-coder Date: Fri, 14 Jun 2024 06:41:39 +0200 Subject: [PATCH] #943: helper classes for getting tenant from Token/ClaimSet using tenant/iss claim --- .../okr/security/helper/ClaimHelper.java | 56 +++++++++++++++++++ .../okr/security/helper/JwtStatusLogger.java | 31 ++++++++++ .../okr/security/helper/TokenHelper.java | 44 +++++++++++++++ .../puzzle/okr/security/helper/UrlHelper.java | 9 +++ 4 files changed, 140 insertions(+) create mode 100644 backend/src/main/java/ch/puzzle/okr/security/helper/ClaimHelper.java create mode 100644 backend/src/main/java/ch/puzzle/okr/security/helper/JwtStatusLogger.java create mode 100644 backend/src/main/java/ch/puzzle/okr/security/helper/TokenHelper.java create mode 100644 backend/src/main/java/ch/puzzle/okr/security/helper/UrlHelper.java diff --git a/backend/src/main/java/ch/puzzle/okr/security/helper/ClaimHelper.java b/backend/src/main/java/ch/puzzle/okr/security/helper/ClaimHelper.java new file mode 100644 index 0000000000..01a1f44608 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/okr/security/helper/ClaimHelper.java @@ -0,0 +1,56 @@ +package ch.puzzle.okr.security.helper; + +import com.nimbusds.jwt.JWTClaimsSet; + +import java.text.ParseException; +import java.util.Optional; + +import static ch.puzzle.okr.security.JwtHelper.CLAIM_ISS; +import static ch.puzzle.okr.security.JwtHelper.CLAIM_TENANT; +import static ch.puzzle.okr.security.helper.JwtStatusLogger.logStatus; +import static ch.puzzle.okr.security.helper.UrlHelper.extractTenantFromIssUrl; + +public class ClaimHelper { + + public Optional getTenantFromClaimsSetUsingClaimTenant(JWTClaimsSet claimSet) { + try { + String tenant = getTenant(claimSet); + return Optional.ofNullable(tenant); + } catch (ParseException e) { + logStatus(CLAIM_TENANT, claimSet, e); + return Optional.empty(); + } + } + + private static String getTenant(JWTClaimsSet claimSet) throws ParseException { + String tenant = claimSet.getStringClaim(CLAIM_TENANT); + logStatus(CLAIM_TENANT, claimSet, tenant); + return tenant; + } + + public Optional getTenantFromClaimsSetUsingClaimIss(JWTClaimsSet claimSet) { + try { + String issUrl = getIssUrl(claimSet); + if (issUrl == null) { + return Optional.empty(); + } + return getTenant(claimSet, issUrl); + } catch (ParseException e) { + logStatus(CLAIM_ISS, claimSet, e); + return Optional.empty(); + } + } + + private static String getIssUrl(JWTClaimsSet claimSet) throws ParseException { + String issUrl = claimSet.getStringClaim(CLAIM_ISS); + logStatus(CLAIM_ISS, claimSet, issUrl); + return issUrl; + } + + private static Optional getTenant(JWTClaimsSet claimSet, String issUrl) { + String tenant = extractTenantFromIssUrl(issUrl); + logStatus(CLAIM_ISS, claimSet, tenant); + return Optional.ofNullable(tenant); + } + +} diff --git a/backend/src/main/java/ch/puzzle/okr/security/helper/JwtStatusLogger.java b/backend/src/main/java/ch/puzzle/okr/security/helper/JwtStatusLogger.java new file mode 100644 index 0000000000..280e295b95 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/okr/security/helper/JwtStatusLogger.java @@ -0,0 +1,31 @@ +package ch.puzzle.okr.security.helper; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.ParseException; + +public class JwtStatusLogger { + + private static final Logger logger = LoggerFactory.getLogger(ClaimHelper.class); + + public static void logStatus(String claim, Object context, String result) { + boolean isOk = result != null; + if (isOk) { + logger.info("Tenant: get claim '{}' from {}{}", claim, context.getClass().getSimpleName(), + statusToSymbol(isOk)); + } else { + logger.warn("Tenant: get claim '{}' from {}{}", claim, context.getClass().getSimpleName(), + statusToSymbol(isOk)); + } + } + + public static void logStatus(String claim, Object context, ParseException e) { + logger.warn("Tenant: get claim '{}' from {}{}", claim, context.getClass().getSimpleName(), + statusToSymbol(false), e); + } + + private static String statusToSymbol(boolean isOk) { + return isOk ? " | OK" : " | FAILED"; + } +} diff --git a/backend/src/main/java/ch/puzzle/okr/security/helper/TokenHelper.java b/backend/src/main/java/ch/puzzle/okr/security/helper/TokenHelper.java new file mode 100644 index 0000000000..ad1eaa4d89 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/okr/security/helper/TokenHelper.java @@ -0,0 +1,44 @@ +package ch.puzzle.okr.security.helper; + +import org.springframework.security.oauth2.jwt.Jwt; + +import java.util.Optional; + +import static ch.puzzle.okr.security.JwtHelper.CLAIM_ISS; +import static ch.puzzle.okr.security.JwtHelper.CLAIM_TENANT; +import static ch.puzzle.okr.security.helper.JwtStatusLogger.logStatus; +import static ch.puzzle.okr.security.helper.UrlHelper.extractTenantFromIssUrl; + +public class TokenHelper { + + public Optional getTenantFromTokenUsingClaimTenant(Jwt token) { + String tenant = getTenant(token); + return Optional.ofNullable(tenant); + } + + private static String getTenant(Jwt token) { + String tenant = token.getClaimAsString(CLAIM_TENANT); // can return null + logStatus(CLAIM_TENANT, token, tenant); + return tenant; + } + + public Optional getTenantFromTokenUsingClaimIss(Jwt token) { + String issUrl = getIssUrl(token); + if (issUrl == null) { + return Optional.empty(); + } + return getTenant(token, issUrl); + } + + private String getIssUrl(Jwt token) { + String issUrl = token.getClaimAsString(CLAIM_ISS); // can return null + logStatus(CLAIM_ISS, token, issUrl); + return issUrl; + } + + private Optional getTenant(Jwt token, String issUrl) { + String tenant = extractTenantFromIssUrl(issUrl); + logStatus(CLAIM_ISS, token, tenant); + return Optional.ofNullable(tenant); + } +} diff --git a/backend/src/main/java/ch/puzzle/okr/security/helper/UrlHelper.java b/backend/src/main/java/ch/puzzle/okr/security/helper/UrlHelper.java new file mode 100644 index 0000000000..b630b654d5 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/okr/security/helper/UrlHelper.java @@ -0,0 +1,9 @@ +package ch.puzzle.okr.security.helper; + +public class UrlHelper { + + public static String extractTenantFromIssUrl(String issUrl) { + String[] issUrlParts = issUrl.split("/"); + return issUrlParts[issUrlParts.length - 1]; + } +}