Added support to JWK endpoint and key sets retrieve to take info about configured key algorithms
This commit is contained in:
parent
7d98fbaa16
commit
e339be5083
|
@ -42,6 +42,7 @@ import org.gcube.common.gxhttp.util.ContentUtils;
|
||||||
import org.gcube.common.gxrest.request.GXHTTPStringRequest;
|
import org.gcube.common.gxrest.request.GXHTTPStringRequest;
|
||||||
import org.gcube.common.gxrest.response.inbound.GXInboundResponse;
|
import org.gcube.common.gxrest.response.inbound.GXInboundResponse;
|
||||||
import org.gcube.common.gxrest.response.inbound.JsonUtils;
|
import org.gcube.common.gxrest.response.inbound.JsonUtils;
|
||||||
|
import org.gcube.common.keycloak.model.JSONWebKeySet;
|
||||||
import org.gcube.common.keycloak.model.ModelUtils;
|
import org.gcube.common.keycloak.model.ModelUtils;
|
||||||
import org.gcube.common.keycloak.model.PublishedRealmRepresentation;
|
import org.gcube.common.keycloak.model.PublishedRealmRepresentation;
|
||||||
import org.gcube.common.keycloak.model.TokenIntrospectionResponse;
|
import org.gcube.common.keycloak.model.TokenIntrospectionResponse;
|
||||||
|
@ -125,6 +126,23 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL getJWKEndpointURL(URL realmBaseURL) throws KeycloakClientException {
|
||||||
|
logger.debug("Constructing JWK endpoint URL starting from base URL: {}", realmBaseURL);
|
||||||
|
try {
|
||||||
|
URL jwkURL = null;
|
||||||
|
if (realmBaseURL.getPath().endsWith("/")) {
|
||||||
|
jwkURL = new URL(realmBaseURL, OPEN_ID_URI_PATH + "/" + JWK_URI_PATH);
|
||||||
|
} else {
|
||||||
|
jwkURL = new URL(realmBaseURL.toString() + "/" + OPEN_ID_URI_PATH + "/" + JWK_URI_PATH);
|
||||||
|
}
|
||||||
|
logger.debug("Constructed JWK URL is: {}", jwkURL);
|
||||||
|
return jwkURL;
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new KeycloakClientException("Cannot constructs JWK URL from base URL: " + realmBaseURL, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getIntrospectionEndpointURL(URL realmBaseURL) throws KeycloakClientException {
|
public URL getIntrospectionEndpointURL(URL realmBaseURL) throws KeycloakClientException {
|
||||||
logger.debug("Constructing introspection URL starting from base URL: {}", realmBaseURL);
|
logger.debug("Constructing introspection URL starting from base URL: {}", realmBaseURL);
|
||||||
|
@ -184,11 +202,21 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
||||||
try {
|
try {
|
||||||
return JsonUtils.fromJson(ContentUtils.toByteArray(realmURL.openStream()),
|
return JsonUtils.fromJson(ContentUtils.toByteArray(realmURL.openStream()),
|
||||||
PublishedRealmRepresentation.class);
|
PublishedRealmRepresentation.class);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new KeycloakClientException("Getting realm's info", e);
|
throw new KeycloakClientException("Getting realm's info", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONWebKeySet getRealmJSONWebKeySet(URL jwkURL) throws KeycloakClientException {
|
||||||
|
try {
|
||||||
|
return JsonUtils.fromJson(ContentUtils.toByteArray(jwkURL.openStream()), JSONWebKeySet.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new KeycloakClientException("Getting realm's JWK", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TokenResponse queryOIDCToken(String context, String clientId, String clientSecret)
|
public TokenResponse queryOIDCToken(String context, String clientId, String clientSecret)
|
||||||
throws KeycloakClientException {
|
throws KeycloakClientException {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import java.net.URL;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
|
import org.gcube.common.keycloak.model.JSONWebKeySet;
|
||||||
import org.gcube.common.keycloak.model.PublishedRealmRepresentation;
|
import org.gcube.common.keycloak.model.PublishedRealmRepresentation;
|
||||||
import org.gcube.common.keycloak.model.TokenIntrospectionResponse;
|
import org.gcube.common.keycloak.model.TokenIntrospectionResponse;
|
||||||
import org.gcube.common.keycloak.model.TokenResponse;
|
import org.gcube.common.keycloak.model.TokenResponse;
|
||||||
|
@ -14,6 +14,7 @@ public interface KeycloakClient {
|
||||||
public static final String PROD_ROOT_SCOPE = "/d4science.research-infrastructures.eu";
|
public static final String PROD_ROOT_SCOPE = "/d4science.research-infrastructures.eu";
|
||||||
public static final String OPEN_ID_URI_PATH = "protocol/openid-connect";
|
public static final String OPEN_ID_URI_PATH = "protocol/openid-connect";
|
||||||
public static final String TOKEN_URI_PATH = "token";
|
public static final String TOKEN_URI_PATH = "token";
|
||||||
|
public static final String JWK_URI_PATH = "certs";
|
||||||
public static final String TOKEN_INTROSPECT_URI_PATH = "introspect";
|
public static final String TOKEN_INTROSPECT_URI_PATH = "introspect";
|
||||||
public static final String AVATAR_URI_PATH = "account-avatar";
|
public static final String AVATAR_URI_PATH = "account-avatar";
|
||||||
public final static String D4S_CONTEXT_HEADER_NAME = "X-D4Science-Context";
|
public final static String D4S_CONTEXT_HEADER_NAME = "X-D4Science-Context";
|
||||||
|
@ -49,6 +50,15 @@ public interface KeycloakClient {
|
||||||
*/
|
*/
|
||||||
URL getTokenEndpointURL(URL realmBaseURL) throws KeycloakClientException;
|
URL getTokenEndpointURL(URL realmBaseURL) throws KeycloakClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the Keycloak <code>JWK</code> endpoint {@link URL} from the realm's base URL.
|
||||||
|
*
|
||||||
|
* @param realmBaseURL the realm's base URL to use
|
||||||
|
* @return the Keycloak <code>JWK</code> endpoint URL
|
||||||
|
* @throws KeycloakClientException if something goes wrong discovering the endpoint URL
|
||||||
|
*/
|
||||||
|
URL getJWKEndpointURL(URL realmBaseURL) throws KeycloakClientException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the Keycloak <code>introspection</code> endpoint {@link URL} from the realm's base URL.
|
* Constructs the Keycloak <code>introspection</code> endpoint {@link URL} from the realm's base URL.
|
||||||
*
|
*
|
||||||
|
@ -77,7 +87,7 @@ public interface KeycloakClient {
|
||||||
URL getAvatarEndpointURL(URL realmBaseURL) throws KeycloakClientException;
|
URL getAvatarEndpointURL(URL realmBaseURL) throws KeycloakClientException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the realm info setup (RSA <code>public_key</code>, <code>token-service</code> URL,
|
* Gets the realm info setup (RSA <code>public_key</code>, <code>token-service</code> URL,
|
||||||
* <code>account-service</code> URL and <code>tokens-not-before</code> setting)
|
* <code>account-service</code> URL and <code>tokens-not-before</code> setting)
|
||||||
*
|
*
|
||||||
* @param realmURL the realm URL
|
* @param realmURL the realm URL
|
||||||
|
@ -86,6 +96,8 @@ public interface KeycloakClient {
|
||||||
*/
|
*/
|
||||||
PublishedRealmRepresentation getRealmInfo(URL realmURL) throws KeycloakClientException;
|
PublishedRealmRepresentation getRealmInfo(URL realmURL) throws KeycloakClientException;
|
||||||
|
|
||||||
|
JSONWebKeySet getRealmJSONWebKeySet(URL jwkURL) throws KeycloakClientException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries an OIDC token from the context's Keycloak server, by using provided clientId and client secret.
|
* Queries an OIDC token from the context's Keycloak server, by using provided clientId and client secret.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* 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.gcube.common.keycloak.model;
|
||||||
|
|
||||||
|
import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class JSONWebKeySet {
|
||||||
|
|
||||||
|
@JsonProperty("keys")
|
||||||
|
private JWK[] keys;
|
||||||
|
|
||||||
|
public JWK[] getKeys() {
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeys(JWK[] keys) {
|
||||||
|
this.keys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* 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.gcube.common.keycloak.model;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.gcube.com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||||
|
import org.gcube.com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||||
|
import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class JWK {
|
||||||
|
|
||||||
|
public static final String KEY_ID = "kid";
|
||||||
|
|
||||||
|
public static final String KEY_TYPE = "kty";
|
||||||
|
|
||||||
|
public static final String ALGORITHM = "alg";
|
||||||
|
|
||||||
|
public static final String PUBLIC_KEY_USE = "use";
|
||||||
|
|
||||||
|
public enum Use {
|
||||||
|
SIG("sig"),
|
||||||
|
ENCRYPTION("enc");
|
||||||
|
|
||||||
|
private String str;
|
||||||
|
|
||||||
|
Use(String str) {
|
||||||
|
this.str = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String asString() {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty(KEY_ID)
|
||||||
|
private String keyId;
|
||||||
|
|
||||||
|
@JsonProperty(KEY_TYPE)
|
||||||
|
private String keyType;
|
||||||
|
|
||||||
|
@JsonProperty(ALGORITHM)
|
||||||
|
private String algorithm;
|
||||||
|
|
||||||
|
@JsonProperty(PUBLIC_KEY_USE)
|
||||||
|
private String publicKeyUse;
|
||||||
|
|
||||||
|
protected Map<String, Object> otherClaims = new HashMap<String, Object>();
|
||||||
|
|
||||||
|
|
||||||
|
public String getKeyId() {
|
||||||
|
return keyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyId(String keyId) {
|
||||||
|
this.keyId = keyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyType() {
|
||||||
|
return keyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyType(String keyType) {
|
||||||
|
this.keyType = keyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAlgorithm() {
|
||||||
|
return algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlgorithm(String algorithm) {
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublicKeyUse() {
|
||||||
|
return publicKeyUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicKeyUse(String publicKeyUse) {
|
||||||
|
this.publicKeyUse = publicKeyUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonAnyGetter
|
||||||
|
public Map<String, Object> getOtherClaims() {
|
||||||
|
return otherClaims;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonAnySetter
|
||||||
|
public void setOtherClaims(String name, Object value) {
|
||||||
|
otherClaims.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import static org.junit.Assert.assertTrue;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.gcube.common.keycloak.model.JSONWebKeySet;
|
||||||
import org.gcube.common.keycloak.model.ModelUtils;
|
import org.gcube.common.keycloak.model.ModelUtils;
|
||||||
import org.gcube.common.keycloak.model.PublishedRealmRepresentation;
|
import org.gcube.common.keycloak.model.PublishedRealmRepresentation;
|
||||||
import org.gcube.common.keycloak.model.TokenIntrospectionResponse;
|
import org.gcube.common.keycloak.model.TokenIntrospectionResponse;
|
||||||
|
@ -126,13 +127,26 @@ public class TestKeycloakClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test10QueryRealmInfo() throws Exception {
|
public void test10aQueryRealmInfo() throws Exception {
|
||||||
logger.info("*** [1.0] Start testing query realm info...");
|
logger.info("*** [1.0a] Start testing query realm info...");
|
||||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||||
PublishedRealmRepresentation realmInfo = client.getRealmInfo(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
PublishedRealmRepresentation realmInfo = client.getRealmInfo(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||||
|
|
||||||
logger.info("*** [1.0] Realm info public key PEM: {}", realmInfo.getPublicKeyPem());
|
logger.info("*** [1.0a] Realm info public key PEM: {}", realmInfo.getPublicKeyPem());
|
||||||
logger.info("*** [1.0] Realm info public key: {}", realmInfo.getPublicKey());
|
logger.info("*** [1.0a] Realm info public key: {}", realmInfo.getPublicKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test10bQueryRealmJWK() throws Exception {
|
||||||
|
logger.info("*** [1.0b] Start testing query realm JWK...");
|
||||||
|
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||||
|
JSONWebKeySet jsonWebKeySet = client
|
||||||
|
.getRealmJSONWebKeySet(client.getJWKEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT)));
|
||||||
|
|
||||||
|
for (int i = 0; i < jsonWebKeySet.getKeys().length; i++) {
|
||||||
|
logger.info("*** [1.0b] Realm JWK public key {} algorithm: {}", i,
|
||||||
|
jsonWebKeySet.getKeys()[i].getAlgorithm());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue