From c09022a45c11abd3b01b7452748148b0b2a0e034 Mon Sep 17 00:00:00 2001 From: AmaliMatharaarachchi Date: Fri, 8 Sep 2023 19:02:16 +0530 Subject: [PATCH 01/10] code format improvements --- .../org/wso2/apk/enforcer/api/APIFactory.java | 1 + .../security/jwt/validator/JWTValidator.java | 18 ++++-------------- .../org/wso2/apk/enforcer/util/JWTUtils.java | 15 ++++++--------- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java index 4713c70a7..bf609352b 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java @@ -89,6 +89,7 @@ public void addApis(List apis) { logger.debug("Total APIs in new cache: {}", newApis.size()); } this.apis = newApis; + //todo(amali) check if cache is initialized even if the cache is disabled CacheProviderUtil.initializeCacheHolder(newApis); } diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java index e7aa0bb6a..37ace5114 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java @@ -73,12 +73,7 @@ public JWTValidator(ExtendedTokenIssuerDto tokenIssuer) throws EnforcerException } } - - public JWTValidationInfo validateJWTToken(SignedJWTInfo signedJWTInfo) throws EnforcerException { - return validateToken(signedJWTInfo); - } - - private JWTValidationInfo validateToken(SignedJWTInfo signedJWTInfo) throws EnforcerException { + public JWTValidationInfo validateToken(SignedJWTInfo signedJWTInfo) throws EnforcerException { JWTValidationInfo jwtValidationInfo = new JWTValidationInfo(); boolean state; try { @@ -94,19 +89,14 @@ private JWTValidationInfo validateToken(SignedJWTInfo signedJWTInfo) throws Enfo jwtValidationInfo.setRawPayload(signedJWTInfo.getToken()); jwtValidationInfo.setKeyManager(tokenIssuer.getName()); return jwtValidationInfo; - } else { - jwtValidationInfo.setValid(false); - jwtValidationInfo.setValidationCode(APIConstants.KeyValidationStatus.API_AUTH_INVALID_CREDENTIALS); - return jwtValidationInfo; } - } else { - jwtValidationInfo.setValid(false); - jwtValidationInfo.setValidationCode(APIConstants.KeyValidationStatus.API_AUTH_INVALID_CREDENTIALS); - return jwtValidationInfo; } } catch (ParseException | JWTGeneratorException e) { throw new EnforcerException("Error while parsing JWT", e); } + jwtValidationInfo.setValid(false); + jwtValidationInfo.setValidationCode(APIConstants.KeyValidationStatus.API_AUTH_INVALID_CREDENTIALS); + return jwtValidationInfo; } protected boolean validateSignature(SignedJWT signedJWT) diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java index 5c4971322..91cf2945d 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java @@ -213,21 +213,18 @@ public static PrivateKey getPrivateKey(String filePath) throws EnforcerException * @throws ParseException if an error occurs when decoding the JWT */ public static SignedJWTInfo getSignedJwt(String accessToken,String organization) throws ParseException { - - String signature = accessToken.split("\\.")[2]; - SignedJWTInfo signedJWTInfo = null; + SignedJWTInfo signedJWTInfo; //Check whether GatewaySignedJWTParseCache is correct LoadingCache gatewaySignedJWTParseCache = CacheProviderUtil.getOrganizationCache(organization).getGatewaySignedJWTParseCache(); if (gatewaySignedJWTParseCache != null) { - Object cachedEntry = gatewaySignedJWTParseCache.getIfPresent(signature); + Object cachedEntry = gatewaySignedJWTParseCache.getIfPresent(accessToken); if (cachedEntry != null) { signedJWTInfo = (SignedJWTInfo) cachedEntry; - } - if (signedJWTInfo == null || !signedJWTInfo.getToken().equals(accessToken)) { + } else { SignedJWT signedJWT = SignedJWT.parse(accessToken); JWTClaimsSet jwtClaimsSet = signedJWT.getJWTClaimsSet(); signedJWTInfo = new SignedJWTInfo(accessToken, signedJWT, jwtClaimsSet); - gatewaySignedJWTParseCache.put(signature, signedJWTInfo); + gatewaySignedJWTParseCache.put(accessToken, signedJWTInfo); } } else { SignedJWT signedJWT = SignedJWT.parse(accessToken); @@ -266,13 +263,13 @@ public static void updateApplicationNameForSubscriptionDisabledKM(APIKeyValidati public static JWTValidationInfo validateJWTToken(SignedJWTInfo signedJWTInfo, String organization, String environment) throws EnforcerException { - JWTValidationInfo jwtValidationInfo = new JWTValidationInfo(); String issuer = signedJWTInfo.getJwtClaimsSet().getIssuer(); JWTValidator jwtValidator = SubscriptionDataStoreImpl.getInstance().getJWTValidatorByIssuer(issuer, organization, environment); if (jwtValidator != null) { - return jwtValidator.validateJWTToken(signedJWTInfo); + return jwtValidator.validateToken(signedJWTInfo); } + JWTValidationInfo jwtValidationInfo = new JWTValidationInfo(); jwtValidationInfo.setValid(false); jwtValidationInfo.setValidationCode(APIConstants.KeyValidationStatus.API_AUTH_INVALID_CREDENTIALS); log.info("No matching issuer found for the token with issuer : " + issuer); From 43b6861ea1b89ed97c3b8d710873cb149e79e434 Mon Sep 17 00:00:00 2001 From: AmaliMatharaarachchi Date: Tue, 12 Sep 2023 22:24:30 +0530 Subject: [PATCH 02/10] combine signedjwt and token caches --- .../commons/dto/JWTValidationInfo.java | 19 ++ .../security/jwt/JWTAuthenticator.java | 203 ++++++++---------- .../security/jwt/validator/JWTValidator.java | 16 +- .../org/wso2/apk/enforcer/util/JWTUtils.java | 66 +++--- 4 files changed, 155 insertions(+), 149 deletions(-) diff --git a/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/dto/JWTValidationInfo.java b/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/dto/JWTValidationInfo.java index 68adc9523..3c4994563 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/dto/JWTValidationInfo.java +++ b/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/dto/JWTValidationInfo.java @@ -18,6 +18,7 @@ package org.wso2.apk.enforcer.commons.dto; +import com.nimbusds.jwt.JWTClaimsSet; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; @@ -43,6 +44,8 @@ public class JWTValidationInfo implements Serializable { private String rawPayload; private String keyManager; private Boolean isAppToken; + private String identifier; + private JWTClaimsSet jwtClaimsSet; public JWTValidationInfo() { @@ -64,6 +67,22 @@ public JWTValidationInfo(JWTValidationInfo jwtValidationInfo) { this.isAppToken = jwtValidationInfo.getAppToken(); } + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public JWTClaimsSet getJwtClaimsSet() { + return jwtClaimsSet; + } + + public void setJwtClaimsSet(JWTClaimsSet jwtClaimsSet) { + this.jwtClaimsSet = jwtClaimsSet; + } + public Boolean getAppToken() { return isAppToken; diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java index 4954995fe..da8d58627 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java @@ -18,6 +18,7 @@ package org.wso2.apk.enforcer.security.jwt; import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; import com.nimbusds.jwt.util.DateUtils; import io.opentelemetry.context.Scope; import net.minidev.json.JSONArray; @@ -108,7 +109,6 @@ public boolean canAuthenticate(RequestContext requestContext) { public AuthenticationContext authenticate(RequestContext requestContext) throws APISecurityException { TracingTracer tracer = null; - TracingSpan decodeTokenHeaderSpan = null; TracingSpan jwtAuthenticatorInfoSpan = null; Scope jwtAuthenticatorInfoSpanScope = null; TracingSpan validateSubscriptionSpan = null; @@ -136,47 +136,13 @@ public AuthenticationContext authenticate(RequestContext requestContext) throws String organization = requestContext.getMatchedAPI().getOrganizationId(); String environment = requestContext.getMatchedAPI().getEnvironment(); context = context + "/" + version; - SignedJWTInfo signedJWTInfo; - Scope decodeTokenHeaderSpanScope = null; - try { - if (Utils.tracingEnabled()) { - decodeTokenHeaderSpan = Utils.startSpan(TracingConstants.DECODE_TOKEN_HEADER_SPAN, tracer); - decodeTokenHeaderSpanScope = decodeTokenHeaderSpan.getSpan().makeCurrent(); - Utils.setTag(decodeTokenHeaderSpan, APIConstants.LOG_TRACE_ID, - ThreadContext.get(APIConstants.LOG_TRACE_ID)); - } - signedJWTInfo = JWTUtils.getSignedJwt(jwtToken, organization); - } catch (ParseException | IllegalArgumentException e) { - log.error("Failed to decode the token header. {}", e.getMessage()); - throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), - APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, "Not a JWT token. Failed to decode the " + - "token header", e); - } finally { - if (Utils.tracingEnabled()) { - decodeTokenHeaderSpanScope.close(); - Utils.finishSpan(decodeTokenHeaderSpan); - } - } - JWTClaimsSet claims = signedJWTInfo.getJwtClaimsSet(); - String jwtTokenIdentifier = getJWTTokenIdentifier(signedJWTInfo); - - String jwtHeader = signedJWTInfo.getSignedJWT().getHeader().toString(); - if (StringUtils.isNotEmpty(jwtTokenIdentifier)) { - if (RevokedJWTDataHolder.isJWTTokenSignatureExistsInRevokedMap(jwtTokenIdentifier)) { - if (log.isDebugEnabled()) { - log.debug("Token retrieved from the revoked jwt token map. Token: " + FilterUtils.getMaskedToken(jwtHeader)); - } - log.debug("Invalid JWT token. " + FilterUtils.getMaskedToken(jwtHeader)); - throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), - APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, "Invalid JWT token"); - } - } - JWTValidationInfo validationInfo = getJwtValidationInfo(signedJWTInfo, jwtTokenIdentifier, organization, environment); + JWTValidationInfo validationInfo = getJwtValidationInfo(jwtToken, organization, environment); if (validationInfo != null) { if (validationInfo.isValid()) { + Map claims = validationInfo.getClaims(); // Validate token type - Object keyType = claims.getClaim("keytype"); + Object keyType = claims.get("keytype"); if (keyType != null && !keyType.toString().equalsIgnoreCase(requestContext.getMatchedAPI().getEnvType())) { throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, "Invalid key type."); @@ -196,7 +162,8 @@ public AuthenticationContext authenticate(RequestContext requestContext) throws ThreadContext.get(APIConstants.LOG_TRACE_ID)); } // if the token is self contained, validation subscription from `subscribedApis` claim - JSONObject api = validateSubscriptionFromClaim(name, version, claims, splitToken, envType + JSONObject api = validateSubscriptionFromClaim(name, version, + validationInfo.getJwtClaimsSet(), splitToken, envType , apiKeyValidationInfoDTO, true); if (api == null) { if (log.isDebugEnabled()) { @@ -264,7 +231,7 @@ public AuthenticationContext authenticate(RequestContext requestContext) throws ThreadContext.get(APIConstants.LOG_TRACE_ID)); } validateScopes(context, version, requestContext.getMatchedResourcePaths(), validationInfo, - signedJWTInfo); + jwtToken); } finally { if (Utils.tracingEnabled()) { validateScopesSpanScope.close(); @@ -293,14 +260,14 @@ public AuthenticationContext authenticate(RequestContext requestContext) throws // set custom claims get from the CR jwtInfoDto.setClaims(claimMap); endUserToken = BackendJwtUtils.generateAndRetrieveJWTToken(this.jwtGenerator, - jwtTokenIdentifier, jwtInfoDto, isGatewayTokenCacheEnabled, organization); + validationInfo.getIdentifier(), jwtInfoDto, isGatewayTokenCacheEnabled, organization); // Set generated jwt token as a response header // Change the backendJWTConfig to API level requestContext.addOrModifyHeaders(this.jwtGenerator.getJWTConfigurationDto().getJwtHeader(), endUserToken); } - return FilterUtils.generateAuthenticationContext(requestContext, jwtTokenIdentifier, + return FilterUtils.generateAuthenticationContext(requestContext, validationInfo.getIdentifier(), validationInfo, apiKeyValidationInfoDTO, endUserToken, jwtToken, true); } else { throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), @@ -358,7 +325,7 @@ private String retrieveAuthHeaderValue(RequestContext requestContext, * @throws APISecurityException in case of scope validation failure */ private void validateScopes(String apiContext, String apiVersion, ArrayList matchingResources, - JWTValidationInfo jwtValidationInfo, SignedJWTInfo jwtToken) throws APISecurityException { + JWTValidationInfo jwtValidationInfo, String jwtToken) throws APISecurityException { APIKeyValidationInfoDTO apiKeyValidationInfoDTO = new APIKeyValidationInfoDTO(); Set scopeSet = new HashSet<>(jwtValidationInfo.getScopes()); @@ -367,7 +334,7 @@ private void validateScopes(String apiContext, String apiVersion, ArrayList Date: Wed, 13 Sep 2023 14:49:29 +0530 Subject: [PATCH 03/10] add error codes for invalid tokens --- .../apk/enforcer/common/CacheProvider.java | 25 +++--------- .../security/jwt/JWTAuthenticator.java | 39 ++++++++----------- .../org/wso2/apk/enforcer/util/JWTUtils.java | 36 +---------------- 3 files changed, 23 insertions(+), 77 deletions(-) diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/common/CacheProvider.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/common/CacheProvider.java index 7c161e2c0..ee217886a 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/common/CacheProvider.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/common/CacheProvider.java @@ -24,7 +24,6 @@ import org.wso2.apk.enforcer.commons.dto.JWTValidationInfo; import org.wso2.apk.enforcer.config.ConfigHolder; import org.wso2.apk.enforcer.config.dto.CacheDto; -import org.wso2.apk.enforcer.security.jwt.SignedJWTInfo; import org.wso2.apk.enforcer.security.jwt.validator.JWTConstants; import java.util.concurrent.TimeUnit; @@ -33,10 +32,13 @@ * Class for initiating and returning caches. */ public class CacheProvider { - private LoadingCache gatewaySignedJWTParseCache; - private LoadingCache gatewayTokenCache; + // gatewayKeyCache contains valid tokens -> key: token, value: JWTValidationInfo private LoadingCache gatewayKeyCache; + // invalidTokenCache contains invalid tokens -> key: token, value: true private LoadingCache invalidTokenCache; + // gatewayJWTTokenCache contains backendJWT generated by gateway -> key: id, value: JWTValidationInfo + + //todo(amali) revisit apikey caches private LoadingCache gatewayJWTTokenCache; private LoadingCache getGatewayInternalKeyCache; private LoadingCache getInvalidGatewayInternalKeyCache; @@ -51,8 +53,6 @@ public void init() { cacheEnabled = cacheDto.isEnabled(); int maxSize = cacheDto.getMaximumSize(); int expiryTime = cacheDto.getExpiryTime(); - gatewaySignedJWTParseCache = initCache(maxSize, expiryTime); - gatewayTokenCache = initCache(maxSize, expiryTime); gatewayKeyCache = initCache(maxSize, expiryTime); invalidTokenCache = initCache(maxSize, expiryTime); gatewayJWTTokenCache = initCache(maxSize, expiryTime); @@ -99,21 +99,6 @@ public LoadingCache getInvalidGatewayInternalKeyCache() { return getInvalidGatewayInternalKeyCache; } - /** - * - * @return SignedJWT ParsedCache - */ - public LoadingCache getGatewaySignedJWTParseCache() { - return gatewaySignedJWTParseCache; - } - - /** - * @return gateway token cache - */ - public LoadingCache getGatewayTokenCache() { - return gatewayTokenCache; - } - /** * @return gateway key cache */ diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java index da8d58627..ef7e9decd 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java @@ -164,7 +164,7 @@ public AuthenticationContext authenticate(RequestContext requestContext) throws // if the token is self contained, validation subscription from `subscribedApis` claim JSONObject api = validateSubscriptionFromClaim(name, version, validationInfo.getJwtClaimsSet(), splitToken, envType - , apiKeyValidationInfoDTO, true); + , apiKeyValidationInfoDTO); if (api == null) { if (log.isDebugEnabled()) { log.debug("Begin subscription validation via Key Manager: " + validationInfo.getKeyManager()); @@ -243,10 +243,6 @@ public AuthenticationContext authenticate(RequestContext requestContext) throws // Generate or get backend JWT String endUserToken = null; - // change the config to api specific config - JWTConfigurationDto backendJwtConfig = - ConfigHolder.getInstance().getConfig().getJwtConfigurationDto(); - // jwt generator is only set if the backend jwt is enabled if (this.jwtGenerator != null) { JWTConfigurationDto configurationDto = this.jwtGenerator.getJWTConfigurationDto(); @@ -374,16 +370,16 @@ private APIKeyValidationInfoDTO validateSubscriptionUsingKeyManager(RequestConte * * @param name API name * @param version API version + * @param payload The payload of the JWT token * @param validationInfo token validation related details. this will be populated based on the available data * during the subscription validation. - * @param payload The payload of the JWT token * @return an JSON object containing subscribed API information retrieved from token payload. * If the subscription information is not found, return a null object. * @throws APISecurityException if the user is not subscribed to the API */ private JSONObject validateSubscriptionFromClaim(String name, String version, JWTClaimsSet payload, String[] splitToken, String envType, - APIKeyValidationInfoDTO validationInfo, boolean isOauth) throws APISecurityException { + APIKeyValidationInfoDTO validationInfo) throws APISecurityException { JSONObject api = null; try { @@ -454,12 +450,6 @@ private JSONObject validateSubscriptionFromClaim(String name, String version, JW if (log.isDebugEnabled()) { log.debug("No subscription information found in the token."); } - // we perform mandatory authentication for Api Keys - if (!isOauth) { - log.error("User is not subscribed to access the API."); - throw new APISecurityException(APIConstants.StatusCodes.UNAUTHORIZED.getCode(), - APISecurityConstants.API_AUTH_FORBIDDEN, APISecurityConstants.API_AUTH_FORBIDDEN_MESSAGE); - } } return api; } @@ -472,7 +462,7 @@ private JWTValidationInfo getJwtValidationInfo(String jwtToken, String organizat JWTValidationInfo validationInfo = (JWTValidationInfo) validCacheToken; if (!isJWTExpired(validationInfo)) { if (RevokedJWTDataHolder.isJWTTokenSignatureExistsInRevokedMap(validationInfo.getIdentifier())) { - log.debug("Token retrieved from the revoked jwt token map."); + log.debug("Token found in the revoked jwt token map."); throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, "Invalid JWT token"); } @@ -480,14 +470,20 @@ private JWTValidationInfo getJwtValidationInfo(String jwtToken, String organizat } else { CacheProviderUtil.getOrganizationCache(organization).getGatewayKeyCache().invalidate(jwtToken); CacheProviderUtil.getOrganizationCache(organization).getInvalidTokenCache().put(jwtToken, true); - return null; + throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), + APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, + APISecurityConstants.API_AUTH_INVALID_CREDENTIALS_MESSAGE); } } else if (CacheProviderUtil.getOrganizationCache(organization).getInvalidTokenCache() .getIfPresent(jwtToken) != null) { - return null; + throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), + APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, + APISecurityConstants.API_AUTH_INVALID_CREDENTIALS_MESSAGE); } } + SignedJWT signedJWT; + JWTClaimsSet jwtClaimsSet; SignedJWTInfo signedJWTInfo; Scope decodeTokenHeaderSpanScope = null; TracingSpan decodeTokenHeaderSpan = null; @@ -499,8 +495,8 @@ private JWTValidationInfo getJwtValidationInfo(String jwtToken, String organizat Utils.setTag(decodeTokenHeaderSpan, APIConstants.LOG_TRACE_ID, ThreadContext.get(APIConstants.LOG_TRACE_ID)); } - SignedJWT signedJWT = SignedJWT.parse(jwtToken); - JWTClaimsSet jwtClaimsSet = signedJWT.getJWTClaimsSet(); + signedJWT = SignedJWT.parse(jwtToken); + jwtClaimsSet = signedJWT.getJWTClaimsSet(); // todo(amali) create validationinfo directly signedJWTInfo = new SignedJWTInfo(jwtToken, signedJWT, jwtClaimsSet); } catch (ParseException | IllegalArgumentException e) { @@ -515,10 +511,11 @@ private JWTValidationInfo getJwtValidationInfo(String jwtToken, String organizat } } - String jwtTokenIdentifier = JWTUtils.getJWTTokenIdentifier(signedJWTInfo); + String jwtTokenIdentifier = StringUtils.isNotEmpty(jwtClaimsSet.getJWTID()) ? jwtClaimsSet.getJWTID() : + signedJWT.getSignature().toString(); // check whether the token is revoked - String jwtHeader = signedJWTInfo.getSignedJWT().getHeader().toString(); + String jwtHeader = signedJWT.getHeader().toString(); if (RevokedJWTDataHolder.isJWTTokenSignatureExistsInRevokedMap(jwtTokenIdentifier)) { log.debug("Token retrieved from the revoked jwt token map. Token: " + FilterUtils.getMaskedToken(jwtHeader)); @@ -552,9 +549,7 @@ private JWTValidationInfo getJwtValidationInfo(String jwtToken, String organizat * @return boolean true if the token is not expired, false otherwise */ private Boolean isJWTExpired(JWTValidationInfo payload) { - long timestampSkew = FilterUtils.getTimeStampSkewInSeconds(); - Date now = new Date(); Date exp = new Date(payload.getExpiryTime()); return !DateUtils.isAfter(exp, now, timestampSkew); diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java index 271e3c8ac..efb1af688 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java @@ -18,7 +18,6 @@ package org.wso2.apk.enforcer.util; -import com.google.common.cache.LoadingCache; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSVerifier; @@ -34,13 +33,11 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.wso2.apk.enforcer.common.CacheProviderUtil; import org.wso2.apk.enforcer.commons.dto.JWTValidationInfo; import org.wso2.apk.enforcer.commons.exception.EnforcerException; import org.wso2.apk.enforcer.config.ConfigHolder; import org.wso2.apk.enforcer.constants.APIConstants; import org.wso2.apk.enforcer.constants.Constants; -import org.wso2.apk.enforcer.constants.JwtConstants; import org.wso2.apk.enforcer.dto.APIKeyValidationInfoDTO; import org.wso2.apk.enforcer.security.jwt.SignedJWTInfo; import org.wso2.apk.enforcer.security.jwt.validator.JWTValidator; @@ -64,7 +61,6 @@ import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; -import java.text.ParseException; import java.util.Base64; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -206,36 +202,6 @@ public static PrivateKey getPrivateKey(String filePath) throws EnforcerException } } -// /** -// * Get the internal representation of the JWT. -// * -// * @param accessToken the raw access token -// * @return the internal representation of the JWT -// * @throws ParseException if an error occurs when decoding the JWT -// */ -// public static SignedJWTInfo getSignedJwt(String accessToken,String organization) throws ParseException { -// SignedJWTInfo signedJWTInfo; - //Check whether GatewaySignedJWTParseCache is correct -// LoadingCache gatewaySignedJWTParseCache = CacheProviderUtil.getOrganizationCache(organization).getGatewaySignedJWTParseCache(); -// if (gatewaySignedJWTParseCache != null) { -// Object cachedEntry = gatewaySignedJWTParseCache.getIfPresent(accessToken); -// if (cachedEntry != null) { -// signedJWTInfo = (SignedJWTInfo) cachedEntry; -// } else { -// SignedJWT signedJWT = SignedJWT.parse(accessToken); -// JWTClaimsSet jwtClaimsSet = signedJWT.getJWTClaimsSet(); -// signedJWTInfo = new SignedJWTInfo(accessToken, signedJWT, jwtClaimsSet); -// gatewaySignedJWTParseCache.put(accessToken, signedJWTInfo); -// } -// } else { -// SignedJWT signedJWT = SignedJWT.parse(accessToken); -// JWTClaimsSet jwtClaimsSet = signedJWT.getJWTClaimsSet(); -// signedJWTInfo = new SignedJWTInfo(accessToken, signedJWT, jwtClaimsSet); -//// } -// return signedJWTInfo; -// } - - /** * Check if the JWT token is expired. * @@ -244,7 +210,7 @@ public static PrivateKey getPrivateKey(String filePath) throws EnforcerException */ public static boolean isExpired(long exp) { long timestampSkew = FilterUtils.getTimeStampSkewInSeconds(); - return (exp-TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) < timestampSkew); + return (exp - TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) < timestampSkew); } /** From 4f8c73ceab990ad1875db3b804e6f81141a28883 Mon Sep 17 00:00:00 2001 From: AmaliMatharaarachchi Date: Thu, 14 Sep 2023 11:42:14 +0530 Subject: [PATCH 04/10] handle jwt from unknown issuers --- .../commons/dto/JWTValidationInfo.java | 59 ------------------- .../security/jwt/JWTAuthenticator.java | 24 +++++++- .../security/jwt/validator/JWTValidator.java | 6 -- .../org/wso2/apk/enforcer/util/JWTUtils.java | 16 ----- 4 files changed, 23 insertions(+), 82 deletions(-) diff --git a/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/dto/JWTValidationInfo.java b/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/dto/JWTValidationInfo.java index 3c4994563..426ba868a 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/dto/JWTValidationInfo.java +++ b/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/dto/JWTValidationInfo.java @@ -32,18 +32,13 @@ public class JWTValidationInfo implements Serializable { private static final long serialVersionUID = 1L; private String user; - private String issuer; private long expiryTime; - private long issuedTime; private String consumerKey; private boolean valid; private List scopes = new ArrayList<>(); private Map claims = new HashMap<>(); - private String jti; private int validationCode; - private String rawPayload; private String keyManager; - private Boolean isAppToken; private String identifier; private JWTClaimsSet jwtClaimsSet; @@ -54,17 +49,13 @@ public JWTValidationInfo() { public JWTValidationInfo(JWTValidationInfo jwtValidationInfo) { this.user = jwtValidationInfo.getUser(); - this.issuer = jwtValidationInfo.getIssuer(); this.expiryTime = jwtValidationInfo.getExpiryTime(); this.consumerKey = jwtValidationInfo.getConsumerKey(); this.valid = jwtValidationInfo.isValid(); this.scopes = jwtValidationInfo.getScopes(); this.claims = jwtValidationInfo.getClaims(); - this.jti = jwtValidationInfo.getJti(); this.validationCode = jwtValidationInfo.getValidationCode(); - this.rawPayload = jwtValidationInfo.getRawPayload(); this.keyManager = jwtValidationInfo.getKeyManager(); - this.isAppToken = jwtValidationInfo.getAppToken(); } public String getIdentifier() { @@ -83,16 +74,6 @@ public void setJwtClaimsSet(JWTClaimsSet jwtClaimsSet) { this.jwtClaimsSet = jwtClaimsSet; } - public Boolean getAppToken() { - - return isAppToken; - } - - public void setAppToken(Boolean appToken) { - - isAppToken = appToken; - } - public String getUser() { return user; @@ -103,16 +84,6 @@ public void setUser(String user) { this.user = user; } - public String getIssuer() { - - return issuer; - } - - public void setIssuer(String issuer) { - - this.issuer = issuer; - } - public long getExpiryTime() { return expiryTime; @@ -123,16 +94,6 @@ public void setExpiryTime(long expiryTime) { this.expiryTime = expiryTime; } - public long getIssuedTime() { - - return issuedTime; - } - - public void setIssuedTime(long issuedTime) { - - this.issuedTime = issuedTime; - } - public boolean isValid() { return valid; @@ -163,16 +124,6 @@ public void setClaims(Map claims) { this.claims = claims; } - public String getJti() { - - return jti; - } - - public void setJti(String jti) { - - this.jti = jti; - } - public String getConsumerKey() { return consumerKey; @@ -193,16 +144,6 @@ public void setValidationCode(int validationCode) { this.validationCode = validationCode; } - public String getRawPayload() { - - return rawPayload; - } - - public void setRawPayload(String rawPayload) { - - this.rawPayload = rawPayload; - } - public String getKeyManager() { return keyManager; diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java index ef7e9decd..1c8569448 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java @@ -49,7 +49,9 @@ import org.wso2.apk.enforcer.security.KeyValidator; import org.wso2.apk.enforcer.security.TokenValidationContext; import org.wso2.apk.enforcer.security.jwt.validator.JWTConstants; +import org.wso2.apk.enforcer.security.jwt.validator.JWTValidator; import org.wso2.apk.enforcer.security.jwt.validator.RevokedJWTDataHolder; +import org.wso2.apk.enforcer.subscription.SubscriptionDataStoreImpl; import org.wso2.apk.enforcer.tracing.TracingConstants; import org.wso2.apk.enforcer.tracing.TracingSpan; import org.wso2.apk.enforcer.tracing.TracingTracer; @@ -454,6 +456,15 @@ private JSONObject validateSubscriptionFromClaim(String name, String version, JW return api; } + /** + * Validate whether the token is a valid JWT and generate the JWTValidationInfo object. + * + * @param jwtToken The full JWT token + * @param organization organization of the API + * @param environment environment of the API + * @return + * @throws APISecurityException + */ private JWTValidationInfo getJwtValidationInfo(String jwtToken, String organization, String environment) throws APISecurityException { if (isGatewayTokenCacheEnabled) { Object validCacheToken = CacheProviderUtil.getOrganizationCache(organization).getGatewayKeyCache() @@ -524,7 +535,18 @@ private JWTValidationInfo getJwtValidationInfo(String jwtToken, String organizat } try { - JWTValidationInfo jwtValidationInfo = JWTUtils.validateJWTToken(signedJWTInfo, organization, environment); + // Get issuer + String issuer = jwtClaimsSet.getIssuer(); + JWTValidator jwtValidator = SubscriptionDataStoreImpl.getInstance().getJWTValidatorByIssuer(issuer, + organization, environment); + // If no validator found for the issuer, we are not caching the token. + if (jwtValidator == null) { + throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), + APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, + APISecurityConstants.API_AUTH_INVALID_CREDENTIALS_MESSAGE); + } + + JWTValidationInfo jwtValidationInfo = jwtValidator.validateToken(signedJWTInfo, organization, environment); if (isGatewayTokenCacheEnabled) { // Add token to tenant token cache if (jwtValidationInfo.isValid()) { diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java index 9b5d8909c..0b698b5e2 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java @@ -86,7 +86,6 @@ public JWTValidationInfo validateToken(SignedJWTInfo signedJWTInfo) throws Enfor jwtValidationInfo.setScopes(jwtTransformer.getTransformedScopes(jwtClaimsSet)); JWTClaimsSet transformedJWTClaimSet = jwtTransformer.transform(jwtClaimsSet); createJWTValidationInfoFromJWT(jwtValidationInfo, transformedJWTClaimSet); - jwtValidationInfo.setRawPayload(signedJWTInfo.getToken()); jwtValidationInfo.setKeyManager(tokenIssuer.getName()); jwtValidationInfo.setIdentifier(JWTUtils.getJWTTokenIdentifier(signedJWTInfo)); jwtValidationInfo.setJwtClaimsSet(signedJWTInfo.getJwtClaimsSet()); @@ -155,15 +154,10 @@ protected boolean validateTokenExpiry(JWTClaimsSet jwtClaimsSet) { private void createJWTValidationInfoFromJWT(JWTValidationInfo jwtValidationInfo, JWTClaimsSet jwtClaimsSet) throws ParseException { - jwtValidationInfo.setIssuer(jwtClaimsSet.getIssuer()); jwtValidationInfo.setValid(true); jwtValidationInfo.setClaims(jwtClaimsSet.getClaims()); jwtValidationInfo.setExpiryTime(jwtClaimsSet.getExpirationTime().getTime()); - if (jwtClaimsSet.getIssueTime() != null) { - jwtValidationInfo.setIssuedTime(jwtClaimsSet.getIssueTime().getTime()); - } jwtValidationInfo.setUser(jwtClaimsSet.getSubject()); - jwtValidationInfo.setJti(jwtClaimsSet.getJWTID()); if (jwtClaimsSet.getClaim(APIConstants.JwtTokenConstants.SCOPE) != null) { if (jwtClaimsSet.getClaim(APIConstants.JwtTokenConstants.SCOPE) instanceof List) { jwtValidationInfo.setScopes(jwtClaimsSet.getStringListClaim(APIConstants.JwtTokenConstants.SCOPE)); diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java index efb1af688..ef65763e6 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java @@ -229,22 +229,6 @@ public static void updateApplicationNameForSubscriptionDisabledKM(APIKeyValidati apiKeyValidationInfoDTO.setApplicationTier(APIConstants.UNLIMITED_TIER); } - public static JWTValidationInfo validateJWTToken(SignedJWTInfo signedJWTInfo, String organization, String environment) throws EnforcerException { - - String issuer = signedJWTInfo.getJwtClaimsSet().getIssuer(); - JWTValidator jwtValidator = SubscriptionDataStoreImpl.getInstance().getJWTValidatorByIssuer(issuer, - organization, environment); - if (jwtValidator != null) { - return jwtValidator.validateToken(signedJWTInfo); - } - JWTValidationInfo jwtValidationInfo = new JWTValidationInfo(); - jwtValidationInfo.setValid(false); - jwtValidationInfo.setValidationCode(APIConstants.KeyValidationStatus.API_AUTH_INVALID_CREDENTIALS); - log.info("No matching issuer found for the token with issuer : " + issuer); - return jwtValidationInfo; - - } - public static String getJWTTokenIdentifier(SignedJWTInfo signedJWTInfo) { JWTClaimsSet jwtClaimsSet = signedJWTInfo.getJwtClaimsSet(); From 74eeb9cd9e079dcb19cb0ee1c351c2e592e72e99 Mon Sep 17 00:00:00 2001 From: AmaliMatharaarachchi Date: Fri, 15 Sep 2023 15:29:32 +0530 Subject: [PATCH 05/10] fix test --- adapter/internal/oasparser/model/http_route.go | 2 +- test/cucumber-tests/src/test/resources/tests/api/JWT.feature | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/adapter/internal/oasparser/model/http_route.go b/adapter/internal/oasparser/model/http_route.go index 8c6fdf122..4fa02b0df 100644 --- a/adapter/internal/oasparser/model/http_route.go +++ b/adapter/internal/oasparser/model/http_route.go @@ -282,7 +282,7 @@ func (swagger *AdapterInternalAPI) SetInfoHTTPRouteCR(httpRoute *gwapiv1b1.HTTPR resourceAPIPolicy = concatAPIPolicies(resourceAPIPolicy, nil) resourceAuthScheme = concatAuthSchemes(resourceAuthScheme, nil) resourceRatelimitPolicy = concatRateLimitPolicies(resourceRatelimitPolicy, nil) - loggers.LoggerAPI.Error(resourceRatelimitPolicy) + addOperationLevelInterceptors(&policies, resourceAPIPolicy, resourceParams.InterceptorServiceMapping, resourceParams.BackendMapping, httpRoute.Namespace) loggers.LoggerOasparser.Debugf("Calculating auths for API ..., API_UUID = %v", swagger.UUID) diff --git a/test/cucumber-tests/src/test/resources/tests/api/JWT.feature b/test/cucumber-tests/src/test/resources/tests/api/JWT.feature index d002757e1..c95b2a8d0 100644 --- a/test/cucumber-tests/src/test/resources/tests/api/JWT.feature +++ b/test/cucumber-tests/src/test/resources/tests/api/JWT.feature @@ -34,9 +34,9 @@ Feature: Test JWT related functionalities Then I set headers |Authorization|bearer ${idp-1-token}| And I send "GET" request to "https://default.gw.wso2.com:9095/jwt-basic/3.14/employee/" with body "" - And I eventually receive 401 response code, not accepting + And I eventually receive 200 response code, not accepting |429| - |200| + |401| Then I generate JWT token from idp1 with kid "456-789" And I send "DELETE" request to "https://default.gw.wso2.com:9095/jwt-basic/3.14/employee/1234" with body "" And I eventually receive 200 response code, not accepting From bbb66abde10771b7f9417d35a4b71a27070cf7ff Mon Sep 17 00:00:00 2001 From: AmaliMatharaarachchi Date: Mon, 9 Oct 2023 10:58:26 +0530 Subject: [PATCH 06/10] improve formatting --- .../wso2/apk/enforcer/security/jwt/JWTAuthenticator.java | 1 - .../apk/enforcer/security/jwt/validator/JWTValidator.java | 6 ++++-- .../src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java | 3 --- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java index 1c8569448..eebec7d2d 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java @@ -40,7 +40,6 @@ import org.wso2.apk.enforcer.commons.model.JWTAuthenticationConfig; import org.wso2.apk.enforcer.commons.model.RequestContext; import org.wso2.apk.enforcer.commons.model.ResourceConfig; -import org.wso2.apk.enforcer.config.ConfigHolder; import org.wso2.apk.enforcer.constants.APIConstants; import org.wso2.apk.enforcer.constants.APISecurityConstants; import org.wso2.apk.enforcer.constants.GeneralErrorCodeConstants; diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java index 0b698b5e2..75cba5349 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java @@ -40,13 +40,15 @@ import org.wso2.apk.enforcer.util.JWKSClient; import org.wso2.apk.enforcer.util.JWTUtils; -import java.io.IOException; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; import java.text.ParseException; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; /** * Class responsible to validate jwt. This should validate the JWT signature, expiry time. diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java index ef65763e6..c9b26d8f9 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/JWTUtils.java @@ -33,15 +33,12 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.wso2.apk.enforcer.commons.dto.JWTValidationInfo; import org.wso2.apk.enforcer.commons.exception.EnforcerException; import org.wso2.apk.enforcer.config.ConfigHolder; import org.wso2.apk.enforcer.constants.APIConstants; import org.wso2.apk.enforcer.constants.Constants; import org.wso2.apk.enforcer.dto.APIKeyValidationInfoDTO; import org.wso2.apk.enforcer.security.jwt.SignedJWTInfo; -import org.wso2.apk.enforcer.security.jwt.validator.JWTValidator; -import org.wso2.apk.enforcer.subscription.SubscriptionDataStoreImpl; import java.io.IOException; import java.io.InputStream; From e55971ea82c140082227e5f86aafac799046ddb4 Mon Sep 17 00:00:00 2001 From: AmaliMatharaarachchi Date: Tue, 10 Oct 2023 15:51:28 +0530 Subject: [PATCH 07/10] token signature as key --- .../commons/dto/JWTValidationInfo.java | 9 +++++++ .../security/jwt/JWTAuthenticator.java | 26 +++++++++++++------ .../security/jwt/validator/JWTValidator.java | 3 ++- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/dto/JWTValidationInfo.java b/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/dto/JWTValidationInfo.java index 426ba868a..dc8259a97 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/dto/JWTValidationInfo.java +++ b/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/dto/JWTValidationInfo.java @@ -41,6 +41,7 @@ public class JWTValidationInfo implements Serializable { private String keyManager; private String identifier; private JWTClaimsSet jwtClaimsSet; + private String token; public JWTValidationInfo() { @@ -58,6 +59,14 @@ public JWTValidationInfo(JWTValidationInfo jwtValidationInfo) { this.keyManager = jwtValidationInfo.getKeyManager(); } + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + public String getIdentifier() { return identifier; } diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java index eebec7d2d..ab9cfce64 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java @@ -466,11 +466,19 @@ private JSONObject validateSubscriptionFromClaim(String name, String version, JW */ private JWTValidationInfo getJwtValidationInfo(String jwtToken, String organization, String environment) throws APISecurityException { if (isGatewayTokenCacheEnabled) { + String[] jwtParts = jwtToken.split("\\."); + String signature = jwtParts[2]; Object validCacheToken = CacheProviderUtil.getOrganizationCache(organization).getGatewayKeyCache() - .getIfPresent(jwtToken); + .getIfPresent(signature); if (validCacheToken != null) { JWTValidationInfo validationInfo = (JWTValidationInfo) validCacheToken; if (!isJWTExpired(validationInfo)) { + if (!StringUtils.equals(validationInfo.getToken(), jwtToken)) { + log.warn("Suspected tampered token; a JWT token with the same signature is " + + "already available in the cache. Tampered token: " + FilterUtils.getMaskedToken(jwtToken)); + throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), + APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, "Invalid JWT token"); + } if (RevokedJWTDataHolder.isJWTTokenSignatureExistsInRevokedMap(validationInfo.getIdentifier())) { log.debug("Token found in the revoked jwt token map."); throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), @@ -478,14 +486,14 @@ private JWTValidationInfo getJwtValidationInfo(String jwtToken, String organizat } return validationInfo; } else { - CacheProviderUtil.getOrganizationCache(organization).getGatewayKeyCache().invalidate(jwtToken); - CacheProviderUtil.getOrganizationCache(organization).getInvalidTokenCache().put(jwtToken, true); + CacheProviderUtil.getOrganizationCache(organization).getGatewayKeyCache().invalidate(signature); + CacheProviderUtil.getOrganizationCache(organization).getInvalidTokenCache().put(signature, true); throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, APISecurityConstants.API_AUTH_INVALID_CREDENTIALS_MESSAGE); } } else if (CacheProviderUtil.getOrganizationCache(organization).getInvalidTokenCache() - .getIfPresent(jwtToken) != null) { + .getIfPresent(signature) != null) { throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, APISecurityConstants.API_AUTH_INVALID_CREDENTIALS_MESSAGE); @@ -521,8 +529,9 @@ private JWTValidationInfo getJwtValidationInfo(String jwtToken, String organizat } } + String signature = signedJWT.getSignature().toString(); String jwtTokenIdentifier = StringUtils.isNotEmpty(jwtClaimsSet.getJWTID()) ? jwtClaimsSet.getJWTID() : - signedJWT.getSignature().toString(); + signature; // check whether the token is revoked String jwtHeader = signedJWT.getHeader().toString(); @@ -545,13 +554,14 @@ private JWTValidationInfo getJwtValidationInfo(String jwtToken, String organizat APISecurityConstants.API_AUTH_INVALID_CREDENTIALS_MESSAGE); } - JWTValidationInfo jwtValidationInfo = jwtValidator.validateToken(signedJWTInfo, organization, environment); + JWTValidationInfo jwtValidationInfo = jwtValidator.validateToken(jwtToken, signedJWTInfo); if (isGatewayTokenCacheEnabled) { // Add token to tenant token cache if (jwtValidationInfo.isValid()) { - CacheProviderUtil.getOrganizationCache(organization).getGatewayKeyCache().put(jwtToken, jwtValidationInfo); + CacheProviderUtil.getOrganizationCache(organization).getGatewayKeyCache().put(signature, + jwtValidationInfo); } else { - CacheProviderUtil.getOrganizationCache(organization).getInvalidTokenCache().put(jwtToken, true); + CacheProviderUtil.getOrganizationCache(organization).getInvalidTokenCache().put(signature, true); } } return jwtValidationInfo; diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java index 75cba5349..1813c9997 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java @@ -75,7 +75,7 @@ public JWTValidator(ExtendedTokenIssuerDto tokenIssuer) throws EnforcerException } } - public JWTValidationInfo validateToken(SignedJWTInfo signedJWTInfo) throws EnforcerException { + public JWTValidationInfo validateToken(String token, SignedJWTInfo signedJWTInfo) throws EnforcerException { JWTValidationInfo jwtValidationInfo = new JWTValidationInfo(); boolean state; try { @@ -91,6 +91,7 @@ public JWTValidationInfo validateToken(SignedJWTInfo signedJWTInfo) throws Enfor jwtValidationInfo.setKeyManager(tokenIssuer.getName()); jwtValidationInfo.setIdentifier(JWTUtils.getJWTTokenIdentifier(signedJWTInfo)); jwtValidationInfo.setJwtClaimsSet(signedJWTInfo.getJwtClaimsSet()); + jwtValidationInfo.setToken(token); return jwtValidationInfo; } logger.debug("Token is expired."); From 1f32d8f342faaf18213a35ac6d0e012d6aafd394 Mon Sep 17 00:00:00 2001 From: AmaliMatharaarachchi Date: Thu, 19 Oct 2023 23:50:30 +0530 Subject: [PATCH 08/10] add unit tests --- .../commons/model/AuthenticationContext.java | 56 -- .../org.wso2.apk.enforcer/build.gradle | 3 + .../security/AuthenticationContext.java | 263 --------- .../security/jwt/JWTAuthenticator.java | 4 +- .../security/jwt/validator/JWTValidator.java | 4 +- .../security/mtls/MTLSAuthenticator.java | 2 - .../wso2/apk/enforcer/util/FilterUtils.java | 4 - .../apk/enforcer/jwt/JWTValidatorTest.java | 546 ++++++++++++++++++ 8 files changed, 554 insertions(+), 328 deletions(-) delete mode 100644 gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/AuthenticationContext.java create mode 100644 gateway/enforcer/org.wso2.apk.enforcer/src/test/java/org/wso2/apk/enforcer/jwt/JWTValidatorTest.java diff --git a/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/model/AuthenticationContext.java b/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/model/AuthenticationContext.java index 7718f3598..067fcfeb0 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/model/AuthenticationContext.java +++ b/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/model/AuthenticationContext.java @@ -40,11 +40,7 @@ public class AuthenticationContext { private String subscriberTenantDomain; private String spikeArrestUnit; private boolean stopOnQuotaReach; - private String productName; - private String productProvider; - private String apiName; private String apiPublisher; - private String apiVersion; private String apiUUID; private String rawToken; private String tokenType; @@ -267,45 +263,6 @@ public void setStopOnQuotaReach(boolean stopOnQuotaReach) { this.stopOnQuotaReach = stopOnQuotaReach; } - public void setProductName(String productName) { - this.productName = productName; - } - - /** - * Matched API Product Name (If the request is from API Product) - * - * @return API Product Name - */ - public String getProductName() { - return productName; - } - - public void setProductProvider(String productProvider) { - this.productProvider = productProvider; - } - - /** - * API Product Provider of the matched API. - * - * @return API Product provider. - */ - public String getProductProvider() { - return productProvider; - } - - /** - * API Name of the matched API. - * - * @return API Name - */ - public String getApiName() { - return apiName; - } - - public void setApiName(String apiName) { - this.apiName = apiName; - } - /** * API Publisher of the matched API. * @@ -319,19 +276,6 @@ public void setApiPublisher(String apiPublisher) { this.apiPublisher = apiPublisher; } - /** - * API Version of the matched API - * - * @return API Version - */ - public String getApiVersion() { - return apiVersion; - } - - public void setApiVersion(String apiVersion) { - this.apiVersion = apiVersion; - } - /** * API UUID of the corresponding API. * diff --git a/gateway/enforcer/org.wso2.apk.enforcer/build.gradle b/gateway/enforcer/org.wso2.apk.enforcer/build.gradle index 6b15aaa25..ae2b7dafc 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/build.gradle +++ b/gateway/enforcer/org.wso2.apk.enforcer/build.gradle @@ -82,4 +82,7 @@ dependencies { implementation libs.protobuf.java // Test dependencites testImplementation libs.junit + testImplementation libs.mockito.core + testImplementation libs.powermock.api.mockito2 + testImplementation libs.powermock.module.junit4 } diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/AuthenticationContext.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/AuthenticationContext.java deleted file mode 100644 index 06b6e73a4..000000000 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/AuthenticationContext.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.wso2.apk.enforcer.security; - -import org.wso2.apk.enforcer.constants.Constants; - -import java.util.List; - -/** - * Contains some context information related to an authenticated request. This can be used - * to access API keys and tier information related to already authenticated requests. - */ -public class AuthenticationContext { - - private boolean authenticated; - private String username; - private String applicationTier; - private String tier; - private String apiTier; - private boolean isContentAwareTierPresent; - private String apiKey; - private String keyType; - private String callerToken; - private String applicationId; - private String applicationName; - private String consumerKey; - private String subscriber; - private List throttlingDataList; - private int spikeArrestLimit; - private String subscriberTenantDomain; - private String spikeArrestUnit; - private boolean stopOnQuotaReach; - private String productName; - private String productProvider; - private String apiName; - private String apiPublisher; - private String apiVersion; - private String apiUUID; - - public AuthenticationContext() { - this.applicationId = Constants.UNKNOWN_VALUE; - this.apiPublisher = Constants.UNKNOWN_VALUE; - this.apiTier = ""; - this.applicationId = Constants.UNKNOWN_VALUE; - this.applicationName = Constants.UNKNOWN_VALUE; - this.applicationTier = "Unlimited"; - this.consumerKey = Constants.UNKNOWN_VALUE; - this.spikeArrestUnit = ""; - this.subscriber = Constants.UNKNOWN_VALUE; - this.subscriberTenantDomain = Constants.UNKNOWN_VALUE; - } - - public List getThrottlingDataList() { - return throttlingDataList; - } - - public void setThrottlingDataList(List throttlingDataList) { - this.throttlingDataList = throttlingDataList; - } - //Following throttle data list can be use to hold throttle data and api level throttle key - //should be its first element. - - public boolean isContentAwareTierPresent() { - return isContentAwareTierPresent; - } - - public void setIsContentAware(boolean isContentAware) { - this.isContentAwareTierPresent = isContentAware; - } - - public String getApiTier() { - return apiTier; - } - - public void setApiTier(String apiTier) { - this.apiTier = apiTier; - } - - public String getSubscriber() { - return subscriber; - } - - public void setSubscriber(String subscriber) { - this.subscriber = subscriber; - } - - public boolean isAuthenticated() { - return authenticated; - } - - public String getUsername() { - return username; - } - - public String getTier() { - return tier; - } - - public String getApiKey() { - return apiKey; - } - - public void setApiKey(String apiKey) { - this.apiKey = apiKey; - } - - public void setAuthenticated(boolean authenticated) { - this.authenticated = authenticated; - } - - public void setUsername(String username) { - this.username = username; - } - - public void setTier(String tier) { - this.tier = tier; - } - - public String getKeyType() { - return keyType; - } - - public void setKeyType(String keyType) { - this.keyType = keyType; - } - - public String getCallerToken() { - return callerToken; - } - - public void setCallerToken(String callerToken) { - this.callerToken = callerToken; - } - - public String getApplicationTier() { - return applicationTier; - } - - public void setApplicationTier(String applicationTier) { - this.applicationTier = applicationTier; - } - - public String getApplicationId() { - return applicationId; - } - - public void setApplicationId(String applicationId) { - this.applicationId = applicationId; - } - - public String getApplicationName() { - return applicationName; - } - - public void setApplicationName(String applicationName) { - this.applicationName = applicationName; - } - - public String getConsumerKey() { - return consumerKey; - } - - public void setConsumerKey(String consumerKey) { - this.consumerKey = consumerKey; - } - - public int getSpikeArrestLimit() { - return spikeArrestLimit; - } - - public void setSpikeArrestLimit(int spikeArrestLimit) { - this.spikeArrestLimit = spikeArrestLimit; - } - - public String getSubscriberTenantDomain() { - return subscriberTenantDomain; - } - - public void setSubscriberTenantDomain(String subscriberTenantDomain) { - this.subscriberTenantDomain = subscriberTenantDomain; - } - - public String getSpikeArrestUnit() { - return spikeArrestUnit; - } - - public void setSpikeArrestUnit(String spikeArrestUnit) { - this.spikeArrestUnit = spikeArrestUnit; - } - - public boolean isStopOnQuotaReach() { - return stopOnQuotaReach; - } - - public void setStopOnQuotaReach(boolean stopOnQuotaReach) { - this.stopOnQuotaReach = stopOnQuotaReach; - } - - public void setProductName(String productName) { - this.productName = productName; - } - - public String getProductName() { - return productName; - } - - public void setProductProvider(String productProvider) { - this.productProvider = productProvider; - } - - public String getProductProvider() { - return productProvider; - } - - public String getApiName() { - return apiName; - } - - public void setApiName(String apiName) { - this.apiName = apiName; - } - - public String getApiPublisher() { - return apiPublisher; - } - - public void setApiPublisher(String apiPublisher) { - this.apiPublisher = apiPublisher; - } - - public String getApiVersion() { - return apiVersion; - } - - public void setApiVersion(String apiVersion) { - this.apiVersion = apiVersion; - } - - public String getApiUUID() { - return apiUUID; - } - - public void setApiUUID(String apiUUID) { - this.apiUUID = apiUUID; - } -} - diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java index ab9cfce64..8b2c836ef 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/JWTAuthenticator.java @@ -489,8 +489,8 @@ private JWTValidationInfo getJwtValidationInfo(String jwtToken, String organizat CacheProviderUtil.getOrganizationCache(organization).getGatewayKeyCache().invalidate(signature); CacheProviderUtil.getOrganizationCache(organization).getInvalidTokenCache().put(signature, true); throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), - APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, - APISecurityConstants.API_AUTH_INVALID_CREDENTIALS_MESSAGE); + APISecurityConstants.API_AUTH_ACCESS_TOKEN_EXPIRED, + APISecurityConstants.API_AUTH_ACCESS_TOKEN_EXPIRED_MESSAGE); } } else if (CacheProviderUtil.getOrganizationCache(organization).getInvalidTokenCache() .getIfPresent(signature) != null) { diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java index 1813c9997..66a1f3b4a 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/jwt/validator/JWTValidator.java @@ -36,6 +36,7 @@ import org.wso2.apk.enforcer.config.ConfigHolder; import org.wso2.apk.enforcer.config.dto.ExtendedTokenIssuerDto; import org.wso2.apk.enforcer.constants.APIConstants; +import org.wso2.apk.enforcer.constants.APISecurityConstants; import org.wso2.apk.enforcer.security.jwt.SignedJWTInfo; import org.wso2.apk.enforcer.util.JWKSClient; import org.wso2.apk.enforcer.util.JWTUtils; @@ -94,15 +95,16 @@ public JWTValidationInfo validateToken(String token, SignedJWTInfo signedJWTInfo jwtValidationInfo.setToken(token); return jwtValidationInfo; } + jwtValidationInfo.setValidationCode(APISecurityConstants.API_AUTH_ACCESS_TOKEN_EXPIRED); logger.debug("Token is expired."); } else { + jwtValidationInfo.setValidationCode(APIConstants.KeyValidationStatus.API_AUTH_INVALID_CREDENTIALS); logger.debug("Token signature is invalid."); } } catch (ParseException | JWTGeneratorException e) { throw new EnforcerException("Error while parsing JWT", e); } jwtValidationInfo.setValid(false); - jwtValidationInfo.setValidationCode(APIConstants.KeyValidationStatus.API_AUTH_INVALID_CREDENTIALS); return jwtValidationInfo; } diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/mtls/MTLSAuthenticator.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/mtls/MTLSAuthenticator.java index 17e940a62..01ab0324f 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/mtls/MTLSAuthenticator.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/security/mtls/MTLSAuthenticator.java @@ -122,9 +122,7 @@ public AuthenticationContext authenticate(RequestContext requestContext) throws String apiUUID = requestContext.getMatchedAPI().getUuid(); authenticationContext.setAuthenticated(authenticated); - authenticationContext.setApiName(apiName); authenticationContext.setApiUUID(apiUUID); - authenticationContext.setApiVersion(apiVersion); return authenticationContext; } finally { diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/FilterUtils.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/FilterUtils.java index 2cd818479..3f3421964 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/FilterUtils.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/util/FilterUtils.java @@ -251,7 +251,6 @@ public static AuthenticationContext generateAuthenticationContextForUnsecured(Re authContext.setApplicationName(null); authContext.setApplicationTier(APIConstants.UNLIMITED_TIER); authContext.setSubscriber(APIConstants.END_USER_ANONYMOUS); - authContext.setApiName(requestContext.getMatchedAPI().getName()); authContext.setStopOnQuotaReach(true); authContext.setConsumerKey(null); authContext.setCallerToken(null); @@ -281,8 +280,6 @@ public static AuthenticationContext generateAuthenticationContext(RequestContext authContext.setSubscriber(apiKeyValidationInfoDTO.getSubscriber()); authContext.setTier(apiKeyValidationInfoDTO.getTier()); authContext.setSubscriberTenantDomain(apiKeyValidationInfoDTO.getSubscriberTenantDomain()); - authContext.setApiName(apiKeyValidationInfoDTO.getApiName()); - authContext.setApiVersion(apiKeyValidationInfoDTO.getApiVersion()); authContext.setApiPublisher(apiKeyValidationInfoDTO.getApiPublisher()); authContext.setStopOnQuotaReach(apiKeyValidationInfoDTO.isStopOnQuotaReach()); authContext.setSpikeArrestLimit(apiKeyValidationInfoDTO.getSpikeArrestLimit()); @@ -363,7 +360,6 @@ public static AuthenticationContext generateAuthenticationContext(String tokenId if (api != null) { authContext.setTier(APIConstants.UNLIMITED_TIER); - authContext.setApiName(api.getAsString(APIConstants.JwtTokenConstants.API_NAME)); authContext.setApiPublisher(api.getAsString(APIConstants.JwtTokenConstants.API_PUBLISHER)); } diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/test/java/org/wso2/apk/enforcer/jwt/JWTValidatorTest.java b/gateway/enforcer/org.wso2.apk.enforcer/src/test/java/org/wso2/apk/enforcer/jwt/JWTValidatorTest.java new file mode 100644 index 000000000..480be50d2 --- /dev/null +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/test/java/org/wso2/apk/enforcer/jwt/JWTValidatorTest.java @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wso2.apk.enforcer.jwt; + +import com.google.common.cache.LoadingCache; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.wso2.apk.enforcer.common.CacheProvider; +import org.wso2.apk.enforcer.common.CacheProviderUtil; +import org.wso2.apk.enforcer.commons.dto.JWKSConfigurationDTO; +import org.wso2.apk.enforcer.commons.dto.JWTConfigurationDto; +import org.wso2.apk.enforcer.commons.dto.JWTValidationInfo; +import org.wso2.apk.enforcer.commons.exception.APISecurityException; +import org.wso2.apk.enforcer.commons.exception.EnforcerException; +import org.wso2.apk.enforcer.commons.jwtgenerator.AbstractAPIMgtGatewayJWTGenerator; +import org.wso2.apk.enforcer.commons.jwttransformer.DefaultJWTTransformer; +import org.wso2.apk.enforcer.commons.jwttransformer.JWTTransformer; +import org.wso2.apk.enforcer.commons.model.APIConfig; +import org.wso2.apk.enforcer.commons.model.AuthenticationConfig; +import org.wso2.apk.enforcer.commons.model.AuthenticationContext; +import org.wso2.apk.enforcer.commons.model.JWTAuthenticationConfig; +import org.wso2.apk.enforcer.commons.model.RequestContext; +import org.wso2.apk.enforcer.commons.model.ResourceConfig; +import org.wso2.apk.enforcer.config.ConfigHolder; +import org.wso2.apk.enforcer.config.EnforcerConfig; +import org.wso2.apk.enforcer.config.dto.ExtendedTokenIssuerDto; +import org.wso2.apk.enforcer.constants.APISecurityConstants; +import org.wso2.apk.enforcer.security.KeyValidator; +import org.wso2.apk.enforcer.security.jwt.JWTAuthenticator; +import org.wso2.apk.enforcer.security.jwt.validator.JWTValidator; +import org.wso2.apk.enforcer.subscription.SubscriptionDataStoreImpl; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({JWTValidator.class, LogManager.class, CacheProviderUtil.class, ConfigHolder.class, + SubscriptionDataStoreImpl.class, KeyValidator.class, Logger.class, JWTAuthenticator.class, + AbstractAPIMgtGatewayJWTGenerator.class, Log.class, LogFactory.class}) +public class JWTValidatorTest { + + @Before + public void setup() { + PowerMockito.mockStatic(CacheProviderUtil.class); + PowerMockito.mockStatic(ConfigHolder.class); + PowerMockito.mockStatic(SubscriptionDataStoreImpl.class); + PowerMockito.mockStatic(KeyValidator.class); + Logger logger = Mockito.mock(Logger.class); + Log logger2 = Mockito.mock(Log.class); + PowerMockito.mockStatic(LogManager.class); + PowerMockito.mockStatic(LogFactory.class); + Mockito.when(LogManager.getLogger(JWTAuthenticator.class)).thenReturn(logger); + Mockito.when(LogFactory.getLog(AbstractAPIMgtGatewayJWTGenerator.class)).thenReturn(logger2); + } + + @Test + public void testJWTValidator() throws APISecurityException, EnforcerException { + String organization = "org1"; + String issuer = "https://localhost:9443/oauth2/token"; + String signature = "sBgeoqJn0log5EZflj_G7ADvm6B3KQ9bdfFCEFVQS1U3oY9" + + "-cqPwAPyOLLh95pdfjYjakkf1UtjPZjeIupwXnzg0SffIc704RoVlZocAx9Ns2XihjU6Imx2MbXq9ARmQxQkyGVkJUMTwZ8" + + "-SfOnprfrhX2cMQQS8m2Lp7hcsvWFRGKxAKIeyUrbY4ihRIA5vOUrMBWYUx9Di1N7qdKA4S3e8O4KQX2VaZPBzN594c9TG" + + "riiH8AuuqnrftfvidSnlRLaFJmko8-QZo8jDepwacaFhtcaPVVJFG4uYP-_-N6sqfxLw3haazPN0_xU0T1zJLPRLC5HPfZMJDMGp" + + "EuSe9w"; + String jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5UZG1aak00WkRrM05qWTBZemM1T" + + "W1abU9EZ3dNVEUzTVdZd05ERTVNV1JsWkRnNE56YzRaQT09In0" + + ".eyJhdWQiOiJodHRwOlwvXC9vcmcud3NvMi5hcGltZ3RcL2dhdGV" + + "3YXkiLCJzdWIiOiJhZG1pbkBjYXJib24uc3VwZXIiLCJhcHBsaWNhdGlvbiI6eyJvd25lciI6ImFkbWluIiwidGllclF1b3RhVHlwZ" + + "SI6InJlcXVlc3RDb3VudCIsInRpZXIiOiJVbmxpbWl0ZWQiLCJuYW1lIjoiRGVmYXVsdEFwcGxpY2F0aW9uIiwiaWQiOjEsInV1aWQ" + + "iOm51bGx9LCJzY29wZSI6ImFtX2FwcGxpY2F0aW9uX3Njb3BlIGRlZmF1bHQiLCJpc3MiOiJodHRwczpcL1wvbG9jYWxob3N0Ojk0" + + "NDNcL29hdXRoMlwvdG9rZW4iLCJ0aWVySW5mbyI6e30sImtleXR5cGUiOiJQUk9EVUNUSU9OIiwic3Vic2NyaWJlZEFQSXMiOltdL" + + "CJjb25zdW1lcktleSI6IlhnTzM5NklIRks3ZUZZeWRycVFlNEhLR3oxa2EiLCJleHAiOjE1OTAzNDIzMTMsImlhdCI6MTU5MDMzO" + + "DcxMywianRpIjoiYjg5Mzg3NjgtMjNmZC00ZGVjLThiNzAtYmVkNDVlYjdjMzNkIn0." + signature; + JWTValidationInfo jwtValidationInfo = new JWTValidationInfo(); + jwtValidationInfo.setValid(true); + jwtValidationInfo.setExpiryTime(System.currentTimeMillis() + 5000L); + jwtValidationInfo.setConsumerKey(UUID.randomUUID().toString()); + jwtValidationInfo.setUser("user1"); + jwtValidationInfo.setKeyManager("Default"); + + JWTConfigurationDto jwtConfigurationDto = new JWTConfigurationDto(); + JWTAuthenticator jwtAuthenticator = new JWTAuthenticator(jwtConfigurationDto, true); + RequestContext requestContext = Mockito.mock(RequestContext.class); + + ArrayList resourceConfigs = new ArrayList<>(); + ResourceConfig resourceConfig = Mockito.mock(ResourceConfig.class); + AuthenticationConfig authenticationConfig = new AuthenticationConfig(); + JWTAuthenticationConfig jwtAuthenticationConfig = new JWTAuthenticationConfig(); + jwtAuthenticationConfig.setHeader("Authorization"); + authenticationConfig.setJwtAuthenticationConfig(jwtAuthenticationConfig); + Mockito.when(resourceConfig.getAuthenticationConfig()).thenReturn(authenticationConfig); + Mockito.when(resourceConfig.getMethod()).thenReturn(ResourceConfig.HttpMethods.GET); + resourceConfigs.add(resourceConfig); + Mockito.when(requestContext.getMatchedResourcePaths()).thenReturn(resourceConfigs); + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + jwt); + Mockito.when(requestContext.getHeaders()).thenReturn(headers); + Mockito.when(requestContext.getAuthenticationContext()).thenReturn(new AuthenticationContext()); + APIConfig apiConfig = Mockito.mock(APIConfig.class); + Mockito.when(apiConfig.getName()).thenReturn("api1"); + Mockito.when(apiConfig.getOrganizationId()).thenReturn(organization); + Mockito.when(requestContext.getMatchedAPI()).thenReturn(apiConfig); + CacheProvider cacheProvider = Mockito.mock(CacheProvider.class); + LoadingCache gatewayKeyCache = Mockito.mock(LoadingCache.class); + LoadingCache invalidTokenCache = Mockito.mock(LoadingCache.class); + Mockito.when(cacheProvider.getGatewayKeyCache()).thenReturn(gatewayKeyCache); + Mockito.when(cacheProvider.getInvalidTokenCache()).thenReturn(invalidTokenCache); + Mockito.when(CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); + + ExtendedTokenIssuerDto tokenIssuerDto = Mockito.mock(ExtendedTokenIssuerDto.class); + Mockito.when(tokenIssuerDto.getIssuer()).thenReturn(issuer); + JWKSConfigurationDTO jwksConfigurationDTO = new JWKSConfigurationDTO(); + jwksConfigurationDTO.setEnabled(true); + + Mockito.when(tokenIssuerDto.getJwksConfigurationDTO()).thenReturn(jwksConfigurationDTO); + + EnforcerConfig enforcerConfig = Mockito.mock(EnforcerConfig.class); + ConfigHolder configHolder = Mockito.mock(ConfigHolder.class); + Mockito.when(ConfigHolder.getInstance()).thenReturn(configHolder); + Mockito.when(configHolder.getConfig()).thenReturn(enforcerConfig); + JWTTransformer jwtTransformer = new DefaultJWTTransformer(); + Mockito.when(enforcerConfig.getJwtTransformer(issuer)).thenReturn(jwtTransformer); + JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); + + SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); + Mockito.when(SubscriptionDataStoreImpl.getInstance()).thenReturn(subscriptionDataStore); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, + organization)).thenReturn(jwtValidator); + Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); + + Mockito.when(KeyValidator.validateScopes(Mockito.any())).thenReturn(true); + AuthenticationContext authenticate = jwtAuthenticator.authenticate(requestContext); + + Assert.assertNotNull(authenticate); + // TODO(amali) enable after subscription validation is enabled +// Assert.assertEquals(authenticate.getApiPublisher(), "admin"); +// Assert.assertEquals(authenticate.getConsumerKey(), jwtValidationInfo.getConsumerKey()); + Mockito.verify(gatewayKeyCache, Mockito.atLeast(1)).put(signature, jwtValidationInfo); + } + + @Test + public void testCachedJWTValidator() throws APISecurityException, EnforcerException { + String organization = "org1"; + String issuer = "https://localhost:9443/oauth2/token"; + String signature = "sBgeoqJn0log5EZflj_G7ADvm6B3KQ9bdfF" + + "CEFVQS1U3oY9" + + "-cqPwAPyOLLh95pdfjYjakkf1UtjPZjeIupwXnzg0SffIc704RoVlZocAx9Ns2XihjU6Imx2MbXq9ARmQxQkyGVkJ" + + "UMTwZ8" + + "-SfOnprfrhX2cMQQS8m2Lp7hcsvWFRGKxAKIeyUrbY4ihRIA5vOUrMBWYUx9Di1N7qdKA4S3e8O4KQX2VaZPBzN594c9TG" + + "riiH8AuuqnrftfvidSnlRLaFJmko8-QZo8jDepwacaFhtcaPVVJFG4uYP-_" + + "-N6sqfxLw3haazPN0_xU0T1zJLPRLC5HPfZMJDMGp" + + "EuSe9w"; + String jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5UZG1aak00WkRrM05qWTBZemM1T" + + "W1abU9EZ3dNVEUzTVdZd05ERTVNV1JsWkRnNE56YzRaQT09In0" + + ".eyJhdWQiOiJodHRwOlwvXC9vcmcud3NvMi5hcGltZ3RcL2dhdGV" + + "3YXkiLCJzdWIiOiJhZG1pbkBjYXJib24uc3VwZXIiLCJhcHBsaWNhdGlvbiI6eyJvd25lciI6ImFkbWluIiwidGllclF1b3RhVHlwZ" + + "SI6InJlcXVlc3RDb3VudCIsInRpZXIiOiJVbmxpbWl0ZWQiLCJuYW1lIjoiRGVmYXVsdEFwcGxpY2F0aW9uIiwiaWQiOjEsInV1aWQ" + + "iOm51bGx9LCJzY29wZSI6ImFtX2FwcGxpY2F0aW9uX3Njb3BlIGRlZmF1bHQiLCJpc3MiOiJodHRwczpcL1wvbG9jYWxob3N0Ojk0" + + "NDNcL29hdXRoMlwvdG9rZW4iLCJ0aWVySW5mbyI6e30sImtleXR5cGUiOiJQUk9EVUNUSU9OIiwic3Vic2NyaWJlZEFQSXMiOltdL" + + "CJjb25zdW1lcktleSI6IlhnTzM5NklIRks3ZUZZeWRycVFlNEhLR3oxa2EiLCJleHAiOjE1OTAzNDIzMTMsImlhdCI6MTU5MDMzO" + + "DcxMywianRpIjoiYjg5Mzg3NjgtMjNmZC00ZGVjLThiNzAtYmVkNDVlYjdjMzNkIn0." + signature; + JWTValidationInfo jwtValidationInfo = new JWTValidationInfo(); + jwtValidationInfo.setValid(true); + jwtValidationInfo.setExpiryTime(System.currentTimeMillis() + 5000L); + jwtValidationInfo.setConsumerKey(UUID.randomUUID().toString()); + jwtValidationInfo.setUser("user1"); + jwtValidationInfo.setKeyManager("Default"); + jwtValidationInfo.setToken(jwt); + jwtValidationInfo.setIdentifier(signature); + + JWTConfigurationDto jwtConfigurationDto = new JWTConfigurationDto(); + JWTAuthenticator jwtAuthenticator = new JWTAuthenticator(jwtConfigurationDto, true); + RequestContext requestContext = Mockito.mock(RequestContext.class); + + ArrayList resourceConfigs = new ArrayList<>(); + ResourceConfig resourceConfig = Mockito.mock(ResourceConfig.class); + AuthenticationConfig authenticationConfig = new AuthenticationConfig(); + JWTAuthenticationConfig jwtAuthenticationConfig = new JWTAuthenticationConfig(); + jwtAuthenticationConfig.setHeader("Authorization"); + authenticationConfig.setJwtAuthenticationConfig(jwtAuthenticationConfig); + Mockito.when(resourceConfig.getAuthenticationConfig()).thenReturn(authenticationConfig); + Mockito.when(resourceConfig.getMethod()).thenReturn(ResourceConfig.HttpMethods.GET); + resourceConfigs.add(resourceConfig); + Mockito.when(requestContext.getMatchedResourcePaths()).thenReturn(resourceConfigs); + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + jwt); + Mockito.when(requestContext.getHeaders()).thenReturn(headers); + Mockito.when(requestContext.getAuthenticationContext()).thenReturn(new AuthenticationContext()); + APIConfig apiConfig = Mockito.mock(APIConfig.class); + Mockito.when(apiConfig.getName()).thenReturn("api1"); + Mockito.when(apiConfig.getOrganizationId()).thenReturn(organization); + Mockito.when(requestContext.getMatchedAPI()).thenReturn(apiConfig); + CacheProvider cacheProvider = Mockito.mock(CacheProvider.class); + LoadingCache gatewayKeyCache = Mockito.mock(LoadingCache.class); + Mockito.when(cacheProvider.getGatewayKeyCache()).thenReturn(gatewayKeyCache); + Mockito.when(gatewayKeyCache.getIfPresent(signature)).thenReturn(jwtValidationInfo); + Mockito.when(CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); + + JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); + SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); + Mockito.when(SubscriptionDataStoreImpl.getInstance()).thenReturn(subscriptionDataStore); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, + organization)).thenReturn(jwtValidator); + Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); + + Mockito.when(KeyValidator.validateScopes(Mockito.any())).thenReturn(true); + AuthenticationContext authenticate = jwtAuthenticator.authenticate(requestContext); + + Assert.assertNotNull(authenticate); + Assert.assertEquals(authenticate.getConsumerKey(), jwtValidationInfo.getConsumerKey()); + Mockito.verify(gatewayKeyCache, Mockito.atLeast(1)).getIfPresent(signature); + } + + @Test + public void testNonJTIJWTValidator() throws APISecurityException, EnforcerException { + String organization = "org1"; + String issuer = "https://localhost:9443/oauth2/token"; + String signature = "SSQyg_VTxF5drIogztn2SyEK2wRE07wG6OW3tufD3vo"; + String jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" + + ".eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdCIsImlhdCI6MTU5OTU0ODE3NCwiZXhwIjoxNjMxMDg0MTc0LC" + + "JhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiR2l2ZW5OYW1l" + + "IjoiSm9obm55IiwiU3VybmFtZSI6IlJvY2tldCIsIkVtYWlsIjoianJvY2tldEBleGFtcGxlLmNvbSIsIl" + + "JvbGUiOlsiTWFuYWdlciIsIlByb2plY3QgQWRtaW5pc3RyYXRvciJdfQ." + signature; + JWTValidationInfo jwtValidationInfo = new JWTValidationInfo(); + jwtValidationInfo.setValid(true); + jwtValidationInfo.setExpiryTime(System.currentTimeMillis() + 5000L); + jwtValidationInfo.setConsumerKey(UUID.randomUUID().toString()); + jwtValidationInfo.setUser("user1"); + jwtValidationInfo.setKeyManager("Default"); + jwtValidationInfo.setToken(jwt); + jwtValidationInfo.setIdentifier(signature); + + JWTConfigurationDto jwtConfigurationDto = new JWTConfigurationDto(); + JWTAuthenticator jwtAuthenticator = new JWTAuthenticator(jwtConfigurationDto, true); + RequestContext requestContext = Mockito.mock(RequestContext.class); + + ArrayList resourceConfigs = new ArrayList<>(); + ResourceConfig resourceConfig = Mockito.mock(ResourceConfig.class); + AuthenticationConfig authenticationConfig = new AuthenticationConfig(); + JWTAuthenticationConfig jwtAuthenticationConfig = new JWTAuthenticationConfig(); + jwtAuthenticationConfig.setHeader("Authorization"); + authenticationConfig.setJwtAuthenticationConfig(jwtAuthenticationConfig); + Mockito.when(resourceConfig.getAuthenticationConfig()).thenReturn(authenticationConfig); + Mockito.when(resourceConfig.getMethod()).thenReturn(ResourceConfig.HttpMethods.GET); + resourceConfigs.add(resourceConfig); + Mockito.when(requestContext.getMatchedResourcePaths()).thenReturn(resourceConfigs); + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + jwt); + Mockito.when(requestContext.getHeaders()).thenReturn(headers); + Mockito.when(requestContext.getAuthenticationContext()).thenReturn(new AuthenticationContext()); + APIConfig apiConfig = Mockito.mock(APIConfig.class); + Mockito.when(apiConfig.getName()).thenReturn("api1"); + Mockito.when(apiConfig.getOrganizationId()).thenReturn(organization); + Mockito.when(requestContext.getMatchedAPI()).thenReturn(apiConfig); + CacheProvider cacheProvider = Mockito.mock(CacheProvider.class); + LoadingCache gatewayKeyCache = Mockito.mock(LoadingCache.class); + Mockito.when(cacheProvider.getGatewayKeyCache()).thenReturn(gatewayKeyCache); + Mockito.when(gatewayKeyCache.getIfPresent(signature)).thenReturn(jwtValidationInfo); + Mockito.when(CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); + + JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); + SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); + Mockito.when(SubscriptionDataStoreImpl.getInstance()).thenReturn(subscriptionDataStore); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, + organization)).thenReturn(jwtValidator); + Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); + + Mockito.when(KeyValidator.validateScopes(Mockito.any())).thenReturn(true); + AuthenticationContext authenticate = jwtAuthenticator.authenticate(requestContext); + + Assert.assertNotNull(authenticate); + Assert.assertEquals(authenticate.getConsumerKey(), jwtValidationInfo.getConsumerKey()); + Mockito.verify(gatewayKeyCache, Mockito.atLeast(1)).getIfPresent(signature); + } + + @Test + public void testExpiredJWTValidator() { + String organization = "org1"; + String issuer = "https://localhost:9443/oauth2/token"; + String signature = "sBgeoqJn0log5EZflj_G7ADvm6B3KQ9bdfFCEFVQS1U3oY9" + + "-cqPwAPyOLLh95pdfjYjakkf1UtjPZjeIupwXnzg0SffIc704RoVlZocAx9Ns2XihjU6Imx2MbXq9ARmQxQkyGVkJUMTwZ8" + + "-SfOnprfrhX2cMQQS8m2Lp7hcsvWFRGKxAKIeyUrbY4ihRIA5vOUrMBWYUx9Di1N7qdKA4S3e8O4KQX2VaZPBzN594c9TG" + + "riiH8AuuqnrftfvidSnlRLaFJmko8-QZo8jDepwacaFhtcaPVVJFG4uYP-_-N6sqfxLw3haazPN0_xU0T1zJLPRLC5HPfZMJDMGp" + + "EuSe9w"; + String jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5UZG1aak00WkRrM05qWTBZemM1T" + + "W1abU9EZ3dNVEUzTVdZd05ERTVNV1JsWkRnNE56YzRaQT09In0" + + ".eyJhdWQiOiJodHRwOlwvXC9vcmcud3NvMi5hcGltZ3RcL2dhdGV" + + "3YXkiLCJzdWIiOiJhZG1pbkBjYXJib24uc3VwZXIiLCJhcHBsaWNhdGlvbiI6eyJvd25lciI6ImFkbWluIiwidGllclF1b3RhVHlwZ" + + "SI6InJlcXVlc3RDb3VudCIsInRpZXIiOiJVbmxpbWl0ZWQiLCJuYW1lIjoiRGVmYXVsdEFwcGxpY2F0aW9uIiwiaWQiOjEsInV1aWQ" + + "iOm51bGx9LCJzY29wZSI6ImFtX2FwcGxpY2F0aW9uX3Njb3BlIGRlZmF1bHQiLCJpc3MiOiJodHRwczpcL1wvbG9jYWxob3N0Ojk0" + + "NDNcL29hdXRoMlwvdG9rZW4iLCJ0aWVySW5mbyI6e30sImtleXR5cGUiOiJQUk9EVUNUSU9OIiwic3Vic2NyaWJlZEFQSXMiOltdL" + + "CJjb25zdW1lcktleSI6IlhnTzM5NklIRks3ZUZZeWRycVFlNEhLR3oxa2EiLCJleHAiOjE1OTAzNDIzMTMsImlhdCI6MTU5MDMzO" + + "DcxMywianRpIjoiYjg5Mzg3NjgtMjNmZC00ZGVjLThiNzAtYmVkNDVlYjdjMzNkIn0." + signature; + JWTValidationInfo jwtValidationInfo = new JWTValidationInfo(); + jwtValidationInfo.setValid(true); + jwtValidationInfo.setExpiryTime(System.currentTimeMillis() - 5000L); + jwtValidationInfo.setConsumerKey(UUID.randomUUID().toString()); + jwtValidationInfo.setUser("user1"); + jwtValidationInfo.setKeyManager("Default"); + jwtValidationInfo.setToken(jwt); + jwtValidationInfo.setIdentifier(signature); + + JWTConfigurationDto jwtConfigurationDto = new JWTConfigurationDto(); + JWTAuthenticator jwtAuthenticator = new JWTAuthenticator(jwtConfigurationDto, true); + RequestContext requestContext = Mockito.mock(RequestContext.class); + + ArrayList resourceConfigs = new ArrayList<>(); + ResourceConfig resourceConfig = Mockito.mock(ResourceConfig.class); + AuthenticationConfig authenticationConfig = new AuthenticationConfig(); + JWTAuthenticationConfig jwtAuthenticationConfig = new JWTAuthenticationConfig(); + jwtAuthenticationConfig.setHeader("Authorization"); + authenticationConfig.setJwtAuthenticationConfig(jwtAuthenticationConfig); + Mockito.when(resourceConfig.getAuthenticationConfig()).thenReturn(authenticationConfig); + Mockito.when(resourceConfig.getMethod()).thenReturn(ResourceConfig.HttpMethods.GET); + resourceConfigs.add(resourceConfig); + Mockito.when(requestContext.getMatchedResourcePaths()).thenReturn(resourceConfigs); + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + jwt); + Mockito.when(requestContext.getHeaders()).thenReturn(headers); + Mockito.when(requestContext.getAuthenticationContext()).thenReturn(new AuthenticationContext()); + APIConfig apiConfig = Mockito.mock(APIConfig.class); + Mockito.when(apiConfig.getName()).thenReturn("api1"); + Mockito.when(apiConfig.getOrganizationId()).thenReturn(organization); + Mockito.when(requestContext.getMatchedAPI()).thenReturn(apiConfig); + CacheProvider cacheProvider = Mockito.mock(CacheProvider.class); + LoadingCache gatewayKeyCache = Mockito.mock(LoadingCache.class); + LoadingCache invalidTokenCache = Mockito.mock(LoadingCache.class); + Mockito.when(cacheProvider.getGatewayKeyCache()).thenReturn(gatewayKeyCache); + Mockito.when(cacheProvider.getInvalidTokenCache()).thenReturn(invalidTokenCache); + Mockito.when(gatewayKeyCache.getIfPresent(signature)).thenReturn(jwtValidationInfo); + Mockito.when(invalidTokenCache.getIfPresent(signature)).thenReturn(null); + Mockito.when(CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); + + try { + Mockito.when(KeyValidator.validateScopes(Mockito.any())).thenReturn(true); + } catch (APISecurityException e) { + Assert.fail("Unexpected exception occurred while validating scopes"); + } + try { + jwtAuthenticator.authenticate(requestContext); + Assert.fail("Authentication should fail for expired tokens"); + } catch (APISecurityException e) { + Assert.assertEquals(e.getMessage(), e.getMessage(), APISecurityConstants.API_AUTH_ACCESS_TOKEN_EXPIRED_MESSAGE); + Mockito.verify(gatewayKeyCache, Mockito.atLeast(1)).getIfPresent(signature); + Mockito.verify(invalidTokenCache, Mockito.atLeast(1)).put(signature, true); + } + } + + @Test + public void testNoCacheExpiredJWTValidator() throws EnforcerException { + String organization = "org1"; + String issuer = "https://localhost:9443/oauth2/token"; + String signature = "sBgeoqJn0log5EZflj_G7ADvm6B3KQ9bdfFCEFVQS1U3oY9" + + "-cqPwAPyOLLh95pdfjYjakkf1UtjPZjeIupwXnzg0SffIc704RoVlZocAx9Ns2XihjU6Imx2MbXq9ARmQxQkyGVkJUMTwZ8" + + "-SfOnprfrhX2cMQQS8m2Lp7hcsvWFRGKxAKIeyUrbY4ihRIA5vOUrMBWYUx9Di1N7qdKA4S3e8O4KQX2VaZPBzN594c9TG" + + "riiH8AuuqnrftfvidSnlRLaFJmko8-QZo8jDepwacaFhtcaPVVJFG4uYP-_-N6sqfxLw3haazPN0_xU0T1zJLPRLC5HPfZMJDMGp" + + "EuSe9w"; + String jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5UZG1aak00WkRrM05qWTBZemM1T" + + "W1abU9EZ3dNVEUzTVdZd05ERTVNV1JsWkRnNE56YzRaQT09In0" + + ".eyJhdWQiOiJodHRwOlwvXC9vcmcud3NvMi5hcGltZ3RcL2dhdGV" + + "3YXkiLCJzdWIiOiJhZG1pbkBjYXJib24uc3VwZXIiLCJhcHBsaWNhdGlvbiI6eyJvd25lciI6ImFkbWluIiwidGllclF1b3RhVHlwZ" + + "SI6InJlcXVlc3RDb3VudCIsInRpZXIiOiJVbmxpbWl0ZWQiLCJuYW1lIjoiRGVmYXVsdEFwcGxpY2F0aW9uIiwiaWQiOjEsInV1aWQ" + + "iOm51bGx9LCJzY29wZSI6ImFtX2FwcGxpY2F0aW9uX3Njb3BlIGRlZmF1bHQiLCJpc3MiOiJodHRwczpcL1wvbG9jYWxob3N0Ojk0" + + "NDNcL29hdXRoMlwvdG9rZW4iLCJ0aWVySW5mbyI6e30sImtleXR5cGUiOiJQUk9EVUNUSU9OIiwic3Vic2NyaWJlZEFQSXMiOltdL" + + "CJjb25zdW1lcktleSI6IlhnTzM5NklIRks3ZUZZeWRycVFlNEhLR3oxa2EiLCJleHAiOjE1OTAzNDIzMTMsImlhdCI6MTU5MDMzO" + + "DcxMywianRpIjoiYjg5Mzg3NjgtMjNmZC00ZGVjLThiNzAtYmVkNDVlYjdjMzNkIn0." + signature; + JWTValidationInfo jwtValidationInfo = new JWTValidationInfo(); + jwtValidationInfo.setValid(false); + jwtValidationInfo.setExpiryTime(System.currentTimeMillis() - 100); + jwtValidationInfo.setConsumerKey(UUID.randomUUID().toString()); + jwtValidationInfo.setValidationCode(APISecurityConstants.API_AUTH_ACCESS_TOKEN_EXPIRED); + jwtValidationInfo.setUser("user1"); + jwtValidationInfo.setKeyManager("Default"); + jwtValidationInfo.setToken(jwt); + jwtValidationInfo.setIdentifier(signature); + + JWTConfigurationDto jwtConfigurationDto = new JWTConfigurationDto(); + JWTAuthenticator jwtAuthenticator = new JWTAuthenticator(jwtConfigurationDto, true); + RequestContext requestContext = Mockito.mock(RequestContext.class); + + ArrayList resourceConfigs = new ArrayList<>(); + ResourceConfig resourceConfig = Mockito.mock(ResourceConfig.class); + AuthenticationConfig authenticationConfig = new AuthenticationConfig(); + JWTAuthenticationConfig jwtAuthenticationConfig = new JWTAuthenticationConfig(); + jwtAuthenticationConfig.setHeader("Authorization"); + authenticationConfig.setJwtAuthenticationConfig(jwtAuthenticationConfig); + Mockito.when(resourceConfig.getAuthenticationConfig()).thenReturn(authenticationConfig); + Mockito.when(resourceConfig.getMethod()).thenReturn(ResourceConfig.HttpMethods.GET); + resourceConfigs.add(resourceConfig); + Mockito.when(requestContext.getMatchedResourcePaths()).thenReturn(resourceConfigs); + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + jwt); + Mockito.when(requestContext.getHeaders()).thenReturn(headers); + Mockito.when(requestContext.getAuthenticationContext()).thenReturn(new AuthenticationContext()); + APIConfig apiConfig = Mockito.mock(APIConfig.class); + Mockito.when(apiConfig.getName()).thenReturn("api1"); + Mockito.when(apiConfig.getOrganizationId()).thenReturn(organization); + Mockito.when(requestContext.getMatchedAPI()).thenReturn(apiConfig); + CacheProvider cacheProvider = Mockito.mock(CacheProvider.class); + LoadingCache gatewayKeyCache = Mockito.mock(LoadingCache.class); + LoadingCache invalidTokenCache = Mockito.mock(LoadingCache.class); + Mockito.when(cacheProvider.getGatewayKeyCache()).thenReturn(gatewayKeyCache); + Mockito.when(cacheProvider.getInvalidTokenCache()).thenReturn(invalidTokenCache); + Mockito.when(gatewayKeyCache.getIfPresent(signature)).thenReturn(null); + Mockito.when(invalidTokenCache.getIfPresent(signature)).thenReturn(null); + Mockito.when(CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); + + JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); + SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); + Mockito.when(SubscriptionDataStoreImpl.getInstance()).thenReturn(subscriptionDataStore); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, + organization)).thenReturn(jwtValidator); + Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); + + try { + Mockito.when(KeyValidator.validateScopes(Mockito.any())).thenReturn(true); + } catch (APISecurityException e) { + Assert.fail("Unexpected exception occurred while validating scopes"); + } + try { + jwtAuthenticator.authenticate(requestContext); + Assert.fail("Authentication should fail for expired tokens"); + } catch (APISecurityException e) { + Assert.assertEquals(e.getMessage(), APISecurityConstants.API_AUTH_ACCESS_TOKEN_EXPIRED_MESSAGE); + Mockito.verify(invalidTokenCache, Mockito.atLeast(1)).put(signature, true); + } + } + + @Test + public void testTamperedPayloadJWTValidator() throws EnforcerException { + String organization = "org1"; + String issuer = "https://localhost:9443/oauth2/token"; + String signature = "sBgeoqJn0log5EZflj_G7ADvm6B3KQ9bdfFCEFVQS1U3oY9" + + "-cqPwAPyOLLh95pdfjYjakkf1UtjPZjeIupwXnzg0SffIc704RoVlZocAx9Ns2XihjU6Imx2MbXq9ARmQxQkyGVkJUMTwZ8" + + "-SfOnprfrhX2cMQQS8m2Lp7hcsvWFRGKxAKIeyUrbY4ihRIA5vOUrMBWYUx9Di1N7qdKA4S3e8O4KQX2VaZPBzN594c9TG" + + "riiH8AuuqnrftfvidSnlRLaFJmko8-QZo8jDepwacaFhtcaPVVJFG4uYP-_-N6sqfxLw3haazPN0_xU0T1zJLPRLC5HPfZMJDMGp" + + "EuSe9w"; + String jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5UZG1aak00WkRrM05qWTBZemM1T" + + "W1abU9EZ3dNVEUzTVdZd05ERTVNV1JsWkRnNE56YzRaQT09In0" + + ".eyJhdWQiOiJodHRwOlwvXC9vcmcud3NvMi5hcGltZ3RcL2dhdGV" + + "3YXkiLCJzdWIiOiJhZG1pbkBjYXJib24uc3VwZXIiLCJhcHBsaWNhdGlvbiI6eyJvd25lciI6ImFkbWluIiwidGllclF1b3RhVHlwZ" + + "SI6InJlcXVlc3RDb3VudCIsInRpZXIiOiJVbmxpbWl0ZWQiLCJuYW1lIjoiRGVmYXVsdEFwcGxpY2F0aW9uIiwiaWQiOjEsInV1aWQ" + + "iOm51bGx9LCJzY29wZSI6ImFtX2FwcGxpY2F0aW9uX3Njb3BlIGRlZmF1bHQiLCJpc3MiOiJodHRwczpcL1wvbG9jYWxob3N0Ojk0" + + "NDNcL29hdXRoMlwvdG9rZW4iLCJ0aWVySW5mbyI6e30sImtleXR5cGUiOiJQUk9EVUNUSU9OIiwic3Vic2NyaWJlZEFQSXMiOltdL" + + "CJjb25zdW1lcktleSI6IlhnTzM5NklIRks3ZUZZeWRycVFlNEhLR3oxa2EiLCJleHAiOjE1OTAzNDIzMTMsImlhdCI6MTU5MDMzO" + + "DcxMywianRpIjoiYjg5Mzg3NjgtMjNmZC00ZGVjLThiNzAtYmVkNDVlYjdjMzNkIn0." + signature; + String tamperedJWT = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5UZG1aak00WkRrM05qWTBZemM1T" + + "W1abU9EZ3dNVEUzTVdZd05ERTVNV1JsWkRnNE56YzRaQT09In0" + + ".ewogICJhdWQiOiAiaHR0cDovL29yZy53c28yLmFwaW1ndC9nYXRld2F5IiwKICAic3ViIjogImFkbWluQGNhcm" + + "Jvbi5zdXBlciIsCiAgImFwcGxpY2F0aW9uIjogewogICAgIm93bmVyIjogImFkbWluIiwKICAgICJ0aWVyUXVvd" + + "GFUeXBlIjogInJlcXVlc3RDb3VudCIsCiAgICAidGllciI6ICJVbmxpbWl0ZWQiLAogICAgIm5hbWUiOiAiRGVm" + + "YXVsdEFwcGxpY2F0aW9uMiIsCiAgICAiaWQiOiAyLAogICAgInV1aWQiOiBudWxsCiAgfSwKICAic2NvcGUiOiA" + + "iYW1fYXBwbGljYXRpb25fc2NvcGUgZGVmYXVsdCIsCiAgImlzcyI6ICJodHRwczovL2xvY2FsaG9zdDo5NDQzL2" + + "9hdXRoMi90b2tlbiIsCiAgInRpZXJJbmZvIjoge30sCiAgImtleXR5cGUiOiAiUFJPRFVDVElPTiIsCiAgInN1Y" + + "nNjcmliZWRBUElzIjogW10sCiAgImNvbnN1bWVyS2V5IjogIlhnTzM5NklIRks3ZUZZeWRycVFlNEhLR3oxa2Ei" + + "LAogICJleHAiOiAxNTkwMzQyMzEzLAogICJpYXQiOiAxNTkwMzM4NzEzLAogICJqdGkiOiAiYjg5Mzg3NjgtMjN" + + "mZC00ZGVjLThiNzAtYmVkNDVlYjdjMzNkIgp9." + signature; + + JWTValidationInfo jwtValidationInfo = new JWTValidationInfo(); + jwtValidationInfo.setValid(false); + jwtValidationInfo.setExpiryTime(System.currentTimeMillis() - 100); + jwtValidationInfo.setConsumerKey(UUID.randomUUID().toString()); + jwtValidationInfo.setUser("user1"); + jwtValidationInfo.setKeyManager("Default"); + jwtValidationInfo.setToken(jwt); + jwtValidationInfo.setIdentifier(signature); + + JWTConfigurationDto jwtConfigurationDto = new JWTConfigurationDto(); + JWTAuthenticator jwtAuthenticator = new JWTAuthenticator(jwtConfigurationDto, true); + RequestContext requestContext = Mockito.mock(RequestContext.class); + + ArrayList resourceConfigs = new ArrayList<>(); + ResourceConfig resourceConfig = Mockito.mock(ResourceConfig.class); + AuthenticationConfig authenticationConfig = new AuthenticationConfig(); + JWTAuthenticationConfig jwtAuthenticationConfig = new JWTAuthenticationConfig(); + jwtAuthenticationConfig.setHeader("Authorization"); + authenticationConfig.setJwtAuthenticationConfig(jwtAuthenticationConfig); + Mockito.when(resourceConfig.getAuthenticationConfig()).thenReturn(authenticationConfig); + Mockito.when(resourceConfig.getMethod()).thenReturn(ResourceConfig.HttpMethods.GET); + resourceConfigs.add(resourceConfig); + Mockito.when(requestContext.getMatchedResourcePaths()).thenReturn(resourceConfigs); + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + tamperedJWT); + Mockito.when(requestContext.getHeaders()).thenReturn(headers); + Mockito.when(requestContext.getAuthenticationContext()).thenReturn(new AuthenticationContext()); + APIConfig apiConfig = Mockito.mock(APIConfig.class); + Mockito.when(apiConfig.getName()).thenReturn("api1"); + Mockito.when(apiConfig.getOrganizationId()).thenReturn(organization); + Mockito.when(requestContext.getMatchedAPI()).thenReturn(apiConfig); + CacheProvider cacheProvider = Mockito.mock(CacheProvider.class); + LoadingCache gatewayKeyCache = Mockito.mock(LoadingCache.class); + LoadingCache invalidTokenCache = Mockito.mock(LoadingCache.class); + Mockito.when(cacheProvider.getGatewayKeyCache()).thenReturn(gatewayKeyCache); + Mockito.when(cacheProvider.getInvalidTokenCache()).thenReturn(invalidTokenCache); + Mockito.when(gatewayKeyCache.getIfPresent(signature)).thenReturn(jwtValidationInfo); + Mockito.when(invalidTokenCache.getIfPresent(signature)).thenReturn(null); + Mockito.when(CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); + + JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); + SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); + Mockito.when(SubscriptionDataStoreImpl.getInstance()).thenReturn(subscriptionDataStore); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, + organization)).thenReturn(jwtValidator); + Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); + + try { + Mockito.when(KeyValidator.validateScopes(Mockito.any())).thenReturn(true); + } catch (APISecurityException e) { + Assert.fail("Unexpected exception occurred while validating scopes"); + } + try { + jwtAuthenticator.authenticate(requestContext); + Assert.fail("Authentication should fail for tampered tokens"); + } catch (APISecurityException e) { + Assert.assertEquals(e.getMessage(), "Invalid JWT token"); + Mockito.verify(invalidTokenCache, Mockito.never()).put(signature, true); + Mockito.verify(gatewayKeyCache, Mockito.never()).put(signature, true); + Mockito.verify(gatewayKeyCache, Mockito.never()).invalidate(signature); + } + } +} From 34c427bc340386b0f4f95f78530258ed93e2ac78 Mon Sep 17 00:00:00 2001 From: AmaliMatharaarachchi Date: Fri, 20 Oct 2023 19:20:13 +0530 Subject: [PATCH 09/10] Remove powermock due to java upgrade --- .../org.wso2.apk.enforcer/build.gradle | 10 +- .../apk/enforcer/jwt/JWTValidatorTest.java | 283 +++++++++--------- libs.versions.toml | 1 + 3 files changed, 143 insertions(+), 151 deletions(-) diff --git a/gateway/enforcer/org.wso2.apk.enforcer/build.gradle b/gateway/enforcer/org.wso2.apk.enforcer/build.gradle index ae2b7dafc..cc67d72b8 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/build.gradle +++ b/gateway/enforcer/org.wso2.apk.enforcer/build.gradle @@ -80,9 +80,9 @@ dependencies { // Added as direct dependency for transitive dependency version upgrades implementation libs.reactor.netty.http implementation libs.protobuf.java - // Test dependencites - testImplementation libs.junit - testImplementation libs.mockito.core - testImplementation libs.powermock.api.mockito2 - testImplementation libs.powermock.module.junit4 + + test { + implementation libs.junit + implementation libs.mockito.inline + } } diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/test/java/org/wso2/apk/enforcer/jwt/JWTValidatorTest.java b/gateway/enforcer/org.wso2.apk.enforcer/src/test/java/org/wso2/apk/enforcer/jwt/JWTValidatorTest.java index 480be50d2..2bc5fe075 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/test/java/org/wso2/apk/enforcer/jwt/JWTValidatorTest.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/test/java/org/wso2/apk/enforcer/jwt/JWTValidatorTest.java @@ -28,11 +28,8 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; +import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; import org.wso2.apk.enforcer.common.CacheProvider; import org.wso2.apk.enforcer.common.CacheProviderUtil; import org.wso2.apk.enforcer.commons.dto.JWKSConfigurationDTO; @@ -58,26 +55,8 @@ import org.wso2.apk.enforcer.security.jwt.validator.JWTValidator; import org.wso2.apk.enforcer.subscription.SubscriptionDataStoreImpl; -@RunWith(PowerMockRunner.class) -@PrepareForTest({JWTValidator.class, LogManager.class, CacheProviderUtil.class, ConfigHolder.class, - SubscriptionDataStoreImpl.class, KeyValidator.class, Logger.class, JWTAuthenticator.class, - AbstractAPIMgtGatewayJWTGenerator.class, Log.class, LogFactory.class}) public class JWTValidatorTest { - @Before - public void setup() { - PowerMockito.mockStatic(CacheProviderUtil.class); - PowerMockito.mockStatic(ConfigHolder.class); - PowerMockito.mockStatic(SubscriptionDataStoreImpl.class); - PowerMockito.mockStatic(KeyValidator.class); - Logger logger = Mockito.mock(Logger.class); - Log logger2 = Mockito.mock(Log.class); - PowerMockito.mockStatic(LogManager.class); - PowerMockito.mockStatic(LogFactory.class); - Mockito.when(LogManager.getLogger(JWTAuthenticator.class)).thenReturn(logger); - Mockito.when(LogFactory.getLog(AbstractAPIMgtGatewayJWTGenerator.class)).thenReturn(logger2); - } - @Test public void testJWTValidator() throws APISecurityException, EnforcerException { String organization = "org1"; @@ -130,37 +109,45 @@ public void testJWTValidator() throws APISecurityException, EnforcerException { LoadingCache invalidTokenCache = Mockito.mock(LoadingCache.class); Mockito.when(cacheProvider.getGatewayKeyCache()).thenReturn(gatewayKeyCache); Mockito.when(cacheProvider.getInvalidTokenCache()).thenReturn(invalidTokenCache); - Mockito.when(CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); - - ExtendedTokenIssuerDto tokenIssuerDto = Mockito.mock(ExtendedTokenIssuerDto.class); - Mockito.when(tokenIssuerDto.getIssuer()).thenReturn(issuer); - JWKSConfigurationDTO jwksConfigurationDTO = new JWKSConfigurationDTO(); - jwksConfigurationDTO.setEnabled(true); - - Mockito.when(tokenIssuerDto.getJwksConfigurationDTO()).thenReturn(jwksConfigurationDTO); - - EnforcerConfig enforcerConfig = Mockito.mock(EnforcerConfig.class); - ConfigHolder configHolder = Mockito.mock(ConfigHolder.class); - Mockito.when(ConfigHolder.getInstance()).thenReturn(configHolder); - Mockito.when(configHolder.getConfig()).thenReturn(enforcerConfig); - JWTTransformer jwtTransformer = new DefaultJWTTransformer(); - Mockito.when(enforcerConfig.getJwtTransformer(issuer)).thenReturn(jwtTransformer); - JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); - SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); - Mockito.when(SubscriptionDataStoreImpl.getInstance()).thenReturn(subscriptionDataStore); - Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, - organization)).thenReturn(jwtValidator); - Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); - - Mockito.when(KeyValidator.validateScopes(Mockito.any())).thenReturn(true); - AuthenticationContext authenticate = jwtAuthenticator.authenticate(requestContext); - - Assert.assertNotNull(authenticate); - // TODO(amali) enable after subscription validation is enabled -// Assert.assertEquals(authenticate.getApiPublisher(), "admin"); -// Assert.assertEquals(authenticate.getConsumerKey(), jwtValidationInfo.getConsumerKey()); - Mockito.verify(gatewayKeyCache, Mockito.atLeast(1)).put(signature, jwtValidationInfo); + try (MockedStatic logManagerDummy = Mockito.mockStatic(LogManager.class); + MockedStatic logFactoryDummy = Mockito.mockStatic(LogFactory.class); + MockedStatic cacheProviderUtilDummy = Mockito.mockStatic(CacheProviderUtil.class); + MockedStatic configHolderDummy = Mockito.mockStatic(ConfigHolder.class); + MockedStatic subscriptionDataStoreImplDummy = + Mockito.mockStatic(SubscriptionDataStoreImpl.class); + MockedStatic keyValidaterDummy = Mockito.mockStatic(KeyValidator.class)) { + Logger logger = Mockito.mock(Logger.class); + logManagerDummy.when(() -> LogManager.getLogger(JWTAuthenticator.class)).thenReturn(logger); + Log logger2 = Mockito.mock(Log.class); + logFactoryDummy.when(() -> LogFactory.getLog(AbstractAPIMgtGatewayJWTGenerator.class)).thenReturn(logger2); + //// + cacheProviderUtilDummy.when(() -> CacheProviderUtil.getOrganizationCache(organization)) + .thenReturn(cacheProvider); + ExtendedTokenIssuerDto tokenIssuerDto = Mockito.mock(ExtendedTokenIssuerDto.class); + Mockito.when(tokenIssuerDto.getIssuer()).thenReturn(issuer); + JWKSConfigurationDTO jwksConfigurationDTO = new JWKSConfigurationDTO(); + jwksConfigurationDTO.setEnabled(true); + Mockito.when(tokenIssuerDto.getJwksConfigurationDTO()).thenReturn(jwksConfigurationDTO); + + EnforcerConfig enforcerConfig = Mockito.mock(EnforcerConfig.class); + ConfigHolder configHolder = Mockito.mock(ConfigHolder.class); + configHolderDummy.when(ConfigHolder::getInstance).thenReturn(configHolder); + Mockito.when(configHolder.getConfig()).thenReturn(enforcerConfig); + JWTTransformer jwtTransformer = new DefaultJWTTransformer(); + Mockito.when(enforcerConfig.getJwtTransformer(issuer)).thenReturn(jwtTransformer); + JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); + + SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, + organization)).thenReturn(jwtValidator); + subscriptionDataStoreImplDummy.when(SubscriptionDataStoreImpl::getInstance).thenReturn(subscriptionDataStore); + Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); + keyValidaterDummy.when(()->KeyValidator.validateScopes(Mockito.any())).thenReturn(true); + AuthenticationContext authenticate = jwtAuthenticator.authenticate(requestContext); + Assert.assertNotNull(authenticate); + Mockito.verify(gatewayKeyCache, Mockito.atLeast(1)).put(signature, jwtValidationInfo); + } } @Test @@ -219,21 +206,23 @@ public void testCachedJWTValidator() throws APISecurityException, EnforcerExcept LoadingCache gatewayKeyCache = Mockito.mock(LoadingCache.class); Mockito.when(cacheProvider.getGatewayKeyCache()).thenReturn(gatewayKeyCache); Mockito.when(gatewayKeyCache.getIfPresent(signature)).thenReturn(jwtValidationInfo); - Mockito.when(CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); - - JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); - SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); - Mockito.when(SubscriptionDataStoreImpl.getInstance()).thenReturn(subscriptionDataStore); - Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, - organization)).thenReturn(jwtValidator); - Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); - - Mockito.when(KeyValidator.validateScopes(Mockito.any())).thenReturn(true); - AuthenticationContext authenticate = jwtAuthenticator.authenticate(requestContext); - - Assert.assertNotNull(authenticate); - Assert.assertEquals(authenticate.getConsumerKey(), jwtValidationInfo.getConsumerKey()); - Mockito.verify(gatewayKeyCache, Mockito.atLeast(1)).getIfPresent(signature); + try (MockedStatic cacheProviderUtilDummy = Mockito.mockStatic(CacheProviderUtil.class); + MockedStatic subscriptionDataStoreImplDummy = Mockito.mockStatic(SubscriptionDataStoreImpl.class); + MockedStatic keyValidatorDummy = Mockito.mockStatic(KeyValidator.class) + ) { + cacheProviderUtilDummy.when(() -> CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); + JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); + SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, + organization)).thenReturn(jwtValidator); + subscriptionDataStoreImplDummy.when(SubscriptionDataStoreImpl::getInstance).thenReturn(subscriptionDataStore); + Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); + keyValidatorDummy.when(() -> KeyValidator.validateScopes(Mockito.any())).thenReturn(true); + AuthenticationContext authenticate = jwtAuthenticator.authenticate(requestContext); + Assert.assertNotNull(authenticate); + Assert.assertEquals(authenticate.getConsumerKey(), jwtValidationInfo.getConsumerKey()); + Mockito.verify(gatewayKeyCache, Mockito.atLeast(1)).getIfPresent(signature); + } } @Test @@ -281,27 +270,27 @@ public void testNonJTIJWTValidator() throws APISecurityException, EnforcerExcept LoadingCache gatewayKeyCache = Mockito.mock(LoadingCache.class); Mockito.when(cacheProvider.getGatewayKeyCache()).thenReturn(gatewayKeyCache); Mockito.when(gatewayKeyCache.getIfPresent(signature)).thenReturn(jwtValidationInfo); - Mockito.when(CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); - - JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); - SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); - Mockito.when(SubscriptionDataStoreImpl.getInstance()).thenReturn(subscriptionDataStore); - Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, - organization)).thenReturn(jwtValidator); - Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); - - Mockito.when(KeyValidator.validateScopes(Mockito.any())).thenReturn(true); - AuthenticationContext authenticate = jwtAuthenticator.authenticate(requestContext); - - Assert.assertNotNull(authenticate); - Assert.assertEquals(authenticate.getConsumerKey(), jwtValidationInfo.getConsumerKey()); - Mockito.verify(gatewayKeyCache, Mockito.atLeast(1)).getIfPresent(signature); + try (MockedStatic cacheProviderUtilDummy = Mockito.mockStatic(CacheProviderUtil.class); + MockedStatic keyValidatorDummy = Mockito.mockStatic(KeyValidator.class); + MockedStatic subscriptionDataStoreImplDummy = Mockito.mockStatic(SubscriptionDataStoreImpl.class);) { + cacheProviderUtilDummy.when(() -> CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); + JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); + SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, + organization)).thenReturn(jwtValidator); + subscriptionDataStoreImplDummy.when(SubscriptionDataStoreImpl::getInstance).thenReturn(subscriptionDataStore); + Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); + keyValidatorDummy.when(() -> KeyValidator.validateScopes(Mockito.any())).thenReturn(true); + AuthenticationContext authenticate = jwtAuthenticator.authenticate(requestContext); + Assert.assertNotNull(authenticate); + Assert.assertEquals(authenticate.getConsumerKey(), jwtValidationInfo.getConsumerKey()); + Mockito.verify(gatewayKeyCache, Mockito.atLeast(1)).getIfPresent(signature); + } } @Test public void testExpiredJWTValidator() { String organization = "org1"; - String issuer = "https://localhost:9443/oauth2/token"; String signature = "sBgeoqJn0log5EZflj_G7ADvm6B3KQ9bdfFCEFVQS1U3oY9" + "-cqPwAPyOLLh95pdfjYjakkf1UtjPZjeIupwXnzg0SffIc704RoVlZocAx9Ns2XihjU6Imx2MbXq9ARmQxQkyGVkJUMTwZ8" + "-SfOnprfrhX2cMQQS8m2Lp7hcsvWFRGKxAKIeyUrbY4ihRIA5vOUrMBWYUx9Di1N7qdKA4S3e8O4KQX2VaZPBzN594c9TG" + @@ -354,20 +343,18 @@ public void testExpiredJWTValidator() { Mockito.when(cacheProvider.getInvalidTokenCache()).thenReturn(invalidTokenCache); Mockito.when(gatewayKeyCache.getIfPresent(signature)).thenReturn(jwtValidationInfo); Mockito.when(invalidTokenCache.getIfPresent(signature)).thenReturn(null); - Mockito.when(CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); - - try { - Mockito.when(KeyValidator.validateScopes(Mockito.any())).thenReturn(true); - } catch (APISecurityException e) { - Assert.fail("Unexpected exception occurred while validating scopes"); - } - try { - jwtAuthenticator.authenticate(requestContext); - Assert.fail("Authentication should fail for expired tokens"); - } catch (APISecurityException e) { - Assert.assertEquals(e.getMessage(), e.getMessage(), APISecurityConstants.API_AUTH_ACCESS_TOKEN_EXPIRED_MESSAGE); - Mockito.verify(gatewayKeyCache, Mockito.atLeast(1)).getIfPresent(signature); - Mockito.verify(invalidTokenCache, Mockito.atLeast(1)).put(signature, true); + try (MockedStatic cacheProviderUtilDummy = Mockito.mockStatic(CacheProviderUtil.class); + MockedStatic keyValidatorDummy = Mockito.mockStatic(KeyValidator.class)) { + cacheProviderUtilDummy.when(() -> CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); + keyValidatorDummy.when(() -> KeyValidator.validateScopes(Mockito.any())).thenReturn(true); + try { + jwtAuthenticator.authenticate(requestContext); + Assert.fail("Authentication should fail for expired tokens"); + } catch (APISecurityException e) { + Assert.assertEquals(e.getMessage(), e.getMessage(), APISecurityConstants.API_AUTH_ACCESS_TOKEN_EXPIRED_MESSAGE); + Mockito.verify(gatewayKeyCache, Mockito.atLeast(1)).getIfPresent(signature); + Mockito.verify(invalidTokenCache, Mockito.atLeast(1)).put(signature, true); + } } } @@ -428,27 +415,29 @@ public void testNoCacheExpiredJWTValidator() throws EnforcerException { Mockito.when(cacheProvider.getInvalidTokenCache()).thenReturn(invalidTokenCache); Mockito.when(gatewayKeyCache.getIfPresent(signature)).thenReturn(null); Mockito.when(invalidTokenCache.getIfPresent(signature)).thenReturn(null); - Mockito.when(CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); - - JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); - SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); - Mockito.when(SubscriptionDataStoreImpl.getInstance()).thenReturn(subscriptionDataStore); - Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, - organization)).thenReturn(jwtValidator); - Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); - - try { - Mockito.when(KeyValidator.validateScopes(Mockito.any())).thenReturn(true); - } catch (APISecurityException e) { - Assert.fail("Unexpected exception occurred while validating scopes"); - } - try { - jwtAuthenticator.authenticate(requestContext); - Assert.fail("Authentication should fail for expired tokens"); - } catch (APISecurityException e) { - Assert.assertEquals(e.getMessage(), APISecurityConstants.API_AUTH_ACCESS_TOKEN_EXPIRED_MESSAGE); - Mockito.verify(invalidTokenCache, Mockito.atLeast(1)).put(signature, true); + try (MockedStatic cacheProviderUtilDummy = Mockito.mockStatic(CacheProviderUtil.class); + MockedStatic subscriptionDataStoreImplDummy = + Mockito.mockStatic(SubscriptionDataStoreImpl.class); + MockedStatic keyValidatorDummy = Mockito.mockStatic(KeyValidator.class) + ) { + cacheProviderUtilDummy.when(() -> CacheProviderUtil.getOrganizationCache(organization)). + thenReturn(cacheProvider); + JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); + SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, organization)).thenReturn(jwtValidator); + subscriptionDataStoreImplDummy.when(SubscriptionDataStoreImpl::getInstance).thenReturn(subscriptionDataStore); + Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); + keyValidatorDummy.when(() -> KeyValidator.validateScopes(Mockito.any())).thenReturn(true); + + try { + jwtAuthenticator.authenticate(requestContext); + Assert.fail("Authentication should fail for expired tokens"); + } catch (APISecurityException e) { + Assert.assertEquals(e.getMessage(), APISecurityConstants.API_AUTH_ACCESS_TOKEN_EXPIRED_MESSAGE); + Mockito.verify(invalidTokenCache, Mockito.atLeast(1)).put(signature, true); + } } + } @Test @@ -512,35 +501,37 @@ public void testTamperedPayloadJWTValidator() throws EnforcerException { Mockito.when(apiConfig.getName()).thenReturn("api1"); Mockito.when(apiConfig.getOrganizationId()).thenReturn(organization); Mockito.when(requestContext.getMatchedAPI()).thenReturn(apiConfig); - CacheProvider cacheProvider = Mockito.mock(CacheProvider.class); - LoadingCache gatewayKeyCache = Mockito.mock(LoadingCache.class); - LoadingCache invalidTokenCache = Mockito.mock(LoadingCache.class); - Mockito.when(cacheProvider.getGatewayKeyCache()).thenReturn(gatewayKeyCache); - Mockito.when(cacheProvider.getInvalidTokenCache()).thenReturn(invalidTokenCache); - Mockito.when(gatewayKeyCache.getIfPresent(signature)).thenReturn(jwtValidationInfo); - Mockito.when(invalidTokenCache.getIfPresent(signature)).thenReturn(null); - Mockito.when(CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); - - JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); - SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); - Mockito.when(SubscriptionDataStoreImpl.getInstance()).thenReturn(subscriptionDataStore); - Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, - organization)).thenReturn(jwtValidator); - Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); - - try { - Mockito.when(KeyValidator.validateScopes(Mockito.any())).thenReturn(true); - } catch (APISecurityException e) { - Assert.fail("Unexpected exception occurred while validating scopes"); - } - try { - jwtAuthenticator.authenticate(requestContext); - Assert.fail("Authentication should fail for tampered tokens"); - } catch (APISecurityException e) { - Assert.assertEquals(e.getMessage(), "Invalid JWT token"); - Mockito.verify(invalidTokenCache, Mockito.never()).put(signature, true); - Mockito.verify(gatewayKeyCache, Mockito.never()).put(signature, true); - Mockito.verify(gatewayKeyCache, Mockito.never()).invalidate(signature); + try (MockedStatic cacheProviderUtilDummy = Mockito.mockStatic(CacheProviderUtil.class); + MockedStatic subscriptionDataStoreImplDummy = + Mockito.mockStatic(SubscriptionDataStoreImpl.class); + MockedStatic keyValidatorDummy = Mockito.mockStatic(KeyValidator.class) + ) { + CacheProvider cacheProvider = Mockito.mock(CacheProvider.class); + LoadingCache gatewayKeyCache = Mockito.mock(LoadingCache.class); + LoadingCache invalidTokenCache = Mockito.mock(LoadingCache.class); + Mockito.when(gatewayKeyCache.getIfPresent(signature)).thenReturn(jwtValidationInfo); + Mockito.when(invalidTokenCache.getIfPresent(signature)).thenReturn(null); + Mockito.when(cacheProvider.getGatewayKeyCache()).thenReturn(gatewayKeyCache); + Mockito.when(cacheProvider.getInvalidTokenCache()).thenReturn(invalidTokenCache); + cacheProviderUtilDummy.when(() -> CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); + + JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); + SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, + organization)).thenReturn(jwtValidator); + + subscriptionDataStoreImplDummy.when(SubscriptionDataStoreImpl::getInstance).thenReturn(subscriptionDataStore); + Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); + keyValidatorDummy.when(() -> KeyValidator.validateScopes(Mockito.any())).thenReturn(true); + try { + jwtAuthenticator.authenticate(requestContext); + Assert.fail("Authentication should fail for tampered tokens"); + } catch (APISecurityException e) { + Assert.assertEquals(e.getMessage(), "Invalid JWT token"); + Mockito.verify(invalidTokenCache, Mockito.never()).put(signature, true); + Mockito.verify(gatewayKeyCache, Mockito.never()).put(signature, true); + Mockito.verify(gatewayKeyCache, Mockito.never()).invalidate(signature); + } } } } diff --git a/libs.versions.toml b/libs.versions.toml index ab47f1cb0..8b03f403e 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -107,6 +107,7 @@ protobuf-java = {module = "com.google.protobuf:protobuf-java", version.ref = "pr hamcrest = {module = "org.hamcrest:hamcrest-all", version.ref = "hamcrest"} jacoco-agent = {module = "org.jacoco:org.jacoco.agent", version.ref = "jacoco"} mockito-core = {module = "org.mockito:mockito-core", version.ref = "mockito"} +mockito-inline = {module = "org.mockito:mockito-inline", version.ref = "mockito"} xml-apis = {module = "xml-apis:xml-apis", version.ref = "xml-apis"} mock-server-netty = {module = "org.mock-server:mockserver-netty", version.ref = "mock-server-netty"} mockito-inline = {module = "org.mockito:mockito-inline", version.ref = "mockito-inline"} From f5c075b45b819f6551d0e8cc2f381cda3210a0e4 Mon Sep 17 00:00:00 2001 From: AmaliMatharaarachchi Date: Mon, 30 Oct 2023 15:36:20 +0530 Subject: [PATCH 10/10] fix issues after rebase --- .../apk/enforcer/jwt/JWTValidatorTest.java | 30 ++++++++++++------- libs.versions.toml | 1 - 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/test/java/org/wso2/apk/enforcer/jwt/JWTValidatorTest.java b/gateway/enforcer/org.wso2.apk.enforcer/src/test/java/org/wso2/apk/enforcer/jwt/JWTValidatorTest.java index 2bc5fe075..5997b312f 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/test/java/org/wso2/apk/enforcer/jwt/JWTValidatorTest.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/test/java/org/wso2/apk/enforcer/jwt/JWTValidatorTest.java @@ -26,7 +26,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; @@ -60,6 +59,7 @@ public class JWTValidatorTest { @Test public void testJWTValidator() throws APISecurityException, EnforcerException { String organization = "org1"; + String environment = "development"; String issuer = "https://localhost:9443/oauth2/token"; String signature = "sBgeoqJn0log5EZflj_G7ADvm6B3KQ9bdfFCEFVQS1U3oY9" + "-cqPwAPyOLLh95pdfjYjakkf1UtjPZjeIupwXnzg0SffIc704RoVlZocAx9Ns2XihjU6Imx2MbXq9ARmQxQkyGVkJUMTwZ8" + @@ -102,6 +102,7 @@ public void testJWTValidator() throws APISecurityException, EnforcerException { Mockito.when(requestContext.getAuthenticationContext()).thenReturn(new AuthenticationContext()); APIConfig apiConfig = Mockito.mock(APIConfig.class); Mockito.when(apiConfig.getName()).thenReturn("api1"); + Mockito.when(apiConfig.getEnvironment()).thenReturn(environment); Mockito.when(apiConfig.getOrganizationId()).thenReturn(organization); Mockito.when(requestContext.getMatchedAPI()).thenReturn(apiConfig); CacheProvider cacheProvider = Mockito.mock(CacheProvider.class); @@ -121,7 +122,6 @@ public void testJWTValidator() throws APISecurityException, EnforcerException { logManagerDummy.when(() -> LogManager.getLogger(JWTAuthenticator.class)).thenReturn(logger); Log logger2 = Mockito.mock(Log.class); logFactoryDummy.when(() -> LogFactory.getLog(AbstractAPIMgtGatewayJWTGenerator.class)).thenReturn(logger2); - //// cacheProviderUtilDummy.when(() -> CacheProviderUtil.getOrganizationCache(organization)) .thenReturn(cacheProvider); ExtendedTokenIssuerDto tokenIssuerDto = Mockito.mock(ExtendedTokenIssuerDto.class); @@ -140,7 +140,7 @@ public void testJWTValidator() throws APISecurityException, EnforcerException { SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, - organization)).thenReturn(jwtValidator); + organization, environment)).thenReturn(jwtValidator); subscriptionDataStoreImplDummy.when(SubscriptionDataStoreImpl::getInstance).thenReturn(subscriptionDataStore); Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); keyValidaterDummy.when(()->KeyValidator.validateScopes(Mockito.any())).thenReturn(true); @@ -153,6 +153,7 @@ public void testJWTValidator() throws APISecurityException, EnforcerException { @Test public void testCachedJWTValidator() throws APISecurityException, EnforcerException { String organization = "org1"; + String environment = "development"; String issuer = "https://localhost:9443/oauth2/token"; String signature = "sBgeoqJn0log5EZflj_G7ADvm6B3KQ9bdfF" + "CEFVQS1U3oY9" + @@ -200,6 +201,7 @@ public void testCachedJWTValidator() throws APISecurityException, EnforcerExcept Mockito.when(requestContext.getAuthenticationContext()).thenReturn(new AuthenticationContext()); APIConfig apiConfig = Mockito.mock(APIConfig.class); Mockito.when(apiConfig.getName()).thenReturn("api1"); + Mockito.when(apiConfig.getEnvironment()).thenReturn(environment); Mockito.when(apiConfig.getOrganizationId()).thenReturn(organization); Mockito.when(requestContext.getMatchedAPI()).thenReturn(apiConfig); CacheProvider cacheProvider = Mockito.mock(CacheProvider.class); @@ -214,7 +216,7 @@ public void testCachedJWTValidator() throws APISecurityException, EnforcerExcept JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, - organization)).thenReturn(jwtValidator); + organization, environment)).thenReturn(jwtValidator); subscriptionDataStoreImplDummy.when(SubscriptionDataStoreImpl::getInstance).thenReturn(subscriptionDataStore); Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); keyValidatorDummy.when(() -> KeyValidator.validateScopes(Mockito.any())).thenReturn(true); @@ -228,6 +230,7 @@ public void testCachedJWTValidator() throws APISecurityException, EnforcerExcept @Test public void testNonJTIJWTValidator() throws APISecurityException, EnforcerException { String organization = "org1"; + String environment = "development"; String issuer = "https://localhost:9443/oauth2/token"; String signature = "SSQyg_VTxF5drIogztn2SyEK2wRE07wG6OW3tufD3vo"; String jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" + @@ -264,6 +267,7 @@ public void testNonJTIJWTValidator() throws APISecurityException, EnforcerExcept Mockito.when(requestContext.getAuthenticationContext()).thenReturn(new AuthenticationContext()); APIConfig apiConfig = Mockito.mock(APIConfig.class); Mockito.when(apiConfig.getName()).thenReturn("api1"); + Mockito.when(apiConfig.getEnvironment()).thenReturn(environment); Mockito.when(apiConfig.getOrganizationId()).thenReturn(organization); Mockito.when(requestContext.getMatchedAPI()).thenReturn(apiConfig); CacheProvider cacheProvider = Mockito.mock(CacheProvider.class); @@ -276,8 +280,8 @@ public void testNonJTIJWTValidator() throws APISecurityException, EnforcerExcept cacheProviderUtilDummy.when(() -> CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); - Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, - organization)).thenReturn(jwtValidator); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, organization, environment)) + .thenReturn(jwtValidator); subscriptionDataStoreImplDummy.when(SubscriptionDataStoreImpl::getInstance).thenReturn(subscriptionDataStore); Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); keyValidatorDummy.when(() -> KeyValidator.validateScopes(Mockito.any())).thenReturn(true); @@ -361,6 +365,7 @@ public void testExpiredJWTValidator() { @Test public void testNoCacheExpiredJWTValidator() throws EnforcerException { String organization = "org1"; + String environment = "development"; String issuer = "https://localhost:9443/oauth2/token"; String signature = "sBgeoqJn0log5EZflj_G7ADvm6B3KQ9bdfFCEFVQS1U3oY9" + "-cqPwAPyOLLh95pdfjYjakkf1UtjPZjeIupwXnzg0SffIc704RoVlZocAx9Ns2XihjU6Imx2MbXq9ARmQxQkyGVkJUMTwZ8" + @@ -406,6 +411,7 @@ public void testNoCacheExpiredJWTValidator() throws EnforcerException { Mockito.when(requestContext.getAuthenticationContext()).thenReturn(new AuthenticationContext()); APIConfig apiConfig = Mockito.mock(APIConfig.class); Mockito.when(apiConfig.getName()).thenReturn("api1"); + Mockito.when(apiConfig.getEnvironment()).thenReturn(environment); Mockito.when(apiConfig.getOrganizationId()).thenReturn(organization); Mockito.when(requestContext.getMatchedAPI()).thenReturn(apiConfig); CacheProvider cacheProvider = Mockito.mock(CacheProvider.class); @@ -424,7 +430,8 @@ public void testNoCacheExpiredJWTValidator() throws EnforcerException { thenReturn(cacheProvider); JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); - Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, organization)).thenReturn(jwtValidator); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, organization, environment)) + .thenReturn(jwtValidator); subscriptionDataStoreImplDummy.when(SubscriptionDataStoreImpl::getInstance).thenReturn(subscriptionDataStore); Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); keyValidatorDummy.when(() -> KeyValidator.validateScopes(Mockito.any())).thenReturn(true); @@ -443,6 +450,7 @@ public void testNoCacheExpiredJWTValidator() throws EnforcerException { @Test public void testTamperedPayloadJWTValidator() throws EnforcerException { String organization = "org1"; + String environment = "development"; String issuer = "https://localhost:9443/oauth2/token"; String signature = "sBgeoqJn0log5EZflj_G7ADvm6B3KQ9bdfFCEFVQS1U3oY9" + "-cqPwAPyOLLh95pdfjYjakkf1UtjPZjeIupwXnzg0SffIc704RoVlZocAx9Ns2XihjU6Imx2MbXq9ARmQxQkyGVkJUMTwZ8" + @@ -499,6 +507,7 @@ public void testTamperedPayloadJWTValidator() throws EnforcerException { Mockito.when(requestContext.getAuthenticationContext()).thenReturn(new AuthenticationContext()); APIConfig apiConfig = Mockito.mock(APIConfig.class); Mockito.when(apiConfig.getName()).thenReturn("api1"); + Mockito.when(apiConfig.getEnvironment()).thenReturn(environment); Mockito.when(apiConfig.getOrganizationId()).thenReturn(organization); Mockito.when(requestContext.getMatchedAPI()).thenReturn(apiConfig); try (MockedStatic cacheProviderUtilDummy = Mockito.mockStatic(CacheProviderUtil.class); @@ -513,12 +522,13 @@ public void testTamperedPayloadJWTValidator() throws EnforcerException { Mockito.when(invalidTokenCache.getIfPresent(signature)).thenReturn(null); Mockito.when(cacheProvider.getGatewayKeyCache()).thenReturn(gatewayKeyCache); Mockito.when(cacheProvider.getInvalidTokenCache()).thenReturn(invalidTokenCache); - cacheProviderUtilDummy.when(() -> CacheProviderUtil.getOrganizationCache(organization)).thenReturn(cacheProvider); + cacheProviderUtilDummy.when(() -> CacheProviderUtil.getOrganizationCache(organization)) + .thenReturn(cacheProvider); JWTValidator jwtValidator = Mockito.mock(JWTValidator.class); SubscriptionDataStoreImpl subscriptionDataStore = Mockito.mock(SubscriptionDataStoreImpl.class); - Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, - organization)).thenReturn(jwtValidator); + Mockito.when(subscriptionDataStore.getJWTValidatorByIssuer(issuer, organization, environment)) + .thenReturn(jwtValidator); subscriptionDataStoreImplDummy.when(SubscriptionDataStoreImpl::getInstance).thenReturn(subscriptionDataStore); Mockito.when(jwtValidator.validateToken(Mockito.eq(jwt), Mockito.any())).thenReturn(jwtValidationInfo); diff --git a/libs.versions.toml b/libs.versions.toml index 8b03f403e..ab47f1cb0 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -107,7 +107,6 @@ protobuf-java = {module = "com.google.protobuf:protobuf-java", version.ref = "pr hamcrest = {module = "org.hamcrest:hamcrest-all", version.ref = "hamcrest"} jacoco-agent = {module = "org.jacoco:org.jacoco.agent", version.ref = "jacoco"} mockito-core = {module = "org.mockito:mockito-core", version.ref = "mockito"} -mockito-inline = {module = "org.mockito:mockito-inline", version.ref = "mockito"} xml-apis = {module = "xml-apis:xml-apis", version.ref = "xml-apis"} mock-server-netty = {module = "org.mock-server:mockserver-netty", version.ref = "mock-server-netty"} mockito-inline = {module = "org.mockito:mockito-inline", version.ref = "mockito-inline"}