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.response.inbound.GXInboundResponse;
|
||||
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.PublishedRealmRepresentation;
|
||||
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
|
||||
public URL getIntrospectionEndpointURL(URL realmBaseURL) throws KeycloakClientException {
|
||||
logger.debug("Constructing introspection URL starting from base URL: {}", realmBaseURL);
|
||||
|
@ -184,11 +202,21 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
try {
|
||||
return JsonUtils.fromJson(ContentUtils.toByteArray(realmURL.openStream()),
|
||||
PublishedRealmRepresentation.class);
|
||||
|
||||
} catch (Exception 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
|
||||
public TokenResponse queryOIDCToken(String context, String clientId, String clientSecret)
|
||||
throws KeycloakClientException {
|
||||
|
|
|
@ -4,7 +4,7 @@ import java.net.URL;
|
|||
import java.util.List;
|
||||
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.TokenIntrospectionResponse;
|
||||
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 OPEN_ID_URI_PATH = "protocol/openid-connect";
|
||||
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 AVATAR_URI_PATH = "account-avatar";
|
||||
public final static String D4S_CONTEXT_HEADER_NAME = "X-D4Science-Context";
|
||||
|
@ -49,6 +50,15 @@ public interface KeycloakClient {
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -77,7 +87,7 @@ public interface KeycloakClient {
|
|||
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)
|
||||
*
|
||||
* @param realmURL the realm URL
|
||||
|
@ -86,6 +96,8 @@ public interface KeycloakClient {
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -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.util.Collections;
|
||||
|
||||
import org.gcube.common.keycloak.model.JSONWebKeySet;
|
||||
import org.gcube.common.keycloak.model.ModelUtils;
|
||||
import org.gcube.common.keycloak.model.PublishedRealmRepresentation;
|
||||
import org.gcube.common.keycloak.model.TokenIntrospectionResponse;
|
||||
|
@ -126,13 +127,26 @@ public class TestKeycloakClient {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test10QueryRealmInfo() throws Exception {
|
||||
logger.info("*** [1.0] Start testing query realm info...");
|
||||
public void test10aQueryRealmInfo() throws Exception {
|
||||
logger.info("*** [1.0a] Start testing query realm info...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
PublishedRealmRepresentation realmInfo = client.getRealmInfo(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
|
||||
logger.info("*** [1.0] 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 PEM: {}", realmInfo.getPublicKeyPem());
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue