diff --git a/src/main/java/org/gcube/common/keycloak/DefaultKeycloakClient.java b/src/main/java/org/gcube/common/keycloak/DefaultKeycloakClient.java index c4f45e7..7d9a2cd 100644 --- a/src/main/java/org/gcube/common/keycloak/DefaultKeycloakClient.java +++ b/src/main/java/org/gcube/common/keycloak/DefaultKeycloakClient.java @@ -28,23 +28,64 @@ import org.gcube.common.gxrest.response.inbound.GXInboundResponse; import org.gcube.common.keycloak.model.ModelUtils; import org.gcube.common.keycloak.model.TokenIntrospectionResponse; import org.gcube.common.keycloak.model.TokenResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class DefaultKeycloakClient implements KeycloakClient { + protected static Logger logger = LoggerFactory.getLogger(KeycloakClient.class); + + @Override + public URL getTokenEndpointURL(URL realmBaseURL) throws KeycloakClientException { + logger.debug("Constructing token endpoint URL starting from base URL: {}", realmBaseURL); + try { + URL tokenURL = null; + if (realmBaseURL.getPath().endsWith("/")) { + tokenURL = new URL(realmBaseURL, OPEN_ID_URI_PATH + "/" + TOKEN_URI_PATH); + } else { + tokenURL = new URL(realmBaseURL.toString() + "/" + OPEN_ID_URI_PATH + "/" + TOKEN_URI_PATH); + } + logger.debug("Constructed token URL is: {}", tokenURL); + return tokenURL; + } catch (MalformedURLException e) { + throw new KeycloakClientException("Cannot constructs toke URL from base URL: " + realmBaseURL, e); + } + } + + @Override + public URL getIntrospectionEndpointURL(URL realmBaseURL) throws KeycloakClientException { + logger.debug("Constructing introspection URL starting from base URL: {}", realmBaseURL); + try { + URL tokenURL = null; + if (realmBaseURL.getPath().endsWith("/")) { + tokenURL = new URL(realmBaseURL, + OPEN_ID_URI_PATH + "/" + TOKEN_URI_PATH + "/" + TOKEN_INTROSPECT_URI_PATH); + } else { + tokenURL = new URL(realmBaseURL.toString() + "/" + OPEN_ID_URI_PATH + "/" + TOKEN_URI_PATH + "/" + + TOKEN_INTROSPECT_URI_PATH); + } + logger.debug("Constructed introspection URL is: {}", tokenURL); + return tokenURL; + } catch (MalformedURLException e) { + throw new KeycloakClientException("Cannot constructs toke URL from base URL: " + realmBaseURL, e); + } + } + @Override public URL computeIntrospectionEndpointURL(URL tokenEndpointURL) throws KeycloakClientException { - logger.debug("Computing introspection URL starting from token endpoint URL: {}", tokenEndpointURL); + logger.debug("Computing introspection endpoint URL starting from token endpoint URL: {}", tokenEndpointURL); try { URL introspectionURL = null; - if (tokenEndpointURL.getPath().endsWith("token/")) { - introspectionURL = new URL(tokenEndpointURL, "introspect"); + if (tokenEndpointURL.getPath().endsWith(TOKEN_URI_PATH + "/")) { + introspectionURL = new URL(tokenEndpointURL, TOKEN_INTROSPECT_URI_PATH); } else { - introspectionURL = new URL(tokenEndpointURL, "token/introspect"); + introspectionURL = new URL(tokenEndpointURL, TOKEN_URI_PATH + "/" + TOKEN_INTROSPECT_URI_PATH); } logger.debug("Computed introspection URL is: {}", introspectionURL); return introspectionURL; } catch (MalformedURLException e) { - throw new KeycloakClientException("Cannot create introspection URL from token URL: " + tokenEndpointURL, e); + throw new KeycloakClientException("Cannot compute introspection URL from token URL: " + tokenEndpointURL, + e); } } diff --git a/src/main/java/org/gcube/common/keycloak/KeycloakClient.java b/src/main/java/org/gcube/common/keycloak/KeycloakClient.java index 142c1a1..ac93ddc 100644 --- a/src/main/java/org/gcube/common/keycloak/KeycloakClient.java +++ b/src/main/java/org/gcube/common/keycloak/KeycloakClient.java @@ -5,17 +5,33 @@ import java.util.List; import org.gcube.common.keycloak.model.TokenIntrospectionResponse; import org.gcube.common.keycloak.model.TokenResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public interface KeycloakClient { - Logger logger = LoggerFactory.getLogger(KeycloakClient.class); + public static final String OPEN_ID_URI_PATH = "protocol/openid-connect"; + public static final String TOKEN_URI_PATH = "token"; + public static final String TOKEN_INTROSPECT_URI_PATH = "introspect"; + + /** + * Constructs the Keycloak token endpoint {@link URL} from the realm's base URL. + * + * @return the Keycloak token endpoint URL + * @throws KeycloakClientException if something goes wrong discovering the endpoint URL + */ + URL getTokenEndpointURL(URL realmBaseURL) throws KeycloakClientException; + + /** + * Constructs the Keycloak introspection endpoint {@link URL} from the realm's base URL. + * + * @return the Keycloak introspection endpoint URL + * @throws KeycloakClientException if something goes wrong discovering the endpoint URL + */ + URL getIntrospectionEndpointURL(URL realmBaseURL) throws KeycloakClientException; /** * Compute the keycloak introspection endpoint {@link URL} starting from the provided token endpoint. * - * @return the keycloak introspection endpoint URL in the current scope + * @return the keycloak introspection endpoint URL * @throws KeycloakClientException if something goes wrong discovering the endpoint URL */ URL computeIntrospectionEndpointURL(URL tokenEndpointURL) throws KeycloakClientException; diff --git a/src/test/java/org/gcube/common/keycloak/TestKeycloakClient.java b/src/test/java/org/gcube/common/keycloak/TestKeycloakClient.java index 0c129f1..7c2377d 100644 --- a/src/test/java/org/gcube/common/keycloak/TestKeycloakClient.java +++ b/src/test/java/org/gcube/common/keycloak/TestKeycloakClient.java @@ -19,7 +19,8 @@ public class TestKeycloakClient { protected static final Logger logger = LoggerFactory.getLogger(TestKeycloakClient.class); - protected static final String DEV_TOKEN_ENDPOINT = "https://accounts.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token"; + protected static final String DEV_BASE_URL = "https://accounts.dev.d4science.org/auth/realms/d4science"; + protected static final String DEV_TOKEN_ENDPOINT = DEV_BASE_URL + "/protocol/openid-connect/token"; protected static final String DEV_INTROSPECTION_ENDPOINT = DEV_TOKEN_ENDPOINT + "/introspect"; protected static final String CLIENT_ID = "keycloak-client-unit-test"; @@ -39,6 +40,46 @@ public class TestKeycloakClient { public void tearDown() throws Exception { } + @Test + public void test00TokenEndpointConstruction() throws Exception { + logger.info("*** [0.0] Start testing Keycloak token endpoint construction from base URL..."); + URL url = KeycloakClientFactory.newInstance().getTokenEndpointURL(new URL(DEV_BASE_URL)); + Assert.assertNotNull(url); + Assert.assertTrue(url.getProtocol().equals("https")); + Assert.assertEquals(new URL(DEV_TOKEN_ENDPOINT), url); + logger.info("Constructed URL is: {}", url); + } + + @Test + public void test00ATokenEndpointConstructionWithTrailingSlash() throws Exception { + logger.info("*** [0.0] Start testing Keycloak token endpoint construction from base URL with trailing slash..."); + URL url = KeycloakClientFactory.newInstance().getTokenEndpointURL(new URL(DEV_BASE_URL + "/")); + Assert.assertNotNull(url); + Assert.assertTrue(url.getProtocol().equals("https")); + Assert.assertEquals(new URL(DEV_TOKEN_ENDPOINT), url); + logger.info("Constructed URL is: {}", url); + } + + @Test + public void test01IntrospectionEndpointConstruction() throws Exception { + logger.info("*** [0.0] Start testing Keycloak introspection endpoint construction from base URL..."); + URL url = KeycloakClientFactory.newInstance().getIntrospectionEndpointURL(new URL(DEV_BASE_URL)); + Assert.assertNotNull(url); + Assert.assertTrue(url.getProtocol().equals("https")); + Assert.assertEquals(new URL(DEV_INTROSPECTION_ENDPOINT), url); + logger.info("Constructed URL is: {}", url); + } + + @Test + public void test01AIntrospectionEndpointConstructionWithTrailingSlash() throws Exception { + logger.info("*** [0.0] Start testing Keycloak introspection endpoint construction from base URL with trailing slash..."); + URL url = KeycloakClientFactory.newInstance().getIntrospectionEndpointURL(new URL(DEV_BASE_URL + "/")); + Assert.assertNotNull(url); + Assert.assertTrue(url.getProtocol().equals("https")); + Assert.assertEquals(new URL(DEV_INTROSPECTION_ENDPOINT), url); + logger.info("Constructed URL is: {}", url); + } + @Test public void test02IntrospectEndpointCompute() throws Exception { logger.info("*** [0.2] Start testing Keycloak userinfo endpoint computed from provided URL..."); @@ -46,7 +87,7 @@ public class TestKeycloakClient { Assert.assertNotNull(url); Assert.assertTrue(url.getProtocol().equals("https")); Assert.assertEquals(new URL(DEV_INTROSPECTION_ENDPOINT), url); - logger.info("Discovered URL is: {}", url); + logger.info("Computed URL is: {}", url); } @Test