+ * Each rsa key should start with xxxxssh-rsa and then contains two big integer (modulus & exponent) which are prime number.
+ * The modulus & exponent are used to generate the RSA Public Key.
+ * See wiki explanations for deeper understanding
+ *
+ * @param encKey String
+ * @return RSAPublicKey
+ */
+ private static RSAPublicKey parseSSHPublicKey(String encKey) {
+ final byte[] PREFIX = new byte[] {0,0,0,7, 's','s','h','-','r','s','a'};
+ ByteArrayInputStream in = new ByteArrayInputStream(Base64.getDecoder().decode(StandardCharsets.UTF_8.encode(encKey)).array());
+
+ byte[] prefix = new byte[11];
+
+ try {
+ if (in.read(prefix) != 11 || !Arrays.equals(PREFIX, prefix)) {
+ throw new IllegalArgumentException("SSH key prefix not found");
+ }
+
+ BigInteger e = new BigInteger(readBigInteger(in));//public exponent
+ BigInteger n = new BigInteger(readBigInteger(in));//modulus
+
+ return createPublicKey(n, e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static RSAPublicKey createPublicKey(BigInteger n, BigInteger e) {
+ try {
+ return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(n, e));
+ }
+ catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * bytes are not in the good order, they are in the big endian format, we reorder them before reading them...
+ * Each time you call this method, the buffer position will move, so result are differents...
+ * @param in byte array of a public encryption key without 11 "xxxxssh-rsa" first byte.
+ * @return BigInteger public exponent on first call, then modulus.
+ * @throws IOException
+ */
+ private static byte[] readBigInteger(ByteArrayInputStream in) throws IOException {
+ byte[] b = new byte[4];
+
+ if (in.read(b) != 4) {
+ throw new IOException("Expected length data as 4 bytes");
+ }
+
+ int l = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
+
+ b = new byte[l];
+
+ if (in.read(b) != l) {
+ throw new IOException("Expected " + l + " key bytes");
+ }
+
+ return b;
+ }
+}
diff --git a/src/main/java/io/gravitee/policy/jwt/configuration/JWTPolicyConfiguration.java b/src/main/java/io/gravitee/policy/jwt/configuration/JWTPolicyConfiguration.java
new file mode 100644
index 00000000..7d37085b
--- /dev/null
+++ b/src/main/java/io/gravitee/policy/jwt/configuration/JWTPolicyConfiguration.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * 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 io.gravitee.policy.jwt.configuration;
+
+import io.gravitee.policy.api.PolicyConfiguration;
+
+public class JWTPolicyConfiguration implements PolicyConfiguration {
+
+ //settings attributes
+ private String givenKey;
+ private boolean useValidationCache = false;
+ private PublicKeyResolver publicKeyResolver = PublicKeyResolver.GIVEN_KEY;
+
+
+ //getter and setters
+ public PublicKeyResolver getPublicKeyResolver() {
+ return publicKeyResolver;
+ }
+
+ public void setPublicKeyResolver(PublicKeyResolver publicKeyResolver) {
+ this.publicKeyResolver = publicKeyResolver;
+ }
+
+ public String getGivenKey() {
+ return givenKey;
+ }
+
+ public void setGivenKey(String givenKey) {
+ this.givenKey = givenKey;
+ }
+
+ public boolean isUseValidationCache() {
+ return useValidationCache;
+ }
+
+ public void setUseValidationCache(boolean useValidationCache) {
+ this.useValidationCache = useValidationCache;
+ }
+}
diff --git a/src/main/java/io/gravitee/policy/jwt/configuration/PublicKeyResolver.java b/src/main/java/io/gravitee/policy/jwt/configuration/PublicKeyResolver.java
new file mode 100644
index 00000000..70d0ac81
--- /dev/null
+++ b/src/main/java/io/gravitee/policy/jwt/configuration/PublicKeyResolver.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * 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 io.gravitee.policy.jwt.configuration;
+
+
+public enum PublicKeyResolver {
+ GIVEN_KEY,
+ GATEWAY_KEYS
+}
diff --git a/src/main/java/io/gravitee/policy/jwt/exceptions/ValidationFromCacheException.java b/src/main/java/io/gravitee/policy/jwt/exceptions/ValidationFromCacheException.java
new file mode 100644
index 00000000..6b959e44
--- /dev/null
+++ b/src/main/java/io/gravitee/policy/jwt/exceptions/ValidationFromCacheException.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * 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 io.gravitee.policy.jwt.exceptions;
+
+/**
+ * Created by Alexandre on 20/07/2016.
+ */
+public class ValidationFromCacheException extends Exception{
+ public ValidationFromCacheException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/resources/plugin.properties b/src/main/resources/plugin.properties
new file mode 100644
index 00000000..7cd7ca45
--- /dev/null
+++ b/src/main/resources/plugin.properties
@@ -0,0 +1,6 @@
+id=jwt
+name=JSON Web Tokens
+version=${project.version}
+description=${project.description}
+class=io.gravitee.policy.jwt.JWTPolicy
+type=policy
diff --git a/src/main/resources/schemas/urn:jsonschema:io:gravitee:policy:jwt:configuration:JWTPolicyConfiguration.json b/src/main/resources/schemas/urn:jsonschema:io:gravitee:policy:jwt:configuration:JWTPolicyConfiguration.json
new file mode 100644
index 00000000..8ecaa661
--- /dev/null
+++ b/src/main/resources/schemas/urn:jsonschema:io:gravitee:policy:jwt:configuration:JWTPolicyConfiguration.json
@@ -0,0 +1,26 @@
+{
+ "type" : "object",
+ "id" : "urn:jsonschema:io:gravitee:policy:jwt:configuration:JWTPolicyConfiguration",
+ "properties" : {
+ "publicKeyResolver" : {
+ "title": "Public key resolver",
+ "description": "Select how public key is retrieved among : given key or gateway key settings...",
+ "type" : "string",
+ "default": "GIVEN_KEY",
+ "enum" : [ "GIVEN_KEY", "GATEWAY_KEYS" ]
+ },
+ "givenKey" : {
+ "title": "Given public key (ssh-rsa ABCDE xxx@yyy.zz)",
+ "description": "The public key to use (needed if you select GIVEN_KEY resolver) (support EL).",
+ "type" : "string"
+ },
+ "useValidationCache" : {
+ "title": "Use validation cache",
+ "description": "Cache validated JWT instead of checking signature on each API call",
+ "type" : "boolean"
+ }
+ },
+ "required": [
+ "useValidationCache"
+ ]
+}
\ No newline at end of file
diff --git a/src/test/java/io/gravitee/policy/jwt/JWTPolicyTest.java b/src/test/java/io/gravitee/policy/jwt/JWTPolicyTest.java
new file mode 100644
index 00000000..dd6fa46c
--- /dev/null
+++ b/src/test/java/io/gravitee/policy/jwt/JWTPolicyTest.java
@@ -0,0 +1,468 @@
+/**
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * 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 io.gravitee.policy.jwt;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.time.Instant;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.springframework.core.env.Environment;
+
+import io.gravitee.common.http.HttpHeaders;
+import io.gravitee.gateway.api.ExecutionContext;
+import io.gravitee.gateway.api.Request;
+import io.gravitee.gateway.api.Response;
+import io.gravitee.gateway.api.expression.TemplateEngine;
+import io.gravitee.policy.api.PolicyChain;
+import io.gravitee.policy.api.PolicyResult;
+import io.gravitee.policy.jwt.configuration.JWTPolicyConfiguration;
+import io.gravitee.policy.jwt.configuration.PublicKeyResolver;
+import io.gravitee.repository.cache.api.CacheManager;
+import io.gravitee.repository.cache.model.Cache;
+import io.gravitee.repository.cache.model.Element;
+import io.gravitee.repository.exceptions.CacheException;
+import io.jsonwebtoken.JwtBuilder;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+
+public class JWTPolicyTest {
+
+ private static final String ISS = "gravitee.authorization.server";
+ private static final String KID = "MAIN";
+
+ @Mock
+ private ExecutionContext executionContext;
+ @Mock
+ private Environment environment;
+ @Mock
+ private Request request;
+ @Mock
+ private Response response;
+ @Mock
+ private PolicyChain policyChain;
+ @Mock
+ private JWTPolicyConfiguration configuration;
+ @Mock
+ private CacheManager cacheManager;
+ @Mock
+ private Cache cache;
+ @Mock
+ private Element element;
+ @Mock
+ TemplateEngine templateEngine;
+
+ @Before
+ public void init() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void test_with_cache_disabled_and_gateway_keys_and_valid_authorization_header() throws Exception {
+
+ String jwt = getJsonWebToken(7200);
+
+ when(executionContext.getComponent(Environment.class)).thenReturn(environment);
+ when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(false);
+ when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+
+ verify(policyChain,Mockito.times(1)).doNext(request, response);
+ }
+
+ @Test
+ public void test_with_cache_disabled_and_given_key_and_valid_authorization_header() throws Exception {
+
+ String jwt = getJsonWebToken(7200);
+
+ when(executionContext.getComponent(Environment.class)).thenReturn(environment);
+ when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(false);
+ when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GIVEN_KEY);
+ when(configuration.getGivenKey()).thenReturn(getSshRsaKey());
+ when(executionContext.getTemplateEngine()).thenReturn(templateEngine);
+ when(templateEngine.convert(getSshRsaKey())).thenReturn(getSshRsaKey());
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+
+ verify(policyChain,Mockito.times(1)).doNext(request, response);
+ }
+
+ @Test
+ public void test_with_cache_disabled_and_given_key_using_EL_and_valid_authorization_header() throws Exception {
+
+ String jwt = getJsonWebToken(7200);
+ final String property = "prop['key']";
+
+ when(executionContext.getComponent(Environment.class)).thenReturn(environment);
+ when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(false);
+ when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GIVEN_KEY);
+ when(configuration.getGivenKey()).thenReturn(property);
+ when(executionContext.getTemplateEngine()).thenReturn(templateEngine);
+ when(templateEngine.convert(property)).thenReturn(getSshRsaKey());
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+
+ verify(policyChain,Mockito.times(1)).doNext(request, response);
+ }
+
+ @Test
+ public void test_with_cache_disabled_and_given_key_but_not_provided() throws Exception {
+
+ String jwt = getJsonWebToken(7200);
+
+ when(executionContext.getComponent(Environment.class)).thenReturn(environment);
+ when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(false);
+ when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GIVEN_KEY);
+ when(configuration.getGivenKey()).thenReturn(null);
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+
+ verify(policyChain,times(1)).failWith(any(PolicyResult.class));
+ verify(policyChain,Mockito.times(0)).doNext(request, response);
+ }
+
+ @Test
+ public void test_with_cache_disabled_and_gateway_keys_and_valid_access_token() throws Exception {
+
+ String jwt = getJsonWebToken(7200);
+
+ when(executionContext.getComponent(Environment.class)).thenReturn(environment);
+ when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
+
+ Map parameters = new HashMap(1);
+ parameters.put("access_token", jwt);
+
+ when(request.headers()).thenReturn(new HttpHeaders());
+ when(request.parameters()).thenReturn(parameters);
+ when(configuration.isUseValidationCache()).thenReturn(false);
+ when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+
+ verify(request,times(1)).parameters();
+ verify(policyChain,times(1)).doNext(request, response);
+ }
+
+ @Test
+ public void test_with_cache_disabled_and_gateway_keys_and_expired_header_token() throws Exception {
+
+ String jwt = getJsonWebToken(0);
+
+ when(executionContext.getComponent(Environment.class)).thenReturn(environment);
+ when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(false);
+ when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+
+ verify(policyChain,times(1)).failWith(any(PolicyResult.class));
+ verify(policyChain,Mockito.times(0)).doNext(request, response);
+ }
+
+ @Test
+ public void test_with_cache_disabled_and_gateway_keys_and_unknonw_issuer() throws Exception {
+
+ String jwt = getJsonWebToken(7200,"unknown",null);
+
+ when(executionContext.getComponent(Environment.class)).thenReturn(environment);
+ when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(false);
+ when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+ verify(policyChain,times(1)).failWith(any(PolicyResult.class));
+ verify(policyChain,Mockito.times(0)).doNext(request, response);
+ }
+
+ @Test
+ public void test_with_cache_enabled_and_valid_expiration_date_cache() throws Exception {
+
+ String jwt = "jwtUsedAsKeyCache";
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(true);
+
+ //Mock cache actions.
+ when(executionContext.getComponent(CacheManager.class)).thenReturn(cacheManager);
+ when(cacheManager.getCache(any(String.class))).thenReturn(cache);
+ when(cache.get(jwt)).thenReturn(element);
+ when(element.value()).thenReturn(Instant.now().plusSeconds(7200));
+
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+
+ verify(policyChain,Mockito.times(1)).doNext(request, response);
+ }
+
+ @Test(expected = JwtException.class)
+ public void test_with_cache_enabled_and_non_valid_expiration_date_cache() throws Exception {
+
+ String jwt = "jwtUsedAsKeyCache";
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(true);
+
+ //Mock cache actions.
+ when(executionContext.getComponent(CacheManager.class)).thenReturn(cacheManager);
+ when(cacheManager.getCache(any(String.class))).thenReturn(cache);
+ when(cache.get(jwt)).thenReturn(element);
+ when(element.value()).thenReturn(Instant.now().minusSeconds(1));
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+
+ verify(policyChain,Mockito.times(1)).doNext(request, response);
+ }
+
+ @Test
+ public void test_with_empty_cache_enabled_and_gateway_keys_and_valid_jwt() throws Exception {
+
+ String jwt = getJsonWebToken(7200);
+ when(executionContext.getComponent(Environment.class)).thenReturn(environment);
+ when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(true);
+ when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);
+
+ //Mock cache actions.
+ when(executionContext.getComponent(CacheManager.class)).thenReturn(cacheManager);
+ when(cacheManager.getCache(any(String.class))).thenReturn(cache);
+ when(cache.get(jwt)).thenReturn(null);
+ when(element.value()).thenReturn(Instant.now().plusSeconds(7200));
+
+ JWTPolicy policy = new JWTPolicy(configuration);
+
+ policy.onRequest(request, response, executionContext, policyChain);//1st time no cache found
+ when(cache.get(jwt)).thenReturn(element);
+ policy.onRequest(request, response, executionContext, policyChain);//2nd time cache found
+
+ verify(cache,times(1)).put(any(Element.class));
+ verify(policyChain,Mockito.times(2)).doNext(request, response);
+ }
+
+ @Test
+ public void test_with_empty_cache_enabled_and_expired_jwt() throws Exception {
+
+ String jwt = getJsonWebToken(0);
+ when(executionContext.getComponent(Environment.class)).thenReturn(environment);
+ when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(true);
+ when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);
+
+ //Mock cache actions.
+ when(executionContext.getComponent(CacheManager.class)).thenReturn(cacheManager);
+ when(cacheManager.getCache(any(String.class))).thenReturn(cache);
+ when(cache.get(jwt)).thenReturn(null);
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+
+ verify(policyChain,times(1)).failWith(any(PolicyResult.class));
+ verify(policyChain,Mockito.times(0)).doNext(request, response);
+ }
+
+ @Test
+ public void test_with_cache_enabled_and_no_cache_manager() throws Exception {
+
+ String jwt = getJsonWebToken(7200);
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(true);
+ when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);
+ when(executionContext.getComponent(Environment.class)).thenReturn(environment);
+ when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
+
+ //Mock cache actions.
+ when(executionContext.getComponent(CacheManager.class)).thenReturn(null);
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+
+ verify(policyChain,Mockito.times(0)).failWith(any(PolicyResult.class));
+ verify(policyChain,Mockito.times(1)).doNext(request, response);
+ }
+
+ @Test
+ public void test_with_cache_enabled_and_no_cache_name() throws Exception {
+
+ String jwt = getJsonWebToken(7200);
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(true);
+ when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);
+ when(executionContext.getComponent(Environment.class)).thenReturn(environment);
+ when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
+
+ //Mock cache actions.
+ when(executionContext.getComponent(CacheManager.class)).thenReturn(cacheManager);
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+
+ verify(policyChain,Mockito.times(0)).failWith(any(PolicyResult.class));
+ verify(policyChain,Mockito.times(1)).doNext(request, response);
+ }
+
+ @Test
+ public void test_with_cache_enabled_and_cache_exception() throws Exception {
+
+ String jwt = getJsonWebToken(7200);
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Authorization", "Bearer "+jwt);
+ when(request.headers()).thenReturn(headers);
+ when(configuration.isUseValidationCache()).thenReturn(true);
+ when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);
+ when(executionContext.getComponent(Environment.class)).thenReturn(environment);
+ when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
+
+ //Mock cache actions.
+ when(executionContext.getComponent(CacheManager.class)).thenThrow(CacheException.class);
+
+ new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);
+
+ verify(policyChain,Mockito.times(0)).failWith(any(PolicyResult.class));
+ verify(policyChain,Mockito.times(1)).doNext(request, response);
+ }
+
+
+ //PRIVATE tools method for tests
+ /**
+ * Return Json Web Token string value.
+ * @return String
+ * @throws Exception
+ */
+ private String getJsonWebToken(long secondsToAdd) throws Exception {
+ return getJsonWebToken(secondsToAdd,null,null);
+ }
+
+ /**
+ * Return Json Web Token string value.
+ * @return String
+ * @throws Exception
+ */
+ private String getJsonWebToken(long secondsToAdd, String iss, String kid) throws Exception{
+
+ Map header = new HashMap(2);
+ header.put("alg", "RS256");
+ header.put("kid", kid!=null?kid:KID);
+
+ JwtBuilder jwtBuilder = Jwts.builder();
+ jwtBuilder.setHeader(header);
+ jwtBuilder.setSubject("alexluso");
+ jwtBuilder.setIssuer(iss!=null?iss:ISS);
+ jwtBuilder.setExpiration(Date.from(Instant.now().plusSeconds(secondsToAdd)));
+
+ jwtBuilder.signWith(SignatureAlgorithm.RS256, getPrivateKey());
+ return jwtBuilder.compact();
+ }
+
+ /**
+ * How to generate keys?
+ * Run : ssh-keygen -t rsa -C "alex.luso@myCompany.com"
+ * ==> Will create id_rsa & id_rsa.pub
+ * Then run : openssl pkcs8 -topk8 -inform PEM -outform DER -in id_rsa -out private_key.der -nocrypt
+ * ==> Will create private_key.der unsecured that can be used.
+ * @return
+ * @throws Exception
+ */
+ private PrivateKey getPrivateKey() throws Exception {
+ File file = new File(getClass().getClassLoader().getResource("private_key.der").getFile());
+ FileInputStream fis = new FileInputStream(file);
+ DataInputStream dis = new DataInputStream(fis);
+ byte[] keyBytes = new byte[(int) file.length()];
+ dis.readFully(keyBytes);
+ dis.close();
+
+ PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+
+ return kf.generatePrivate(spec);
+ }
+
+ /**
+ * Return string value of public key matching format ssh-(rsa|dsa) ([A-Za-z0-9/+]+=*) (.*)
+ * @return String
+ * @throws IOException
+ */
+ private String getSshRsaKey() throws IOException {
+ InputStream input = getClass().getClassLoader().getResourceAsStream("id_rsa.pub");
+ BufferedReader buffer = new BufferedReader(new InputStreamReader(input));
+ return buffer.lines().collect(Collectors.joining("\n"));
+ }
+}
diff --git a/src/test/resources/id_rsa b/src/test/resources/id_rsa
new file mode 100644
index 00000000..359c0033
--- /dev/null
+++ b/src/test/resources/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA43noN5Z2kxxzg4sy2vUwi87Qk+JpdXRGmHR4b4w1sAe8fcFX
+Af066FFR8ph9Yu/+H1MoSBWnP+YsYjh8tByYuO9LanzUkKsNW97V3wQn5sFsHCdk
+rF6xSsFRWWXCPjOYIVzxmuvrFLs7X9yUbjUxSzFd6mvN+PrUKUcJZfHkmaEblnHk
+lKU0O2UtEIyJjBnp9XQYdYlY8FwhJ/z0FMMcaf+mv+NFrXll87Yz2hNFxFqAJyWB
+3ogH0J5bWGPZcCnoRCTn+7CQn5fmmq4ocippjofjVPcQ/z1jB5MGUpWpQZ9pP6p9
+adPbN1AzWEKyVU6VCBvPJUyB4kgqqGl5d9JWDwIDAQABAoIBAQDXR30MdppbWVa9
+DFShweAiwCTHgEP8A4H4MGn2b4Qzxu6NORel60j/qk5awBQSOTyP2rxJlCyHncct
+YXYrYtDqXJVL/z2QeEGZS4eumxlEGpO9BU8Sjj9Nlyzs5Q/ynBOCp5qD2nfNU/C6
+JWBX+IFhPyQ5gbMZyhBVzEPJtiZ5eKTF9qJuExrB9E1HzCrWxSftUBc9CkBiPntM
+jA2Qq43Eh1Z2+/lcfT3Qc/7/vf3UBvf50J/3JejE6PAw4jNe04ozs9LHenKrLoRL
+3dhuqHZPM1XEEH8xJEOnFngK4k4cA073gthL7fKz5kUAUPoAu8Im32Kpmg2WfVlT
+bS1ZijFRAoGBAP+Vqa6MUddJoch3sipIIFXQFkI57oNQ57YRy8T9rj8ahLxuiLxF
+1k/a5ihNGWvHChQhyARUaah0Z2O2tQ43ZJ7dZPabNwQov+COF5MGl/BxCkQ7QinY
+aaGq8LUJLy3PdlyGUYHj9dC0gYfEWiV2t/l+ZPEUhFG30gXFZeobnINJAoGBAOPY
+jLUXMNEK4Px1trhkExNVPcnlKzZztklvX6N1O8KgNrAPl64b2XoujINhohT86RX4
+weBR36r38UHtZWOpozW/1FOdQFY3z9+8crTJgZqpKaXKGTRL0OIoU2MEWqLlMSw2
+pZxAvB1RBYEmFX07SOMfaqSGLGUUBDN4XIAU07aXAoGAFHBqjmvoS5g22OpBlEIK
+W/J1JTyux0+cCCJqMkm7Oo6rWMpaIvxOxDoUN9rakpTrSGrfLQF3JaKRdhbxab6i
+TFYWMeZ9wtZjadjTJ83aLr9Le+NlSiVlZSlfcIrYfAhgRcv0DrglO1iEF1BriR1y
+XwBtoB3s6wARSqbbnJoyrQkCgYA63aPc1ZUDLTBbiX4fvZtAD3HbS54Sf2rFJkUr
+UgqSihoW+rBRh1h0vLoI55ycl4sQ5igQ8JY88bofMlpTmWxVYq5Uu/f3TowiXem0
+06rsbnAYKVLBtCTPiWOh3WodU+GUbrny2LbBTEGD0HcU19BI/cDrqM6nfrhnI92i
+Kb9ZGQKBgGVeOyqSlXXE6mXLtSxSRpZ8S917xwgD1iHZIT42aYG5ER4MDUZ0+Wcw
+7y9IjNxGIT1G2RIxMW880dVz/uXd6b7/XLn2J1JcvEIA+11OmBk2DfFtlDDlWWyz
+fu00emXp/W9lLtTsN68ebEOY65hcOMSqZ8dC9j9bPfvqm29OXX9b
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/resources/id_rsa.pub b/src/test/resources/id_rsa.pub
new file mode 100644
index 00000000..da01480b
--- /dev/null
+++ b/src/test/resources/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDjeeg3lnaTHHODizLa9TCLztCT4ml1dEaYdHhvjDWwB7x9wVcB/TroUVHymH1i7/4fUyhIFac/5ixiOHy0HJi470tqfNSQqw1b3tXfBCfmwWwcJ2SsXrFKwVFZZcI+M5ghXPGa6+sUuztf3JRuNTFLMV3qa834+tQpRwll8eSZoRuWceSUpTQ7ZS0QjImMGen1dBh1iVjwXCEn/PQUwxxp/6a/40WteWXztjPaE0XEWoAnJYHeiAfQnltYY9lwKehEJOf7sJCfl+aarihyKmmOh+NU9xD/PWMHkwZSlalBn2k/qn1p09s3UDNYQrJVTpUIG88lTIHiSCqoaXl30lYP alex.luso@myCompany.com
diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml
new file mode 100644
index 00000000..3e842fd9
--- /dev/null
+++ b/src/test/resources/logback.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ %date{yyyy-MM-dd HH:mm:ss.SSS} [%-5p] %c: %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/private_key.der b/src/test/resources/private_key.der
new file mode 100644
index 00000000..126f7dc7
Binary files /dev/null and b/src/test/resources/private_key.der differ