Compare commits
19 Commits
Author | SHA1 | Date |
---|---|---|
Mauro Mugnaini | 45c52f1d56 | |
Mauro Mugnaini | a4c5de4e9e | |
Mauro Mugnaini | e0b165c491 | |
Mauro Mugnaini | 1cbfa034e7 | |
Mauro Mugnaini | 22013667d1 | |
Mauro Mugnaini | dfb35bad62 | |
Mauro Mugnaini | eabd708631 | |
Mauro Mugnaini | 899cf13afd | |
Mauro Mugnaini | 49af6590f7 | |
Mauro Mugnaini | ca0423cdf2 | |
Mauro Mugnaini | 027803b7e9 | |
Mauro Mugnaini | 0dbf1c0c95 | |
Mauro Mugnaini | 5938bf4af8 | |
Mauro Mugnaini | e339be5083 | |
Mauro Mugnaini | 7d98fbaa16 | |
Mauro Mugnaini | 8c009b9a8d | |
Mauro Mugnaini | 23f387f832 | |
Mauro Mugnaini | 726291ca55 | |
Mauro Mugnaini | 35c913db02 |
|
@ -2,6 +2,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|||
|
||||
# Changelog for "keycloak-client"
|
||||
|
||||
## [v2.1.0]
|
||||
- Added `token-exchange` support, also with `offline-token` scope, and methods to add extra headers during the OIDC token requests (#27099).
|
||||
- Added custom base URL set via factory (not automatically working cross environments) [#27234].
|
||||
- Added JWKS server configuration retrieval, realm's info (as `PublishedRealmRepresentation` JSON containing public key) and JWT digital signature verification by using the RSA public key of the realm on server. It uses the `jjwt` library by `io.jsonwebtoken` [#27340]
|
||||
|
||||
## [v2.0.0]
|
||||
- Removed the discovery functionality to be compatible with SmartGears.v4 and moved to the new library `keycloak-client-legacy-is` that will provide the backward compatibility. (#23478).
|
||||
- Fixed typo in `AccessToken` class for `setAccessToken(..)` method (#23654)
|
||||
|
|
31
pom.xml
31
pom.xml
|
@ -7,13 +7,12 @@
|
|||
<parent>
|
||||
<artifactId>maven-parent</artifactId>
|
||||
<groupId>org.gcube.tools</groupId>
|
||||
<version>1.1.0</version>
|
||||
<relativePath />
|
||||
<version>1.2.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.gcube.common</groupId>
|
||||
<artifactId>keycloak-client</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<version>2.1.0</version>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
@ -28,11 +27,20 @@
|
|||
</dependencyManagement>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</connection>
|
||||
<developerConnection>scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</developerConnection>
|
||||
<connection>
|
||||
scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</connection>
|
||||
<developerConnection>
|
||||
scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</developerConnection>
|
||||
<url>https://code-repo.d4science.org/gCubeSystem/${project.artifactId}</url>
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<jjwt.version>0.12.5</jjwt.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
|
@ -60,6 +68,19 @@
|
|||
<artifactId>gxJRS</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
package org.gcube.common.keycloak;
|
||||
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.ACCESS_TOKEN_TOKEN_TYPE;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.AUDIENCE_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.CLIENT_CREDENTIALS_GRANT_TYPE;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.CLIENT_ID_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.CLIENT_SECRET_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.GRANT_TYPE_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.OFFLINE_ACCESS_SCOPE;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.PASSWORD_GRANT_TYPE;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.PASSWORD_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.PERMISSION_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.REFRESH_TOKEN_GRANT_TYPE;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.REFRESH_TOKEN_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.REFRESH_TOKEN_TOKEN_TYPE;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.REQUESTED_TOKEN_TYPE_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.SCOPE_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.SUBJECT_TOKEN_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.SUBJECT_TOKEN_TYPE_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.TOKEN_EXCHANGE_GRANT_TYPE;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.TOKEN_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.UMA_TOKEN_GRANT_TYPE;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.PASSWORD_GRANT_TYPE;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.USERNAME_PARAMETER;
|
||||
import static org.gcube.common.keycloak.model.OIDCConstants.PASSWORD_PARAMETER;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
@ -20,6 +28,7 @@ import java.lang.reflect.InvocationTargetException;
|
|||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
|
@ -29,9 +38,14 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.AccessToken;
|
||||
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;
|
||||
import org.gcube.common.keycloak.model.TokenResponse;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -42,9 +56,22 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
protected static Logger logger = LoggerFactory.getLogger(KeycloakClient.class);
|
||||
|
||||
protected final static String AUTHORIZATION_HEADER = "Authorization";
|
||||
protected final static String D4S_CONTEXT_HEADER_NAME = "X-D4Science-Context";
|
||||
|
||||
public static final String BASE_URL = "https://url.d4science.org/auth/realms/";
|
||||
public static final String DEFAULT_BASE_URL = "https://url.d4science.org/auth/realms/";
|
||||
|
||||
private String customBaseURL = null;
|
||||
|
||||
public void setCustomBaseURL(String customBaseURL) {
|
||||
if (customBaseURL == null || customBaseURL.endsWith("/")) {
|
||||
this.customBaseURL = customBaseURL;
|
||||
} else {
|
||||
this.customBaseURL = customBaseURL += "/";
|
||||
}
|
||||
}
|
||||
|
||||
public String getCustomBaseURL() {
|
||||
return customBaseURL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getRealmBaseURL(String context) throws KeycloakClientException {
|
||||
|
@ -53,20 +80,36 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
|
||||
@Override
|
||||
public URL getRealmBaseURL(String context, String realm) throws KeycloakClientException {
|
||||
String urlString = BASE_URL + realm + "/";
|
||||
if (!context.startsWith(PROD_ROOT_SCOPE)) {
|
||||
String root = context.split("/")[1];
|
||||
urlString = urlString.replace("url", "url." + root.replaceAll("\\.", "-"));
|
||||
String realmBaseURLString = null;
|
||||
if (getCustomBaseURL() != null) {
|
||||
realmBaseURLString = getCustomBaseURL() + realm + "/";
|
||||
} else {
|
||||
realmBaseURLString = DEFAULT_BASE_URL + realm + "/";
|
||||
if (!context.startsWith(PROD_ROOT_SCOPE)) {
|
||||
String root = checkContext(context).split("/")[1];
|
||||
realmBaseURLString = realmBaseURLString.replace("url", "url." + root.replaceAll("\\.", "-"));
|
||||
}
|
||||
}
|
||||
try {
|
||||
return new URL(urlString);
|
||||
return new URL(realmBaseURLString);
|
||||
} catch (MalformedURLException e) {
|
||||
// That should be almost impossible
|
||||
logger.warn("Cannot create base URL from string: {}", urlString, e);
|
||||
logger.error("Cannot create base URL from string: {}", realmBaseURLString, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String checkContext(String context) {
|
||||
if (!context.startsWith("/")) {
|
||||
try {
|
||||
logger.trace("Context was provided in URL encoded form, decoding it");
|
||||
return URLDecoder.decode(context, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.error("Cannot URL decode 'context'", e);
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getTokenEndpointURL(URL realmBaseURL) throws KeycloakClientException {
|
||||
logger.debug("Constructing token endpoint URL starting from base URL: {}", realmBaseURL);
|
||||
|
@ -80,7 +123,24 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
logger.debug("Constructed token URL is: {}", tokenURL);
|
||||
return tokenURL;
|
||||
} catch (MalformedURLException e) {
|
||||
throw new KeycloakClientException("Cannot constructs toke URL from base URL: " + realmBaseURL, e);
|
||||
throw new KeycloakClientException("Cannot constructs token URL from base URL: " + realmBaseURL, e);
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +159,24 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
logger.debug("Constructed introspection URL is: {}", tokenURL);
|
||||
return tokenURL;
|
||||
} catch (MalformedURLException e) {
|
||||
throw new KeycloakClientException("Cannot constructs toke URL from base URL: " + realmBaseURL, e);
|
||||
throw new KeycloakClientException("Cannot constructs introspection URL from base URL: " + realmBaseURL, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getAvatarEndpointURL(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, AVATAR_URI_PATH);
|
||||
} else {
|
||||
tokenURL = new URL(realmBaseURL.toString() + "/" + AVATAR_URI_PATH);
|
||||
}
|
||||
logger.debug("Constructed avatar URL is: {}", tokenURL);
|
||||
return tokenURL;
|
||||
} catch (MalformedURLException e) {
|
||||
throw new KeycloakClientException("Cannot constructs avatar URL from base URL: " + realmBaseURL, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,11 +198,90 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublishedRealmRepresentation getRealmInfo(URL realmURL) throws KeycloakClientException {
|
||||
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 {
|
||||
|
||||
return queryOIDCTokenWithContext(context, clientId, clientSecret, null);
|
||||
return queryOIDCToken(context, clientId, clientSecret, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCToken(String context, String clientId, String clientSecret,
|
||||
Map<String, String> extraHeaders) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCToken(getTokenEndpointURL(getRealmBaseURL(context)), clientId, clientSecret, extraHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCToken(URL tokenURL, String clientId, String clientSecret)
|
||||
throws KeycloakClientException {
|
||||
|
||||
return queryOIDCToken(tokenURL, clientId, clientSecret, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCToken(URL tokenURL, String clientId, String clientSecret,
|
||||
Map<String, String> extraHeaders) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenWithContext(tokenURL, clientId, clientSecret, null, extraHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCToken(String context, String authorization) throws KeycloakClientException {
|
||||
return queryOIDCTokenWithContext(context, authorization, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCToken(URL tokenURL, String authorization) throws KeycloakClientException {
|
||||
return queryOIDCToken(tokenURL, authorization, (Map<String, String>) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCToken(String context, String authorization, Map<String, String> extraHeaders)
|
||||
throws KeycloakClientException {
|
||||
|
||||
return queryOIDCToken(getTokenEndpointURL(getRealmBaseURL(context)), authorization, extraHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCToken(URL tokenURL, String authorization, Map<String, String> extraHeaders)
|
||||
throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenWithContext(tokenURL, authorization, null, extraHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenOfUser(String context, String clientId, String clientSecret, String username,
|
||||
String password) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenOfUser(context, clientId, clientSecret, username, password, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenOfUser(String context, String clientId, String clientSecret, String username,
|
||||
String password, Map<String, String> extraHeaders) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenOfUserWithContext(context, clientId, clientSecret, username, password, null, extraHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -137,39 +293,70 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenOfUser(String context, String clientId, String clientSecret, String username,
|
||||
String password) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenOfUserWithContext(context, clientId, clientSecret, username, password, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(String context, String clientId, String clientSecret,
|
||||
String username, String password, String audience) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenOfUserWithContext(getTokenEndpointURL(getRealmBaseURL(context)), clientId, clientSecret,
|
||||
username, password, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCToken(URL tokenURL, String clientId, String clientSecret)
|
||||
public TokenResponse queryOIDCTokenWithContext(String context, String authorization, String audience)
|
||||
throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenWithContext(tokenURL, clientId, clientSecret, null);
|
||||
return queryOIDCTokenWithContext(getTokenEndpointURL(getRealmBaseURL(context)), authorization, audience);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenWithContext(URL tokenURL, String clientId, String clientSecret,
|
||||
String audience) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenWithContext(tokenURL, constructBasicAuthenticationHeader(clientId, clientSecret),
|
||||
audience);
|
||||
return queryOIDCTokenWithContext(tokenURL, clientId, clientSecret, audience, null);
|
||||
}
|
||||
|
||||
protected String constructBasicAuthenticationHeader(String clientId, String clientSecret) {
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenWithContext(String context, String clientId, String clientSecret,
|
||||
String audience, Map<String, String> extraHeaders) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenWithContext(getTokenEndpointURL(getRealmBaseURL(context)), clientId, clientSecret,
|
||||
audience, extraHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenWithContext(URL tokenURL, String clientId, String clientSecret, String audience,
|
||||
Map<String, String> extraHeaders) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenWithContext(tokenURL, constructBasicAuthenticationHeader(clientId, clientSecret),
|
||||
audience, extraHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenWithContext(String context, String authorization, String audience,
|
||||
Map<String, String> extraHeaders) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenWithContext(getTokenEndpointURL(getRealmBaseURL(context)), authorization, audience,
|
||||
extraHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenWithContext(URL tokenURL, String authorization, String audience)
|
||||
throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenWithContext(tokenURL, authorization, audience, (Map<String, String>) null);
|
||||
}
|
||||
|
||||
protected static String constructBasicAuthenticationHeader(String clientId, String clientSecret) {
|
||||
return "Basic " + Base64.getEncoder().encodeToString((clientId + ":" + clientSecret).getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(String context, String clientId, String clientSecret,
|
||||
String username, String password, String audience) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenOfUserWithContext(context, clientId, clientSecret, username, password, audience,
|
||||
(Map<String, String>) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(String context, String clientId, String clientSecret,
|
||||
String username, String password, String audience, Map<String, String> extraHeaders)
|
||||
throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenOfUserWithContext(getTokenEndpointURL(getRealmBaseURL(context)), clientId, clientSecret,
|
||||
username, password, audience, extraHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(String context, String authorization, String username,
|
||||
String password, String audience) throws KeycloakClientException {
|
||||
|
@ -179,36 +366,55 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCToken(String context, String authorization) throws KeycloakClientException {
|
||||
return queryOIDCTokenWithContext(context, authorization, null);
|
||||
}
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(String context, String authorization, String username,
|
||||
String password, String audience, Map<String, String> extraHeaders) throws KeycloakClientException {
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenWithContext(String context, String authorization, String audience)
|
||||
throws KeycloakClientException {
|
||||
return queryOIDCTokenWithContext(getTokenEndpointURL(getRealmBaseURL(context)), authorization, audience);
|
||||
return queryOIDCTokenOfUserWithContext(getTokenEndpointURL(getRealmBaseURL(context)), authorization, username,
|
||||
password, audience, extraHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String clientId, String clientSecret,
|
||||
String username, String password, String audience) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenOfUserWithContext(tokenURL, clientId, clientSecret, username, password, audience,
|
||||
(Map<String, String>) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String clientId, String clientSecret,
|
||||
String username, String password, String audience, Map<String, String> extraHeaders)
|
||||
throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenOfUserWithContext(tokenURL, constructBasicAuthenticationHeader(clientId, clientSecret),
|
||||
username, password, audience);
|
||||
username, password, audience, extraHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String authorization, String username,
|
||||
String password, String audience) throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenOfUserWithContext(tokenURL, authorization, username, password, audience,
|
||||
(Map<String, String>) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String authorization, String username,
|
||||
String password, String audience, Map<String, String> extraHeaders) throws KeycloakClientException {
|
||||
|
||||
Map<String, List<String>> params = new HashMap<>();
|
||||
params.put(GRANT_TYPE_PARAMETER, Arrays.asList(PASSWORD_GRANT_TYPE));
|
||||
params.put(USERNAME_PARAMETER, Arrays.asList(username));
|
||||
params.put(PASSWORD_PARAMETER, Arrays.asList(password));
|
||||
// params.put(SCOPE_PARAMETER, Arrays.asList("openid profile " + OFFLINE_ACCESS_SCOPE));
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
logger.debug("Adding authorization header as: {}", authorization);
|
||||
headers.put(AUTHORIZATION_HEADER, authorization);
|
||||
if (extraHeaders != null) {
|
||||
logger.debug("Adding provided extra headers: {}", extraHeaders);
|
||||
headers.putAll(extraHeaders);
|
||||
}
|
||||
|
||||
if (audience != null) {
|
||||
logger.debug("Adding d4s context header as: {}", audience);
|
||||
|
@ -219,12 +425,8 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCToken(URL tokenURL, String authorization) throws KeycloakClientException {
|
||||
return queryOIDCTokenWithContext(tokenURL, authorization, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenWithContext(URL tokenURL, String authorization, String audience)
|
||||
public TokenResponse queryOIDCTokenWithContext(URL tokenURL, String authorization, String audience,
|
||||
Map<String, String> extraHeaders)
|
||||
throws KeycloakClientException {
|
||||
logger.debug("Querying OIDC token from Keycloak server with URL: {}", tokenURL);
|
||||
|
||||
|
@ -257,7 +459,7 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
return queryUMAToken(tokenURL, constructBeareAuthenticationHeader(oidcTokenResponse), audience, permissions);
|
||||
}
|
||||
|
||||
protected String constructBeareAuthenticationHeader(TokenResponse oidcTokenResponse) {
|
||||
protected static String constructBeareAuthenticationHeader(TokenResponse oidcTokenResponse) {
|
||||
return "Bearer " + oidcTokenResponse.getAccessToken();
|
||||
}
|
||||
|
||||
|
@ -327,30 +529,47 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
protected TokenResponse performRequest(URL tokenURL, Map<String, String> headers, Map<String, List<String>> params)
|
||||
throws KeycloakClientException {
|
||||
|
||||
if (tokenURL == null) {
|
||||
return performRequest(TokenResponse.class, tokenURL, headers, params);
|
||||
}
|
||||
|
||||
protected <T> T performRequest(Class<T> returnObjectClass, URL url, Map<String, String> headers,
|
||||
Map<String, List<String>> params)
|
||||
throws KeycloakClientException {
|
||||
|
||||
if (url == null) {
|
||||
throw new KeycloakClientException("Token URL must be not null");
|
||||
}
|
||||
|
||||
if (!headers.containsKey(AUTHORIZATION_HEADER) || "".equals(headers.get(AUTHORIZATION_HEADER))) {
|
||||
throw new KeycloakClientException("Authorization must be not null nor empty");
|
||||
}
|
||||
// Constructing request object
|
||||
GXHTTPStringRequest request;
|
||||
try {
|
||||
String queryString = params.entrySet().stream()
|
||||
.flatMap(p -> p.getValue().stream().map(v -> p.getKey() + "=" + v))
|
||||
.reduce((p1, p2) -> p1 + "&" + p2).orElse("");
|
||||
String queryString = "";
|
||||
if (params != null) {
|
||||
queryString = params.entrySet().stream()
|
||||
.flatMap(p -> p.getValue().stream().map(v -> p.getKey() + "=" + v))
|
||||
.reduce((p1, p2) -> p1 + "&" + p2).orElse("");
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Params map is null");
|
||||
}
|
||||
}
|
||||
|
||||
logger.trace("Query string is {}", queryString);
|
||||
logger.trace("Query string is: {}", queryString);
|
||||
|
||||
request = GXHTTPStringRequest.newRequest(tokenURL.toString())
|
||||
request = GXHTTPStringRequest.newRequest(url.toString())
|
||||
.header("Content-Type", "application/x-www-form-urlencoded").withBody(queryString);
|
||||
|
||||
safeSetAsExternalCallForOldAPI(request);
|
||||
|
||||
logger.trace("Adding provided headers: {}", headers);
|
||||
for (String headerName : headers.keySet()) {
|
||||
request.header(headerName, headers.get(headerName));
|
||||
if (headers != null) {
|
||||
logger.trace("Adding provided headers: {}", headers);
|
||||
for (String headerName : headers.keySet()) {
|
||||
request.header(headerName, headers.get(headerName));
|
||||
}
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HTTP headers map is null");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new KeycloakClientException("Cannot construct the request object correctly", e);
|
||||
|
@ -359,12 +578,13 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
GXInboundResponse response;
|
||||
try {
|
||||
response = request.post();
|
||||
// TODO: Fill a bug ticket for the gxJRS lib for JSON responses in case of not 2XX code (e.g. 403 error with JSON details in this case).
|
||||
} catch (Exception e) {
|
||||
throw new KeycloakClientException("Cannot send request correctly", e);
|
||||
}
|
||||
if (response.isSuccessResponse()) {
|
||||
try {
|
||||
return response.tryConvertStreamedContentFromJson(TokenResponse.class);
|
||||
return response.tryConvertStreamedContentFromJson(returnObjectClass);
|
||||
} catch (Exception e) {
|
||||
throw new KeycloakClientException("Cannot construct token response object correctly", e);
|
||||
}
|
||||
|
@ -428,8 +648,7 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
|
||||
@Override
|
||||
public TokenResponse refreshToken(String context, String clientId, String clientSecret,
|
||||
String refreshTokenJWTString)
|
||||
throws KeycloakClientException {
|
||||
String refreshTokenJWTString) throws KeycloakClientException {
|
||||
|
||||
return refreshToken(getTokenEndpointURL(getRealmBaseURL(context)), clientId, clientSecret,
|
||||
refreshTokenJWTString);
|
||||
|
@ -453,47 +672,124 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
|
||||
logger.debug("Refreshing token from Keycloak server with URL: {}", tokenURL);
|
||||
|
||||
// Constructing request object
|
||||
GXHTTPStringRequest request;
|
||||
try {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put(GRANT_TYPE_PARAMETER, REFRESH_TOKEN_GRANT_TYPE);
|
||||
params.put(REFRESH_TOKEN_PARAMETER, refreshTokenJWTString);
|
||||
params.put(CLIENT_ID_PARAMETER, URLEncoder.encode(clientId, "UTF-8"));
|
||||
Map<String, List<String>> params = new HashMap<>();
|
||||
params.put(GRANT_TYPE_PARAMETER, Collections.singletonList(REFRESH_TOKEN_GRANT_TYPE));
|
||||
params.put(REFRESH_TOKEN_PARAMETER, Collections.singletonList(refreshTokenJWTString));
|
||||
params.put(CLIENT_ID_PARAMETER, Collections.singletonList(URLEncoder.encode(clientId, "UTF-8")));
|
||||
if (clientSecret != null && !"".equals(clientSecret)) {
|
||||
params.put(CLIENT_SECRET_PARAMETER, URLEncoder.encode(clientSecret, "UTF-8"));
|
||||
params.put(CLIENT_SECRET_PARAMETER,
|
||||
Collections.singletonList(URLEncoder.encode(clientSecret, "UTF-8")));
|
||||
}
|
||||
|
||||
String queryString = params.entrySet().stream()
|
||||
.map(p -> p.getKey() + "=" + p.getValue())
|
||||
.reduce((p1, p2) -> p1 + "&" + p2).orElse("");
|
||||
|
||||
request = GXHTTPStringRequest.newRequest(tokenURL.toString()).header("Content-Type",
|
||||
"application/x-www-form-urlencoded").withBody(queryString);
|
||||
|
||||
safeSetAsExternalCallForOldAPI(request);
|
||||
} catch (Exception e) {
|
||||
throw new KeycloakClientException("Cannot construct the request object correctly", e);
|
||||
return performRequest(tokenURL, null, params);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new KeycloakClientException("Cannot encode parameters", e);
|
||||
}
|
||||
|
||||
GXInboundResponse response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeTokenForAccessToken(String context, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException {
|
||||
|
||||
return exchangeTokenForAccessToken(getTokenEndpointURL(getRealmBaseURL(context)), oidcAccessToken, clientId,
|
||||
clientSecret, audience);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeTokenForAccessToken(URL tokenURL, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException {
|
||||
|
||||
return exchangeToken(tokenURL, oidcAccessToken, clientId, clientSecret, audience, ACCESS_TOKEN_TOKEN_TYPE,
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeTokenForRefreshToken(String context, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException {
|
||||
|
||||
return exchangeTokenForRefreshToken(getTokenEndpointURL(getRealmBaseURL(context)), oidcAccessToken, clientId,
|
||||
clientSecret, audience);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeTokenForRefreshToken(URL tokenURL, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException {
|
||||
|
||||
return exchangeToken(tokenURL, oidcAccessToken, clientId, clientSecret, audience, REFRESH_TOKEN_TOKEN_TYPE,
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeTokenForOfflineToken(String context, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws IllegalArgumentException, KeycloakClientException {
|
||||
|
||||
return exchangeTokenForOfflineToken(getTokenEndpointURL(getRealmBaseURL(context)), oidcAccessToken, clientId,
|
||||
clientSecret, audience);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeTokenForOfflineToken(URL tokenURL, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws IllegalArgumentException, KeycloakClientException {
|
||||
|
||||
AccessToken at = null;
|
||||
try {
|
||||
response = request.post();
|
||||
at = ModelUtils.getAccessTokenFrom(oidcAccessToken);
|
||||
} catch (Exception e) {
|
||||
throw new KeycloakClientException("Cannot send request correctly", e);
|
||||
throw new IllegalArgumentException("Impossible to parse the access token as JSON", e);
|
||||
}
|
||||
if (response.isSuccessResponse()) {
|
||||
try {
|
||||
return response.tryConvertStreamedContentFromJson(TokenResponse.class);
|
||||
} catch (Exception e) {
|
||||
throw new KeycloakClientException("Cannot construct token response object correctly", e);
|
||||
}
|
||||
} else {
|
||||
throw KeycloakClientException.create("Unable to refresh token", response.getHTTPCode(),
|
||||
response.getHeaderFields()
|
||||
.getOrDefault("content-type", Collections.singletonList("unknown/unknown")).get(0),
|
||||
response.getMessage());
|
||||
if (at.getScope().indexOf(OFFLINE_ACCESS_SCOPE) < 0) {
|
||||
logger.info("Token to be exchanged doesn't contain 'offline_token' within scopes");
|
||||
throw new IllegalArgumentException("Orignal access token doesn't contain the 'offline_token' scope");
|
||||
}
|
||||
return exchangeToken(tokenURL, oidcAccessToken, clientId, clientSecret, audience, REFRESH_TOKEN_TOKEN_TYPE,
|
||||
OFFLINE_ACCESS_SCOPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries from the OIDC server an exchanged token by using provided access token, for the given audience (context),
|
||||
* in URLEncoded form or not,
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the OIDC server
|
||||
* @param oidcAccessToken the auth token (the access token URLEncoded by the "Bearer " string)
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param audience the audience (context) where to request the issuing of the ticket (URLEncoded)
|
||||
* @param requestedTokenType the token type (e.g. <code>refresh</code>)
|
||||
* @param scope the scope, optional can be <code>null</code>
|
||||
* @return the issued exchanged token
|
||||
* @throws KeycloakClientException if an error occurs, inspect the exception for details
|
||||
*/
|
||||
protected TokenResponse exchangeToken(URL tokenURL, String oidcAccessToken, String clientId, String clientSecret,
|
||||
String audience, String requestedTokenType, String scope) throws KeycloakClientException {
|
||||
|
||||
if (audience == null || "".equals(audience)) {
|
||||
throw new KeycloakClientException("Audience must be not null nor empty");
|
||||
}
|
||||
|
||||
logger.debug("Exchanging token from Keycloak server with URL: {}", tokenURL);
|
||||
|
||||
Map<String, List<String>> params = new HashMap<>();
|
||||
params.put(SUBJECT_TOKEN_PARAMETER, Arrays.asList(oidcAccessToken));
|
||||
params.put(CLIENT_ID_PARAMETER, Arrays.asList(clientId));
|
||||
params.put(CLIENT_SECRET_PARAMETER, Arrays.asList(clientSecret));
|
||||
params.put(GRANT_TYPE_PARAMETER, Arrays.asList(TOKEN_EXCHANGE_GRANT_TYPE));
|
||||
params.put(SUBJECT_TOKEN_TYPE_PARAMETER, Arrays.asList(ACCESS_TOKEN_TOKEN_TYPE));
|
||||
params.put(REQUESTED_TOKEN_TYPE_PARAMETER, Arrays.asList(requestedTokenType));
|
||||
if (scope != null) {
|
||||
params.put(SCOPE_PARAMETER, Arrays.asList(scope));
|
||||
}
|
||||
|
||||
try {
|
||||
String audienceToSend = URLEncoder.encode(checkAudience(audience), "UTF-8");
|
||||
params.put(AUDIENCE_PARAMETER, Arrays.asList(audienceToSend));
|
||||
logger.trace("audience is {}", audienceToSend);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.error("Can't URL encode audience: {}", audience, e);
|
||||
}
|
||||
|
||||
return performRequest(tokenURL, null, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -522,44 +818,9 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
|
||||
logger.debug("Verifying access token against Keycloak server with URL: {}", introspectionURL);
|
||||
|
||||
// Constructing request object
|
||||
GXHTTPStringRequest request;
|
||||
try {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put(TOKEN_PARAMETER, accessTokenJWTString);
|
||||
|
||||
String queryString = params.entrySet().stream()
|
||||
.map(p -> p.getKey() + "=" + p.getValue())
|
||||
.reduce((p1, p2) -> p1 + "&" + p2).orElse("");
|
||||
|
||||
request = GXHTTPStringRequest.newRequest(introspectionURL.toString()).header("Content-Type",
|
||||
"application/x-www-form-urlencoded").withBody(queryString);
|
||||
|
||||
safeSetAsExternalCallForOldAPI(request);
|
||||
|
||||
request = request.header("Authorization", constructBasicAuthenticationHeader(clientId, clientSecret));
|
||||
} catch (Exception e) {
|
||||
throw new KeycloakClientException("Cannot construct the request object correctly", e);
|
||||
}
|
||||
|
||||
GXInboundResponse response;
|
||||
try {
|
||||
response = request.post();
|
||||
} catch (Exception e) {
|
||||
throw new KeycloakClientException("Cannot send request correctly", e);
|
||||
}
|
||||
if (response.isSuccessResponse()) {
|
||||
try {
|
||||
return response.tryConvertStreamedContentFromJson(TokenIntrospectionResponse.class);
|
||||
} catch (Exception e) {
|
||||
throw new KeycloakClientException("Cannot construct introspection response object correctly", e);
|
||||
}
|
||||
} else {
|
||||
throw KeycloakClientException.create("Unable to get token introspection response", response.getHTTPCode(),
|
||||
response.getHeaderFields()
|
||||
.getOrDefault("content-type", Collections.singletonList("unknown/unknown")).get(0),
|
||||
response.getMessage());
|
||||
}
|
||||
return performRequest(TokenIntrospectionResponse.class, introspectionURL,
|
||||
Collections.singletonMap("Authorization", constructBasicAuthenticationHeader(clientId, clientSecret)),
|
||||
Collections.singletonMap(TOKEN_PARAMETER, Collections.singletonList(accessTokenJWTString)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -590,4 +851,46 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getAvatarData(String context, TokenResponse tokenResponse) throws KeycloakClientException {
|
||||
return getAvatarData(getAvatarEndpointURL(getRealmBaseURL(context)), tokenResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getAvatarData(URL avatarURL, TokenResponse tokenResponse) throws KeycloakClientException {
|
||||
logger.debug("Getting user's avatar from URL: {}", avatarURL);
|
||||
try {
|
||||
GXHTTPStringRequest request;
|
||||
try {
|
||||
request = GXHTTPStringRequest.newRequest(avatarURL.toString());
|
||||
|
||||
safeSetAsExternalCallForOldAPI(request);
|
||||
|
||||
String authorization = constructBeareAuthenticationHeader(tokenResponse);
|
||||
logger.debug("Adding authorization header as: {}", authorization);
|
||||
request = request.header("Authorization", authorization);
|
||||
request = request.header("Accept", "image/png, image/gif");
|
||||
} catch (Exception e) {
|
||||
throw new KeycloakClientException("Cannot construct the request object correctly", e);
|
||||
}
|
||||
|
||||
GXInboundResponse response;
|
||||
try {
|
||||
response = request.get();
|
||||
} catch (Exception e) {
|
||||
throw new KeycloakClientException("Cannot send request correctly", e);
|
||||
}
|
||||
if (response.isSuccessResponse()) {
|
||||
return response.getStreamedContent();
|
||||
} else {
|
||||
throw KeycloakClientException.create("Unable to get avatar image data", response.getHTTPCode(),
|
||||
response.getHeaderFields()
|
||||
.getOrDefault("content-type", Collections.singletonList("unknown/unknown")).get(0),
|
||||
response.getMessage());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new KeycloakClientException("Error getting user's avatar data", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,10 @@ package org.gcube.common.keycloak;
|
|||
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -11,10 +14,14 @@ 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";
|
||||
|
||||
public static String DEFAULT_REALM = "d4science";
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Keycloak base {@link URL} for the given context and the default realm (<code>d4science</code>)
|
||||
*
|
||||
|
@ -23,7 +30,6 @@ public interface KeycloakClient {
|
|||
* @throws KeycloakClientException if something goes wrong discovering the endpoint URL
|
||||
*/
|
||||
URL getRealmBaseURL(String context) throws KeycloakClientException;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Keycloak base {@link URL} for the given context and in the given realm.
|
||||
|
@ -44,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.
|
||||
*
|
||||
|
@ -62,6 +77,27 @@ public interface KeycloakClient {
|
|||
*/
|
||||
URL computeIntrospectionEndpointURL(URL tokenEndpointURL) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Constructs the Keycloak <code>avatar</code> endpoint {@link URL} from the realm's base URL.
|
||||
*
|
||||
* @param realmBaseURL the realm's base URL to use
|
||||
* @return the Keycloak <code>avatar</code> endpoint URL
|
||||
* @throws KeycloakClientException if something goes wrong discovering the endpoint URL
|
||||
*/
|
||||
URL getAvatarEndpointURL(URL realmBaseURL) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return the configured realm info
|
||||
* @throws KeycloakClientException if something goes wrong getting realm info
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -74,45 +110,17 @@ public interface KeycloakClient {
|
|||
TokenResponse queryOIDCToken(String context, String clientId, String clientSecret) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the context's Keycloak server, by using provided clientId and client secret, reducing the audience to the requested one.
|
||||
* Queries an OIDC token from the context's Keycloak server, by using provided clientId and client secret.
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenWithContext(String context, String clientId, String clientSecret, String audience) throws KeycloakClientException;
|
||||
TokenResponse queryOIDCToken(String context, String clientId, String clientSecret, Map<String, String> extraHeaders) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUser(String context, String clientId, String clientSecret, String username, String password) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the Keycloak server, by using provided clientId and client secret and user's username and password, reducing the audience to the requested one.
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUserWithContext(String context, String clientId, String clientSecret, String username, String password, String audience) throws KeycloakClientException;
|
||||
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided clientId and client secret.
|
||||
*
|
||||
|
@ -125,35 +133,22 @@ public interface KeycloakClient {
|
|||
TokenResponse queryOIDCToken(URL tokenURL, String clientId, String clientSecret) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided clientId and client secret, reducing the audience to the requested one.
|
||||
* Queries an OIDC token from the Keycloak server, by using provided clientId and client secret.
|
||||
* Optionally extra HTTP headers can be provided to be used in the call.
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenWithContext(URL tokenURL, String clientId, String clientSecret, String audience) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password, , reducing the audience to the requested one.
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String clientId, String clientSecret, String username, String password, String audience) throws KeycloakClientException;
|
||||
TokenResponse queryOIDCToken(URL tokenURL, String clientId, String clientSecret, Map<String, String> extraHeaders) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided authorization.
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
|
@ -161,55 +156,189 @@ public interface KeycloakClient {
|
|||
TokenResponse queryOIDCToken(String context, String authorization) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided authorization, reducing the audience to the requested one.
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenWithContext(String context, String authorization, String audience) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
|
||||
* Queries an OIDC token from the Keycloak server, by using provided authorization.
|
||||
* Optionally extra HTTP headers can be provided to be used in the call.
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUserWithContext(String context, String authorization, String username, String password, String audience) throws KeycloakClientException;
|
||||
TokenResponse queryOIDCToken(String context, String authorization, Map<String, String> extraHeaders) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided authorization.
|
||||
*
|
||||
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
|
||||
* @param tokenURL the token endpoint {@link URL} of the OIDC server
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCToken(URL tokenURL, String authorization) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided authorization.
|
||||
* Optionally extra HTTP headers can be provided to be used in the call.
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the OIDC server
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCToken(URL tokenURL, String authorization, Map<String, String> extraHeaders) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the context's Keycloak server, by using provided clientId and client secret, reducing the audience to the requested one.
|
||||
*
|
||||
* The implementation uses the custom <code>X-D4Science-Context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenWithContext(String context, String clientId, String clientSecret, String audience)
|
||||
throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the context's Keycloak server, by using provided clientId and client secret, reducing the audience to the requested one.
|
||||
* Optionally extra HTTP headers can be provided to be used in the call.
|
||||
*
|
||||
* The implementation uses the custom <code>X-D4Science-Context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenWithContext(String context, String clientId, String clientSecret, String audience, Map<String, String> extraHeaders)
|
||||
throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided clientId and client secret, reducing the audience to the requested one.
|
||||
*
|
||||
* The implementation uses the custom <code>X-D4Science-Context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenWithContext(URL tokenURL, String clientId, String clientSecret, String audience)
|
||||
throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided clientId and client secret, reducing the audience to the requested one.
|
||||
* Optionally extra HTTP headers can be provided to be used in the call.
|
||||
*
|
||||
* The implementation uses the custom <code>X-D4Science-Context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenWithContext(URL tokenURL, String clientId, String clientSecret, String audience, Map<String, String> extraHeaders)
|
||||
throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided authorization, reducing the audience to the requested one.
|
||||
*
|
||||
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenWithContext(URL tokenURL, String authorization, String audience) throws KeycloakClientException;
|
||||
TokenResponse queryOIDCTokenWithContext(String context, String authorization, String audience)
|
||||
throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided authorization, reducing the audience to the requested one.
|
||||
* Optionally extra HTTP headers can be provided to be used in the call.
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenWithContext(String context, String authorization, String audience, Map<String, String> extraHeaders)
|
||||
throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided authorization, reducing the audience to the requested one.
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the OIDC server
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenWithContext(URL tokenURL, String authorization, String audience)
|
||||
throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided authorization, reducing the audience to the requested one.
|
||||
* Optionally extra HTTP headers can be provided to be used in the call.
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the OIDC server
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenWithContext(URL tokenURL, String authorization, String audience, Map<String, String> extraHeaders)
|
||||
throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
|
||||
*
|
||||
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUser(String context, String clientId, String clientSecret, String username,
|
||||
String password) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
|
||||
* Optionally extra HTTP headers can be provided to be used in the call.
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUser(String context, String clientId, String clientSecret, String username,
|
||||
String password, Map<String, String> extraHeaders) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
|
@ -217,7 +346,124 @@ public interface KeycloakClient {
|
|||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String authorization, String username, String password, String audience) throws KeycloakClientException;
|
||||
TokenResponse queryOIDCTokenOfUserWithContext(String context, String authorization, String username,
|
||||
String password, String audience) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the Keycloak server, by using provided clientId and client secret and user's username and password, reducing the audience to the requested one.
|
||||
*
|
||||
* The implementation uses the custom <code>X-D4Science-Context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUserWithContext(String context, String clientId, String clientSecret, String username,
|
||||
String password, String audience) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the Keycloak server, by using provided clientId and client secret and user's username and password, reducing the audience to the requested one.
|
||||
* Optionally extra HTTP headers can be provided to be used in the call.
|
||||
*
|
||||
* The implementation uses the custom <code>X-D4Science-Context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUserWithContext(String context, String clientId, String clientSecret,
|
||||
String username, String password, String audience, Map<String, String> extraHeaders) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password, reducing the audience to the requested one.
|
||||
*
|
||||
* The implementation uses the custom <code>X-D4Science-Context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String clientId, String clientSecret, String username,
|
||||
String password, String audience) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password, , reducing the audience to the requested one.
|
||||
* Optionally extra HTTP headers can be provided to be used in the call.
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String clientId, String clientSecret,
|
||||
String username, String password, String audience, Map<String, String> extraHeaders) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
|
||||
* Optionally extra HTTP headers can be provided to be used in the call.
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUserWithContext(String context, String authorization, String username,
|
||||
String password, String audience, Map<String, String> extraHeaders) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the OIDC server
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String authorization, String username, String password,
|
||||
String audience) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
|
||||
* Optionally extra HTTP headers can be provided to be used in the call.
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the OIDC server
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @param username the user's username
|
||||
* @param password the user's password
|
||||
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
|
||||
* @param extraHeaders extra HTTP headers to add to the request
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String authorization, String username,
|
||||
String password, String audience, Map<String, String> extraHeaders) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an UMA token from the Keycloak server, by using provided authorization, for the given audience (context),
|
||||
|
@ -237,7 +483,7 @@ public interface KeycloakClient {
|
|||
* Queries an UMA token from the Keycloak server, by using provided authorization, for the given audience (context),
|
||||
* in URLEncoded form or not, and optionally a list of permissions.
|
||||
*
|
||||
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
|
||||
* @param tokenURL the token endpoint {@link URL} of the OIDC server
|
||||
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
|
||||
* @param audience the audience (context) where to request the issuing of the ticket (URLEncoded)
|
||||
* @param permissions a list of permissions, can be <code>null</code>
|
||||
|
@ -252,7 +498,7 @@ public interface KeycloakClient {
|
|||
* for the given audience (context), in URLEncoded form or not, and optionally a list of permissions.
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param tokenResponse the previously issued token as {@link TokenResponse} object
|
||||
* @param oidcTokenResponse the previously issued token as {@link TokenResponse} object
|
||||
* @param audience the audience (context) where to request the issuing of the ticket
|
||||
* @param permissions a list of permissions, can be <code>null</code>
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
|
@ -265,8 +511,8 @@ public interface KeycloakClient {
|
|||
* Queries an UMA token from the Keycloak server, by using access-token provided by the {@link TokenResponse} object
|
||||
* for the given audience (context), in URLEncoded form or not, and optionally a list of permissions.
|
||||
*
|
||||
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
|
||||
* @param tokenResponse the previously issued token as {@link TokenResponse} object
|
||||
* @param tokenURL the token endpoint {@link URL} of the OIDC server
|
||||
* @param oidcTokenResponse the previously issued token as {@link TokenResponse} object
|
||||
* @param audience the audience (context) where to request the issuing of the ticket
|
||||
* @param permissions a list of permissions, can be <code>null</code>
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
|
@ -326,7 +572,7 @@ public interface KeycloakClient {
|
|||
* Client id will be read from "issued for" access token's claim and client secret will be not sent.
|
||||
* <br><b>NOTE</b>: For <code>public</code> clients types only.
|
||||
*
|
||||
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
|
||||
* @param tokenURL the token endpoint {@link URL} of the OIDC server
|
||||
* @param tokenResponse the previously issued token as {@link TokenResponse} object
|
||||
* @return the refreshed token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the refresh query
|
||||
|
@ -351,7 +597,7 @@ public interface KeycloakClient {
|
|||
* Refreshes a previously issued token from the Keycloak server using the refresh token JWT encoded string in the
|
||||
* token response object and the provided client id and secret.
|
||||
*
|
||||
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
|
||||
* @param tokenURL the token endpoint {@link URL} of the OIDC server
|
||||
* @param clientId the requestor client id, may be <code>null</code> and in this case will be take from the access token "issued for" claim
|
||||
* @param clientSecret the requestor client secret, may be <code>null</code> for non-confidential clients
|
||||
* @param tokenResponse the previously issued token as {@link TokenResponse} object
|
||||
|
@ -379,7 +625,7 @@ public interface KeycloakClient {
|
|||
* Refreshes a previously issued token from the Keycloak server by using the client id and secret
|
||||
* and the refresh token JWT encoded string obtained with the access token in the previous token response.
|
||||
*
|
||||
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
|
||||
* @param tokenURL the token endpoint {@link URL} of the OIDC server
|
||||
* @param clientId the requestor client id
|
||||
* @param clientSecret the requestor client secret, may be <code>null</code> for non-confidential clients
|
||||
* @param refreshTokenJWTString the previously issued refresh token JWT string
|
||||
|
@ -389,6 +635,94 @@ public interface KeycloakClient {
|
|||
TokenResponse refreshToken(URL tokenURL, String clientId, String clientSecret, String refreshTokenJWTString)
|
||||
throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Exchanges a token for another access token for a specific client and a specific audience
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param oidcAccessToken the original access token to exchange
|
||||
* @param clientId the authorized client's id
|
||||
* @param clientSecret the authorized client's secret
|
||||
* @param audience the requested token audience
|
||||
* @return the exchanged token response
|
||||
* @throws KeycloakClientException if an error occurs during the exchange
|
||||
*/
|
||||
TokenResponse exchangeTokenForAccessToken(String context, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Exchanges a token for another access token for a specific client and a specific audience
|
||||
*
|
||||
* @param tokenURL the token endpoint URL
|
||||
* @param oidcAccessToken the original access token to exchange
|
||||
* @param clientId the authorized client's id
|
||||
* @param clientSecret the authorized client's secret
|
||||
* @param audience the requested token audience
|
||||
* @return the exchanged token response
|
||||
* @throws KeycloakClientException if an error occurs during the exchange
|
||||
*/
|
||||
TokenResponse exchangeTokenForAccessToken(URL tokenURL, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Exchanges a token for another access and a refresh tokens for a specific client and a specific audience
|
||||
*
|
||||
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
|
||||
* @param oidcAccessToken the original access token to exchange
|
||||
* @param clientId the authorized client's id
|
||||
* @param clientSecret the authorized client's secret
|
||||
* @param audience the requested token audience
|
||||
* @return the exchanged token response
|
||||
* @throws KeycloakClientException if an error occurs during the exchange
|
||||
*/
|
||||
TokenResponse exchangeTokenForRefreshToken(String context, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Exchanges a token for another access and a refresh tokens for a specific client and a specific audience
|
||||
*
|
||||
* @param tokenURL the token endpoint URL
|
||||
* @param oidcAccessToken the original access token to exchange
|
||||
* @param clientId the authorized client's id
|
||||
* @param clientSecret the authorized client's secret
|
||||
* @param audience the requested token audience
|
||||
* @return the exchanged token response
|
||||
* @throws KeycloakClientException if an error occurs during the exchange
|
||||
*/
|
||||
TokenResponse exchangeTokenForRefreshToken(URL tokenURL, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Exchanges a token for another access and an offline refresh tokens for a specific client and a specific audience
|
||||
* The refresh token will be of the offline type only if the original token has the <code>offline_access</code> within its scopes
|
||||
*
|
||||
* @param tokenURL the token endpoint URL
|
||||
* @param oidcAccessToken the original access token to exchange
|
||||
* @param clientId the authorized client's id
|
||||
* @param clientSecret the authorized client's secret
|
||||
* @param audience the requested token audience
|
||||
* @return the exchanged token response
|
||||
* @throws IllegalArgumentException if the original token does'nt contains the <code>offline_access</code> scope within its scopes or if is impossible to parse the access token as JSON
|
||||
* @throws KeycloakClientException if an error occurs during the exchange
|
||||
*/
|
||||
TokenResponse exchangeTokenForOfflineToken(String context, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws IllegalArgumentException, KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Exchanges a token for another access and an offline refresh tokens for a specific client and a specific audience
|
||||
* The refresh token will be of the offline type only if the original token has the scope <code>offline_access</code> within its scopes
|
||||
*
|
||||
* @param tokenURL the token endpoint URL
|
||||
* @param oidcAccessToken the original access token to exchange
|
||||
* @param clientId the authorized client's id
|
||||
* @param clientSecret the authorized client's secret
|
||||
* @param audience the requested token audience
|
||||
* @return the exchanged token response
|
||||
* @throws IllegalArgumentException if the original token does'nt contains the <code>offline_access</code> scope within its scopes or if is impossible to parse the access token as JSON
|
||||
* @throws KeycloakClientException if an error occurs during the exchange
|
||||
*/
|
||||
TokenResponse exchangeTokenForOfflineToken(URL tokenURL, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws IllegalArgumentException, KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Introspects an access token against the Keycloak server.
|
||||
*
|
||||
|
@ -441,4 +775,8 @@ public interface KeycloakClient {
|
|||
boolean isAccessTokenVerified(URL introspectionURL, String clientId, String clientSecret,
|
||||
String accessTokenJWTString) throws KeycloakClientException;
|
||||
|
||||
byte[] getAvatarData(String context, TokenResponse tokenResponse) throws KeycloakClientException;
|
||||
|
||||
byte[] getAvatarData(URL avatarURL, TokenResponse tokenResponse) throws KeycloakClientException;
|
||||
|
||||
}
|
|
@ -7,9 +7,32 @@ public class KeycloakClientFactory {
|
|||
|
||||
protected static final Logger logger = LoggerFactory.getLogger(KeycloakClientFactory.class);
|
||||
|
||||
protected static String CUSTOM_BASE_URL;
|
||||
|
||||
public static void setCustomBaseURL(String customBaseURL) {
|
||||
if (customBaseURL != null) {
|
||||
logger.info("Setting custom base URL static value to {}", customBaseURL);
|
||||
} else {
|
||||
logger.info("Removing custom base URL static value");
|
||||
}
|
||||
if (customBaseURL == null || customBaseURL.endsWith("/")) {
|
||||
CUSTOM_BASE_URL = customBaseURL;
|
||||
} else {
|
||||
CUSTOM_BASE_URL = customBaseURL += "/";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getCustomBaseURL() {
|
||||
return CUSTOM_BASE_URL;
|
||||
}
|
||||
|
||||
public static KeycloakClient newInstance() {
|
||||
logger.debug("Instantiating a new keycloak client instance");
|
||||
return new DefaultKeycloakClient();
|
||||
DefaultKeycloakClient newInstance = new DefaultKeycloakClient();
|
||||
if (getCustomBaseURL() != null) {
|
||||
newInstance.setCustomBaseURL(CUSTOM_BASE_URL);
|
||||
}
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* 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.io.Serializable;
|
||||
|
@ -10,6 +26,9 @@ import java.util.Set;
|
|||
import org.gcube.com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
*/
|
||||
public class AccessToken extends IDToken {
|
||||
|
||||
private static final long serialVersionUID = 6364784008775737335L;
|
||||
|
@ -156,4 +175,12 @@ public class AccessToken extends IDToken {
|
|||
this.trustedCertificates = trustedCertificates;
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,26 @@
|
|||
/*
|
||||
* 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:bill@burkecentral.com">Bill Burke</a>
|
||||
*/
|
||||
public class AddressClaimSet {
|
||||
|
||||
public static final String FORMATTED = "formatted";
|
||||
|
|
|
@ -1,7 +1,26 @@
|
|||
/*
|
||||
* 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:bill@burkecentral.com">Bill Burke</a>
|
||||
*/
|
||||
public class IDToken extends JsonWebToken {
|
||||
|
||||
private static final long serialVersionUID = 8406175387651749097L;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -15,6 +15,9 @@ import org.gcube.common.keycloak.model.util.StringOrArrayDeserializer;
|
|||
import org.gcube.common.keycloak.model.util.StringOrArraySerializer;
|
||||
import org.gcube.common.keycloak.model.util.Time;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
*/
|
||||
public class JsonWebToken implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8136409077130940942L;
|
||||
|
@ -80,7 +83,7 @@ public class JsonWebToken implements Serializable {
|
|||
/**
|
||||
* Tests that the token is not expired and is not-before.
|
||||
*
|
||||
* @return
|
||||
* @return <code>true</code> if is not expired and is not-before
|
||||
*/
|
||||
@JsonIgnore
|
||||
public boolean isActive() {
|
||||
|
@ -98,6 +101,7 @@ public class JsonWebToken implements Serializable {
|
|||
|
||||
/**
|
||||
* Set issuedAt to the current time
|
||||
* @return the token itself
|
||||
*/
|
||||
@JsonIgnore
|
||||
public JsonWebToken issuedNow() {
|
||||
|
@ -183,7 +187,7 @@ public class JsonWebToken implements Serializable {
|
|||
/**
|
||||
* OAuth client the token was issued for.
|
||||
*
|
||||
* @return
|
||||
* @return the issued for vale
|
||||
*/
|
||||
public String getIssuedFor() {
|
||||
return issuedFor;
|
||||
|
@ -197,7 +201,7 @@ public class JsonWebToken implements Serializable {
|
|||
/**
|
||||
* This is a map of any other claims and data that might be in the IDToken. Could be custom claims set up by the auth server
|
||||
*
|
||||
* @return
|
||||
* @return the object's other claims
|
||||
*/
|
||||
@JsonAnyGetter
|
||||
public Map<String, Object> getOtherClaims() {
|
||||
|
|
|
@ -1,16 +1,36 @@
|
|||
package org.gcube.common.keycloak.model;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.gcube.com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import org.gcube.com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.gcube.com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.gcube.com.fasterxml.jackson.databind.ObjectWriter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.JwtParser;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.io.DeserializationException;
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mauro.mugnaini@nubisware.com">Mauro Mugnaini</a>
|
||||
*/
|
||||
public class ModelUtils {
|
||||
|
||||
protected static final Logger logger = LoggerFactory.getLogger(ModelUtils.class);
|
||||
|
@ -37,6 +57,80 @@ public class ModelUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link RSAPublicKey} instance from its string PEM representation
|
||||
*
|
||||
* @param publicKeyPem the public key PEM string
|
||||
* @return the RSA public key
|
||||
* @throws Exception if it's not possible to create the RSA public key from the PEM string
|
||||
*/
|
||||
public static RSAPublicKey createRSAPublicKey(String publicKeyPem) throws Exception {
|
||||
return (RSAPublicKey) createPublicKey(publicKeyPem, "RSA");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link PublicKey} instance from its string PEM representation
|
||||
*
|
||||
* @param publicKeyPem the public key PEM string
|
||||
* @param algorithm the key type (e.g. RSA)
|
||||
* @return the public key
|
||||
* @throws Exception if it's not possible to create the public key from the PEM string
|
||||
*/
|
||||
public static PublicKey createPublicKey(String publicKeyPem, String algorithm) throws Exception {
|
||||
try {
|
||||
String publicKey = publicKeyPem.replaceFirst("-----BEGIN (.*)-----\n", "");
|
||||
publicKey = publicKey.replaceFirst("-----END (.*)-----", "");
|
||||
publicKey = publicKey.replaceAll("\r\n", "");
|
||||
publicKey = publicKey.replaceAll("\n", "");
|
||||
|
||||
byte[] encoded = Base64.getDecoder().decode(publicKey);
|
||||
KeyFactory kf = KeyFactory.getInstance(algorithm);
|
||||
return kf.generatePublic(new X509EncodedKeySpec(encoded));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot create public key from PEM string", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the token validity
|
||||
*
|
||||
* @param token the base64 JWT token string
|
||||
* @param publicKey the realm's public key on server
|
||||
* @return <code>true</code> if the token is valid, <code>false</code> otherwise
|
||||
* @throws Exception if an error occurs constructing the verifier
|
||||
*/
|
||||
public static boolean isValid(String token, PublicKey publicKey) throws Exception {
|
||||
return isValid(token, publicKey, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the token validity
|
||||
*
|
||||
* @param token the base64 JWT token string
|
||||
* @param publicKey the public key to use for verification
|
||||
* @param checkExpiration if <code>false</code> token expiration check is disabled
|
||||
* @return <code>true</code> if the token is valid, <code>false</code> otherwise
|
||||
* @throws Exception if an error occurs constructing the verifier
|
||||
*/
|
||||
public static boolean isValid(String token, PublicKey publicKey, boolean checkExpiration) throws Exception {
|
||||
JwtParser jwtParser = Jwts.parser().json(new GcubeJacksonDeserializer()).verifyWith(publicKey).build();
|
||||
try {
|
||||
jwtParser.parse(token);
|
||||
return true;
|
||||
} catch (ExpiredJwtException e) {
|
||||
// This is OK because expiration check is after the signature validation in the implementation
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("JWT is expired: {}", e.getMessage());
|
||||
}
|
||||
return !checkExpiration;
|
||||
} catch (Exception e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("JWT is not verified: {}", e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAccessTokenPayloadJSONStringFrom(TokenResponse tokenResponse) throws Exception {
|
||||
return getAccessTokenPayloadJSONStringFrom(tokenResponse, true);
|
||||
}
|
||||
|
@ -146,4 +240,23 @@ public class ModelUtils {
|
|||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class GcubeJacksonDeserializer implements Deserializer<Map<String,?>> {
|
||||
|
||||
@Override
|
||||
public Map<String, ?> deserialize(byte[] bytes) throws DeserializationException {
|
||||
return deserialize(new InputStreamReader(new ByteArrayInputStream(bytes)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ?> deserialize(Reader reader) throws DeserializationException {
|
||||
try {
|
||||
return new ObjectMapper().readValue(reader, new TypeReference<HashMap<String,Object>>() {});
|
||||
} catch (IOException e) {
|
||||
throw new DeserializationException("Cannot deserialize JSON with GCube Jackson", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
package org.gcube.common.keycloak.model;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mauro.mugnaini@nubisware.com">Mauro Mugnaini</a>
|
||||
*/
|
||||
public class OIDCConstants {
|
||||
|
||||
public static final String PERMISSION_PARAMETER = "permission";
|
||||
|
@ -7,6 +10,7 @@ public class OIDCConstants {
|
|||
public static final String SCOPE_PARAMETER = "scope";
|
||||
public static final String CLIENT_CREDENTIALS_GRANT_TYPE = "client_credentials";
|
||||
public static final String UMA_TOKEN_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:uma-ticket";
|
||||
public static final String TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
|
||||
public static final String AUDIENCE_PARAMETER = "audience";
|
||||
public static final String REFRESH_TOKEN_GRANT_TYPE = "refresh_token";
|
||||
public static final String REFRESH_TOKEN_PARAMETER = "refresh_token";
|
||||
|
@ -16,5 +20,11 @@ public class OIDCConstants {
|
|||
public static final String PASSWORD_GRANT_TYPE = "password";
|
||||
public static final String USERNAME_PARAMETER = "username";
|
||||
public static final String PASSWORD_PARAMETER = "password";
|
||||
public static final String SUBJECT_TOKEN_PARAMETER = "subject_token";
|
||||
public static final String SUBJECT_TOKEN_TYPE_PARAMETER = "subject_token_type";
|
||||
public static final String REQUESTED_TOKEN_TYPE_PARAMETER = "requested_token_type";
|
||||
public static final String ACCESS_TOKEN_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
|
||||
public static final String REFRESH_TOKEN_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:refresh_token";
|
||||
public static final String OFFLINE_ACCESS_SCOPE = "offline_access";
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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.security.interfaces.RSAPublicKey;
|
||||
|
||||
import org.gcube.com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @author (modified by) <a href="mailto:mauro.mugnaini@nubisware.com">Mauro Mugnaini</a>
|
||||
*/
|
||||
public class PublishedRealmRepresentation {
|
||||
|
||||
protected String realm;
|
||||
|
||||
@JsonProperty("public_key")
|
||||
protected String publicKeyPem;
|
||||
|
||||
@JsonProperty("token-service")
|
||||
protected String tokenServiceUrl;
|
||||
|
||||
@JsonProperty("account-service")
|
||||
protected String accountServiceUrl;
|
||||
|
||||
@JsonProperty("tokens-not-before")
|
||||
protected int notBefore;
|
||||
|
||||
@JsonIgnore
|
||||
protected volatile transient RSAPublicKey publicKey;
|
||||
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public void setRealm(String realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public String getPublicKeyPem() {
|
||||
return publicKeyPem;
|
||||
}
|
||||
|
||||
public void setPublicKeyPem(String publicKeyPem) {
|
||||
this.publicKeyPem = publicKeyPem;
|
||||
this.publicKey = null;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public RSAPublicKey getPublicKey() {
|
||||
if (publicKey != null)
|
||||
return publicKey;
|
||||
if (publicKeyPem != null) {
|
||||
try {
|
||||
publicKey = ModelUtils.createRSAPublicKey(publicKeyPem);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public void setPublicKey(RSAPublicKey publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
// this.publicKeyPem = PemUtils.encodeKey(publicKey);
|
||||
}
|
||||
|
||||
public String getTokenServiceUrl() {
|
||||
return tokenServiceUrl;
|
||||
}
|
||||
|
||||
public void setTokenServiceUrl(String tokenServiceUrl) {
|
||||
this.tokenServiceUrl = tokenServiceUrl;
|
||||
}
|
||||
|
||||
public String getAccountServiceUrl() {
|
||||
return accountServiceUrl;
|
||||
}
|
||||
|
||||
public void setAccountServiceUrl(String accountServiceUrl) {
|
||||
this.accountServiceUrl = accountServiceUrl;
|
||||
}
|
||||
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(int notBefore) {
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,24 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
*/
|
||||
public class RefreshToken extends AccessToken {
|
||||
|
||||
private static final long serialVersionUID = 2646534143077862960L;
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* 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.List;
|
||||
|
@ -5,6 +21,9 @@ import java.util.List;
|
|||
import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.gcube.common.keycloak.model.idm.authorization.Permission;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class TokenIntrospectionResponse extends JsonWebToken {
|
||||
|
||||
private static final long serialVersionUID = -3105799239959636906L;
|
||||
|
|
|
@ -10,6 +10,9 @@ import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mauro.mugnaini@nubisware.com">Mauro Mugnaini</a>
|
||||
*/
|
||||
public class TokenResponse implements Serializable {
|
||||
|
||||
protected static Logger logger = LoggerFactory.getLogger(TokenResponse.class);
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* 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;
|
||||
|
@ -295,7 +311,7 @@ public class UserInfo {
|
|||
/**
|
||||
* This is a map of any other claims and data that might be in the UserInfo. Could be custom claims set up by the auth server
|
||||
*
|
||||
* @return
|
||||
* @return the object's other claims
|
||||
*/
|
||||
@JsonAnyGetter
|
||||
public Map<String, Object> getOtherClaims() {
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* 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.idm.authorization;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
@ -9,6 +25,9 @@ import org.gcube.com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|||
import org.gcube.com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Permission {
|
||||
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
|
@ -1,13 +1,32 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class Time {
|
||||
|
||||
private static int offset;
|
||||
|
||||
/**
|
||||
* Returns current time in seconds adjusted by adding {@link #offset) seconds.
|
||||
* Returns current time in seconds adjusted by adding {@link #offset} seconds.
|
||||
* @return see description
|
||||
*/
|
||||
public static int currentTime() {
|
||||
|
@ -15,7 +34,7 @@ public class Time {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns current time in milliseconds adjusted by adding {@link #offset) seconds.
|
||||
* Returns current time in milliseconds adjusted by adding {@link #offset} seconds.
|
||||
* @return see description
|
||||
*/
|
||||
public static long currentTimeMillis() {
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
package org.gcube.common.keycloak;
|
||||
|
||||
import java.net.URL;
|
||||
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;
|
||||
import org.gcube.common.keycloak.model.TokenResponse;
|
||||
import org.junit.After;
|
||||
|
@ -23,7 +28,11 @@ public class TestKeycloakClient {
|
|||
protected static final String DEV_BASE_URL = "https://url.gcube.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 DEV_AVATAR_ENDPOINT = DEV_BASE_URL + "/account-avatar";
|
||||
|
||||
protected static final String ACCOUNTS_DEV_INTROSPECTION_ENDPOINT = "https://accounts.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token/introspect";
|
||||
|
||||
protected static final String TOKEN_RESTRICTION_VRE_CONTEXT = "%2Fgcube%2Fdevsec%2FCCP";
|
||||
protected static final String CLIENT_ID = "keycloak-client-unit-test";
|
||||
protected static final String CLIENT_SECRET = "ebf6f82e-9511-408e-8321-203081e472d8";
|
||||
protected static final String TEST_AUDIENCE = "conductor-server";
|
||||
|
@ -32,38 +41,41 @@ public class TestKeycloakClient {
|
|||
protected static final String OLD_OIDC_ACCESS_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSSklZNEpoNF9qdDdvNmREY0NlUDFfS1l0akcxVExXVW9oMkQ2Tzk1bFNBIn0.eyJleHAiOjE2NTI5Nzk4NDUsImlhdCI6MTY1Mjk3OTU0NSwianRpIjoiMzQ2MjgwMWItODg4NS00YTM4LWJkNDUtNWExM2U1MGE5MGU5IiwiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5kZXYuZDRzY2llbmNlLm9yZy9hdXRoL3JlYWxtcy9kNHNjaWVuY2UiLCJhdWQiOlsiJTJGZ2N1YmUiLCIlMkZnY3ViZSUyRmRldnNlYyUyRmRldlZSRSIsImFjY291bnQiXSwic3ViIjoiYTQ3ZGZlMTYtYjRlZC00NGVkLWExZDktOTdlY2Q1MDQzNjBjIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoia2V5Y2xvYWstY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6ImQ4MDk3MDBmLWEyNDUtNDI3Zi1hYzhjLTQxYjFkZDNkYTQ3MCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsIkluZnJhc3RydWN0dXJlLUNsaWVudCIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiJTJGZ2N1YmUiOnsicm9sZXMiOlsiTWVtYmVyIl19LCJrZXljbG9hay1jbGllbnQiOnsicm9sZXMiOlsidW1hX3Byb3RlY3Rpb24iXX0sIiUyRmdjdWJlJTJGZGV2c2VjJTJGZGV2VlJFIjp7InJvbGVzIjpbIk1lbWJlciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SWQiOiJrZXljbG9hay1jbGllbnQiLCJjbGllbnRIb3N0IjoiOTMuNjYuMTg1Ljc1IiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQta2V5Y2xvYWstY2xpZW50IiwiY2xpZW50QWRkcmVzcyI6IjkzLjY2LjE4NS43NSJ9.FQu4ox2HWeqeaY7nHYVGeJVpkJOcASfOb8tbOUeG-GB6sMjRB2S8PjLLaw63r_c42yxKszP04XdxGqIWqXTtoD9QCiUHTT5yJTkIpio4tMMGHth9Fbx-9dwk0yy_IFi1_OsCvZFmOQRdjMuUkj1lSqslCzAw-2E5q1Zt415-au5pEVJYNTFqIsG72ChJwh6eq1Dh1XBy8krb7YVPQyIwxO_awgAYO5hbsdvXYlRfCrnB38kk2V6-CQ-XYoL1m7xIB-gjhKCiFvDmmntQSRCZFgb0qi8eOmh9FdzPxZgx7yPJwAAj17dS4B_gz9FpZBVciNzpA6Lf4P2bqvoD9-R6ow";
|
||||
protected static final String OLD_UMA_ACCESS_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSSklZNEpoNF9qdDdvNmREY0NlUDFfS1l0akcxVExXVW9oMkQ2Tzk1bFNBIn0.eyJleHAiOjE2NTI5ODA0NzgsImlhdCI6MTY1Mjk4MDE3OCwianRpIjoiNjBkNzU3MGMtZmQxOC00NGQ1LTg1MzUtODhlMmFmOGQ1ZTgwIiwiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5kZXYuZDRzY2llbmNlLm9yZy9hdXRoL3JlYWxtcy9kNHNjaWVuY2UiLCJhdWQiOiJjb25kdWN0b3Itc2VydmVyIiwic3ViIjoiYTQ3ZGZlMTYtYjRlZC00NGVkLWExZDktOTdlY2Q1MDQzNjBjIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoia2V5Y2xvYWstY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjI3NDUyN2M5LWNkZjMtNGM2Yi1iNTUxLTFmMTRkZGE5ZGVlZiIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiSW5mcmFzdHJ1Y3R1cmUtQ2xpZW50Il19LCJhdXRob3JpemF0aW9uIjp7InBlcm1pc3Npb25zIjpbeyJzY29wZXMiOlsiZ2V0Il0sInJzaWQiOiIyNDlmZDQ2OS03OWM1LTRiODUtYjE5NS1mMjliM2ViNjAzNDUiLCJyc25hbWUiOiJtZXRhZGF0YSJ9LHsic2NvcGVzIjpbImdldCIsInN0YXJ0IiwidGVybWluYXRlIl0sInJzaWQiOiJhNmYzZWFkZS03NDA0LTRlNWQtOTA3MC04MDBhZGI1YWFjNGUiLCJyc25hbWUiOiJ3b3JrZmxvdyJ9LHsicnNpZCI6IjFiNmMwMGI3LTkxMzktNGVhYS1hYWM3LTIwMjMxZmVlMDVhNSIsInJzbmFtZSI6IkRlZmF1bHQgUmVzb3VyY2UifV19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJjbGllbnRJZCI6ImtleWNsb2FrLWNsaWVudCIsImNsaWVudEhvc3QiOiI5My42Ni4xODUuNzUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1rZXljbG9hay1jbGllbnQiLCJjbGllbnRBZGRyZXNzIjoiOTMuNjYuMTg1Ljc1In0.Hh62E56R-amHwoDPFQEylMvrvmNzWnC_4bDI7_iQYAPJ5YzCNH9d7zcdGaQ96kRmps_JRc2Giv_1W9kYorOhlXl-5QLDrSoqrqFxrNpEGG5r5jpNJbusbu4wNUKiCt_GMnM1UmztgXiQeuggNGkmeBIjotj0eubnmIbUV9ukHj3v7Z5PwNKKX3BCpsghd1u8lg6Nfqk_Oho4GXUfdaFY_AR3SNqzVI_9YLhND_a03MNNWlnfOvj8T4nDCKBZIs91tVyiu98d2TjnQt8PdlVwokMP3LA58m0Khy2cmUm1KF2k0zlzP8MxV9wTxNrpovMr-PnbtEPZ_IlVQIzHwjHfwQ";
|
||||
|
||||
protected static URL tokenURL;
|
||||
protected static URL introspectionURL;
|
||||
protected static TokenResponse oidcTR = null;
|
||||
protected static TokenResponse umaTR = null;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
// Assure to reset the factory's default base URL
|
||||
KeycloakClientFactory.setCustomBaseURL(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test00TokenEndpointConstruction() throws Exception {
|
||||
logger.info("*** [0.0] Start testing Keycloak token endpoint construction from base URL computed from context...");
|
||||
logger.info(
|
||||
"*** [0.0] Start testing Keycloak token endpoint construction from base URL computed from context...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
logger.info("*** [0.0] Constructed token URL is: {}", tokenURL);
|
||||
URL devTokenURL = new URL(DEV_TOKEN_ENDPOINT);
|
||||
logger.info("*** [0.0] DEV token URL is: {}", devTokenURL);
|
||||
Assert.assertNotNull(tokenURL);
|
||||
Assert.assertTrue(tokenURL.getProtocol().equals("https"));
|
||||
Assert.assertEquals(new URL(DEV_TOKEN_ENDPOINT), tokenURL);
|
||||
logger.info("Constructed URL is: {}", tokenURL);
|
||||
Assert.assertEquals(tokenURL.getProtocol(), "https");
|
||||
Assert.assertEquals(devTokenURL.toString(), tokenURL.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test01IntrospectionEndpointConstruction() throws Exception {
|
||||
logger.info("*** [0.0] Start testing Keycloak introspection endpoint construction from base URL...");
|
||||
logger.info("*** [0.1] Start testing Keycloak introspection endpoint construction from base URL...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
logger.info("*** [0.1] Constructed introspection URL is: {}", introspectionURL);
|
||||
URL devIntrospectionURL = new URL(DEV_INTROSPECTION_ENDPOINT);
|
||||
logger.info("*** [0.1] DEV introspection URL is: {}", devIntrospectionURL);
|
||||
Assert.assertNotNull(introspectionURL);
|
||||
Assert.assertTrue(introspectionURL.getProtocol().equals("https"));
|
||||
Assert.assertEquals(new URL(DEV_INTROSPECTION_ENDPOINT), introspectionURL);
|
||||
Assert.assertEquals(introspectionURL.getProtocol(), "https");
|
||||
Assert.assertEquals(devIntrospectionURL.toString(), introspectionURL.toString());
|
||||
logger.info("Constructed URL is: {}", introspectionURL);
|
||||
}
|
||||
|
||||
|
@ -71,158 +83,300 @@ public class TestKeycloakClient {
|
|||
public void test02IntrospectEndpointCompute() throws Exception {
|
||||
logger.info("*** [0.2] Start testing Keycloak userinfo endpoint computed from provided URL string...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
URL url = client
|
||||
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
logger.info("*** [0.2] Constructed introspection URL is: {}", introspectionURL);
|
||||
URL computedIntrospectionURL = client
|
||||
.computeIntrospectionEndpointURL(client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT)));
|
||||
logger.info("*** [0.2] Computed introspection URL is: {}", computedIntrospectionURL);
|
||||
URL devIntrospectionURL = new URL(DEV_INTROSPECTION_ENDPOINT);
|
||||
logger.info("*** [0.2] DEV introspection URL is: {}", devIntrospectionURL);
|
||||
Assert.assertNotNull(computedIntrospectionURL);
|
||||
Assert.assertEquals(computedIntrospectionURL.getProtocol(), "https");
|
||||
Assert.assertEquals(introspectionURL.toString(), computedIntrospectionURL.toString());
|
||||
Assert.assertEquals(devIntrospectionURL.toString(), computedIntrospectionURL.toString());
|
||||
}
|
||||
|
||||
Assert.assertNotNull(url);
|
||||
Assert.assertTrue(url.getProtocol().equals("https"));
|
||||
Assert.assertEquals(introspectionURL, url);
|
||||
Assert.assertEquals(new URL(DEV_INTROSPECTION_ENDPOINT), url);
|
||||
logger.info("Computed URL is: {}", url);
|
||||
@Test
|
||||
public void test03AvatarEndpointConstruction() throws Exception {
|
||||
logger.info("*** [0.3] Start testing Keycloak avatar endpoint construction from base URL...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
URL avatarURL = client.getAvatarEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
logger.info("*** [0.3] Constructed avatar URL is: {}", avatarURL);
|
||||
URL devAvatarURL = new URL(DEV_AVATAR_ENDPOINT);
|
||||
logger.info("*** [0.3] DEV avatar URL is: {}", devAvatarURL);
|
||||
Assert.assertNotNull(avatarURL);
|
||||
Assert.assertEquals(avatarURL.getProtocol(), "https");
|
||||
Assert.assertEquals(devAvatarURL.toString(), avatarURL.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test04CustomEndpointTest() throws Exception {
|
||||
String customBase = "https://accounts.cloud.dev.d4science.org/auth/realms/";
|
||||
logger.info("*** [0.4] Start testing Keycloak token endpoint construction from custom base URL {}...",
|
||||
customBase);
|
||||
|
||||
KeycloakClientFactory.setCustomBaseURL(customBase);
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
URL customBaseURL = client.getRealmBaseURL(DEV_ROOT_CONTEXT);
|
||||
logger.info("*** [0.4] Constructed token URL is: {}", customBaseURL);
|
||||
URL devTokenURL = new URL(DEV_TOKEN_ENDPOINT);
|
||||
logger.info("*** [0.4] DEV token URL is: {}", devTokenURL);
|
||||
Assert.assertNotNull(customBaseURL);
|
||||
Assert.assertEquals(customBaseURL.getProtocol(), "https");
|
||||
Assert.assertEquals(customBase + KeycloakClient.DEFAULT_REALM + "/", customBaseURL.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
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.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
|
||||
public void test11TestAccessTokenJWTSignature() throws Exception {
|
||||
logger.info("*** [1.0] Start testing access token JTW signature with model utils...");
|
||||
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());
|
||||
|
||||
TokenResponse oidcTR = client.queryOIDCToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET);
|
||||
logger.info("*** [1.0] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
|
||||
Assert.assertTrue("Access token is not valid",
|
||||
ModelUtils.isValid(oidcTR.getAccessToken(), realmInfo.getPublicKey()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test12QueryOIDCToken() throws Exception {
|
||||
logger.info("*** [1.2] Start testing query OIDC token from Keycloak with context...");
|
||||
oidcTR = KeycloakClientFactory.newInstance().queryOIDCToken(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
TokenResponse oidcTR = KeycloakClientFactory.newInstance().queryOIDCToken(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
CLIENT_SECRET);
|
||||
|
||||
logger.info("*** [1.2] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
logger.info("*** [1.2] OIDC refresh token: {}", oidcTR.getRefreshToken());
|
||||
TestModels.checkTokenResponse(oidcTR);
|
||||
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), "service-account-" + CLIENT_ID, false);
|
||||
TestModelUtils.checkTokenResponse(oidcTR);
|
||||
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), "service-account-" + CLIENT_ID, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test12aQueryOIDCToken() throws Exception {
|
||||
logger.info("*** [1.2a] Start testing query OIDC token from Keycloak with URL...");
|
||||
oidcTR = KeycloakClientFactory.newInstance().queryOIDCToken(tokenURL, CLIENT_ID,
|
||||
CLIENT_SECRET);
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
TokenResponse oidcTR = client.queryOIDCToken(tokenURL, CLIENT_ID, CLIENT_SECRET);
|
||||
|
||||
logger.info("*** [1.2a] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
logger.info("*** [1.2a] OIDC refresh token: {}", oidcTR.getRefreshToken());
|
||||
TestModels.checkTokenResponse(oidcTR);
|
||||
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), "service-account-" + CLIENT_ID, false);
|
||||
TestModelUtils.checkTokenResponse(oidcTR);
|
||||
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), "service-account-" + CLIENT_ID, false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test13QueryOIDCTokenOfUser() throws Exception {
|
||||
logger.info("*** [1.3] Start testing query OIDC token from Keycloak with context for user...");
|
||||
oidcTR = KeycloakClientFactory.newInstance().queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
TokenResponse oidcTR = KeycloakClientFactory.newInstance().queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
CLIENT_SECRET, TEST_USER_USERNAME, TEST_USER_PASSWORD);
|
||||
|
||||
|
||||
logger.info("*** [1.3] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
logger.info("*** [1.3] OIDC refresh token: {}", oidcTR.getRefreshToken());
|
||||
TestModels.checkTokenResponse(oidcTR);
|
||||
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, false);
|
||||
TestModelUtils.checkTokenResponse(oidcTR);
|
||||
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test13aQueryOIDCTokenOfUserWithContext() throws Exception {
|
||||
logger.info("*** [1.3a] Start testing query OIDC token for audience from Keycloak with context for user...");
|
||||
oidcTR = KeycloakClientFactory.newInstance().queryOIDCTokenOfUserWithContext(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
CLIENT_SECRET, TEST_USER_USERNAME, TEST_USER_PASSWORD, TEST_AUDIENCE);
|
||||
|
||||
TokenResponse oidcTR = KeycloakClientFactory.newInstance().queryOIDCTokenOfUser(DEV_ROOT_CONTEXT,
|
||||
CLIENT_ID, CLIENT_SECRET, TEST_USER_USERNAME, TEST_USER_PASSWORD);
|
||||
|
||||
logger.info("*** [1.3a] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
logger.info("*** [1.3a] OIDC refresh token: {}", oidcTR.getRefreshToken());
|
||||
TestModels.checkTokenResponse(oidcTR);
|
||||
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, true);
|
||||
TestModelUtils.checkTokenResponse(oidcTR);
|
||||
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, true);
|
||||
|
||||
TokenResponse oidcRestrictedTR = KeycloakClientFactory.newInstance().queryOIDCTokenOfUserWithContext(
|
||||
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TEST_USER_USERNAME, TEST_USER_PASSWORD,
|
||||
TOKEN_RESTRICTION_VRE_CONTEXT);
|
||||
|
||||
logger.info("*** [1.3a] OIDC restricted access token: {}", oidcRestrictedTR.getAccessToken());
|
||||
logger.info("*** [1.3a] OIDC restricted refresh token: {}", oidcRestrictedTR.getRefreshToken());
|
||||
TestModelUtils.checkTokenResponse(oidcTR);
|
||||
TestModelUtils.checkTokenResponse(oidcRestrictedTR);
|
||||
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, true);
|
||||
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcRestrictedTR), TEST_USER_USERNAME, true);
|
||||
assertTrue(ModelUtils.getAccessTokenFrom(oidcTR).getAudience().length > 1);
|
||||
assertTrue(ModelUtils.getAccessTokenFrom(oidcRestrictedTR).getAudience().length == 1);
|
||||
assertTrue(
|
||||
TOKEN_RESTRICTION_VRE_CONTEXT.equals(ModelUtils.getAccessTokenFrom(oidcRestrictedTR).getAudience()[0]));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test13bQueryOIDCTokenAndUMAOfUser() throws Exception {
|
||||
logger.info("*** [1.3b] Start testing query OIDC and UMA tokens from Keycloak with context for user...");
|
||||
oidcTR = KeycloakClientFactory.newInstance().queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
CLIENT_SECRET, TEST_USER_USERNAME, TEST_USER_PASSWORD);
|
||||
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
TokenResponse oidcTR = client.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
|
||||
TEST_USER_USERNAME, TEST_USER_PASSWORD);
|
||||
|
||||
logger.info("*** [1.3b] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
logger.info("*** [1.3b] OIDC refresh token: {}", oidcTR.getRefreshToken());
|
||||
TestModels.checkTokenResponse(oidcTR);
|
||||
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, false);
|
||||
|
||||
umaTR = KeycloakClientFactory.newInstance().queryUMAToken(DEV_ROOT_CONTEXT, oidcTR, TEST_AUDIENCE, null);
|
||||
TestModelUtils.checkTokenResponse(oidcTR);
|
||||
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, false);
|
||||
|
||||
TokenResponse umaTR = client.queryUMAToken(DEV_ROOT_CONTEXT, oidcTR, TEST_AUDIENCE, null);
|
||||
|
||||
logger.info("*** [1.3b] UMA access token: {}", umaTR.getAccessToken());
|
||||
logger.info("*** [1.3b] UMA refresh token: {}", umaTR.getRefreshToken());
|
||||
|
||||
TestModels.checkTokenResponse(umaTR);
|
||||
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(umaTR), TEST_USER_USERNAME, true);
|
||||
TestModelUtils.checkTokenResponse(umaTR);
|
||||
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(umaTR), TEST_USER_USERNAME, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test13aQueryOIDCTokenOfUserWithContextAndCustomHeader() throws Exception {
|
||||
logger.info(
|
||||
"*** [1.3c] Start testing query OIDC token for audience from Keycloak with context and custom headers for user...");
|
||||
TokenResponse oidcTR = KeycloakClientFactory.newInstance().queryOIDCTokenOfUserWithContext(DEV_ROOT_CONTEXT,
|
||||
CLIENT_ID, CLIENT_SECRET, TEST_USER_USERNAME, TEST_USER_PASSWORD, TOKEN_RESTRICTION_VRE_CONTEXT,
|
||||
Collections.singletonMap("X_A_CUSTOM_HEADER", "HEADER_VALUE"));
|
||||
|
||||
logger.info("*** [1.3c] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
logger.info("*** [1.3c] OIDC refresh token: {}", oidcTR.getRefreshToken());
|
||||
|
||||
TestModelUtils.checkTokenResponse(oidcTR);
|
||||
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, true);
|
||||
assertTrue(ModelUtils.getAccessTokenFrom(oidcTR).getAudience().length == 1);
|
||||
// It is not possible to check programmatically if the header has been added to the call, See the logs if the Header is present.
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test24QueryUMAToken() throws Exception {
|
||||
logger.info("*** [2.4] Start testing query UMA token from Keycloak with context...");
|
||||
umaTR = KeycloakClientFactory.newInstance().queryUMAToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
|
||||
TEST_AUDIENCE, null);
|
||||
TokenResponse umaTR = KeycloakClientFactory.newInstance().queryUMAToken(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
CLIENT_SECRET, TEST_AUDIENCE, null);
|
||||
|
||||
logger.info("*** [2.4] UMA access token: {}", umaTR.getAccessToken());
|
||||
logger.info("*** [2.4] UMA refresh token: {}", umaTR.getRefreshToken());
|
||||
|
||||
TestModels.checkTokenResponse(umaTR);
|
||||
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(umaTR), "service-account-" + CLIENT_ID, true);
|
||||
TestModelUtils.checkTokenResponse(umaTR);
|
||||
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(umaTR), "service-account-" + CLIENT_ID, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test24aQueryUMAToken() throws Exception {
|
||||
logger.info("*** [2.4a] Start testing query UMA token from Keycloak with context...");
|
||||
umaTR = KeycloakClientFactory.newInstance().queryUMAToken(tokenURL, CLIENT_ID, CLIENT_SECRET,
|
||||
TEST_AUDIENCE, null);
|
||||
logger.info("*** [2.4a] Start testing query UMA token from Keycloak with URL...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
TokenResponse umaTR = client.queryUMAToken(tokenURL, CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
|
||||
|
||||
logger.info("*** [2.4a] UMA access token: {}", umaTR.getAccessToken());
|
||||
logger.info("*** [2.4a] UMA refresh token: {}", umaTR.getRefreshToken());
|
||||
|
||||
TestModels.checkTokenResponse(umaTR);
|
||||
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(umaTR), "service-account-" + CLIENT_ID, true);
|
||||
TestModelUtils.checkTokenResponse(umaTR);
|
||||
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(umaTR), "service-account-" + CLIENT_ID, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test302IntrospectOIDCAccessToken() throws Exception {
|
||||
logger.info("*** [3.2] Start testing introspect OIDC access token...");
|
||||
TokenIntrospectionResponse tir = KeycloakClientFactory.newInstance().introspectAccessToken(
|
||||
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken());
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
TokenResponse oidcTR = client.queryOIDCToken(tokenURL, CLIENT_ID, CLIENT_SECRET);
|
||||
TokenIntrospectionResponse tir = client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
|
||||
oidcTR.getAccessToken());
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(tir);
|
||||
TestModelUtils.checkTokenIntrospectionResponse(tir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test302aIntrospectOIDCAccessToken() throws Exception {
|
||||
logger.info("*** [3.2a] Start testing introspect OIDC access token...");
|
||||
TokenIntrospectionResponse tir = KeycloakClientFactory.newInstance().introspectAccessToken(
|
||||
introspectionURL, CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken());
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
TokenResponse oidcTR = client.queryOIDCToken(tokenURL, CLIENT_ID, CLIENT_SECRET);
|
||||
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
TokenIntrospectionResponse tir = client.introspectAccessToken(introspectionURL, CLIENT_ID, CLIENT_SECRET,
|
||||
oidcTR.getAccessToken());
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(tir);
|
||||
TestModelUtils.checkTokenIntrospectionResponse(tir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test302bIntrospectOIDCAccessTokenFromDifferentServer() throws Exception {
|
||||
logger.info("*** [3.2b] Start testing introspect OIDC access token with different base URLs...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
TokenResponse oidcTR = client.queryOIDCToken(tokenURL, CLIENT_ID, CLIENT_SECRET);
|
||||
|
||||
logger.info("*** [3.2b] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
|
||||
URL introspectionURL = new URL(ACCOUNTS_DEV_INTROSPECTION_ENDPOINT);
|
||||
TokenIntrospectionResponse tir = client.introspectAccessToken(introspectionURL, CLIENT_ID, CLIENT_SECRET,
|
||||
oidcTR.getAccessToken());
|
||||
|
||||
TestModelUtils.checkTokenIntrospectionResponse(tir, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test304IntrospectUMAAccessToken() throws Exception {
|
||||
logger.info("*** [3.4] Start testing introspect UMA access token...");
|
||||
TokenIntrospectionResponse tir = KeycloakClientFactory.newInstance().introspectAccessToken(
|
||||
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken());
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
TokenResponse umaTR = client.queryUMAToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
|
||||
TokenIntrospectionResponse tir = client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
|
||||
umaTR.getAccessToken());
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(tir);
|
||||
TestModelUtils.checkTokenIntrospectionResponse(tir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test304aIntrospectUMAAccessToken() throws Exception {
|
||||
logger.info("*** [3.4a] Start testing introspect UMA access token...");
|
||||
TokenIntrospectionResponse tir = KeycloakClientFactory.newInstance().introspectAccessToken(
|
||||
introspectionURL, CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken());
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
TokenResponse umaTR = client.queryUMAToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
|
||||
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
TokenIntrospectionResponse tir = client.introspectAccessToken(introspectionURL, CLIENT_ID, CLIENT_SECRET,
|
||||
umaTR.getAccessToken());
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(tir);
|
||||
TestModelUtils.checkTokenIntrospectionResponse(tir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test306OIDCAccessTokenVerification() throws Exception {
|
||||
logger.info("*** [3.6] Start OIDC access token verification...");
|
||||
Assert.assertTrue(KeycloakClientFactory.newInstance().isAccessTokenVerified(
|
||||
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken()));
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
TokenResponse oidcTR = client.queryOIDCToken(tokenURL, CLIENT_ID, CLIENT_SECRET);
|
||||
Assert.assertTrue(
|
||||
client.isAccessTokenVerified(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test306aOIDCAccessTokenVerification() throws Exception {
|
||||
logger.info("*** [3.6a] Start OIDC access token verification...");
|
||||
Assert.assertTrue(KeycloakClientFactory.newInstance().isAccessTokenVerified(
|
||||
introspectionURL, CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken()));
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
TokenResponse oidcTR = client.queryOIDCToken(tokenURL, CLIENT_ID, CLIENT_SECRET);
|
||||
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
Assert.assertTrue(
|
||||
client.isAccessTokenVerified(introspectionURL, CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -235,22 +389,29 @@ public class TestKeycloakClient {
|
|||
@Test
|
||||
public void test307aOIDCAccessTokenNonVerification() throws Exception {
|
||||
logger.info("*** [3.7a] Start OIDC access token NON verification...");
|
||||
Assert.assertFalse(KeycloakClientFactory.newInstance().isAccessTokenVerified(
|
||||
introspectionURL, CLIENT_ID, CLIENT_SECRET, OLD_OIDC_ACCESS_TOKEN));
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
Assert.assertFalse(
|
||||
client.isAccessTokenVerified(introspectionURL, CLIENT_ID, CLIENT_SECRET, OLD_OIDC_ACCESS_TOKEN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test309UMAAccessTokenVerification() throws Exception {
|
||||
logger.info("*** [3.9] Start UMA access token verification...");
|
||||
Assert.assertTrue(KeycloakClientFactory.newInstance().isAccessTokenVerified(
|
||||
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken()));
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
TokenResponse umaTR = client.queryUMAToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
|
||||
Assert.assertTrue(
|
||||
client.isAccessTokenVerified(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test309aUMAAccessTokenVerification() throws Exception {
|
||||
logger.info("*** [3.9a] Start UMA access token verification...");
|
||||
Assert.assertTrue(KeycloakClientFactory.newInstance().isAccessTokenVerified(
|
||||
introspectionURL, CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken()));
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
TokenResponse umaTR = client.queryUMAToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
|
||||
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
Assert.assertTrue(
|
||||
client.isAccessTokenVerified(introspectionURL, CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -263,19 +424,122 @@ public class TestKeycloakClient {
|
|||
@Test
|
||||
public void test310aUMAAccessTokenNonVerification() throws Exception {
|
||||
logger.info("*** [3.10a] Start UMA access token NON verification...");
|
||||
Assert.assertFalse(KeycloakClientFactory.newInstance().isAccessTokenVerified(
|
||||
introspectionURL, CLIENT_ID, CLIENT_SECRET, OLD_UMA_ACCESS_TOKEN));
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
Assert.assertFalse(
|
||||
client.isAccessTokenVerified(introspectionURL, CLIENT_ID, CLIENT_SECRET, OLD_UMA_ACCESS_TOKEN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test4GetTokenOfUser() throws Exception {
|
||||
logger.info("*** [4] Start testing query token from Keycloak for user...");
|
||||
TokenResponse tokenResponse = KeycloakClientHelper.getTokenForUser(DEV_ROOT_CONTEXT, TEST_USER_USERNAME, TEST_USER_PASSWORD);
|
||||
|
||||
TokenResponse tokenResponse = KeycloakClientHelper.getTokenForUser(DEV_ROOT_CONTEXT, TEST_USER_USERNAME,
|
||||
TEST_USER_PASSWORD);
|
||||
|
||||
logger.info("*** [4] UMA access token: {}", tokenResponse.getAccessToken());
|
||||
logger.info("*** [4] UMA refresh token: {}", tokenResponse.getRefreshToken());
|
||||
|
||||
TestModels.checkTokenResponse(tokenResponse);
|
||||
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(tokenResponse), TEST_USER_USERNAME, true);
|
||||
TestModelUtils.checkTokenResponse(tokenResponse);
|
||||
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(tokenResponse), TEST_USER_USERNAME, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test51ExchangeToken4Access() throws Exception {
|
||||
logger.info("*** [5.1] Start testing token exchange for access token from Keycloak...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
TokenResponse oidcTR = client.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
|
||||
TEST_USER_USERNAME, TEST_USER_PASSWORD);
|
||||
|
||||
logger.info("*** [5.1] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
|
||||
TokenResponse exchangedTR = client.exchangeTokenForAccessToken(DEV_ROOT_CONTEXT, oidcTR.getAccessToken(),
|
||||
CLIENT_ID, CLIENT_SECRET, CLIENT_ID);
|
||||
|
||||
logger.info("*** [5.1] Exchanged access token: {}", exchangedTR.getAccessToken());
|
||||
TestModelUtils.checkTokenResponse(exchangedTR, false);
|
||||
Assert.assertNull(exchangedTR.getRefreshToken());
|
||||
|
||||
TestModelUtils.checkTokenIntrospectionResponse(client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
CLIENT_SECRET, exchangedTR.getAccessToken()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test51aExchangeUMAToken4Access() throws Exception {
|
||||
logger.info("*** [5.1a] Start testing UMA token exchange for access token from Keycloak...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
TokenResponse oidcTR = client.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
|
||||
TEST_USER_USERNAME, TEST_USER_PASSWORD);
|
||||
|
||||
logger.info("*** [5.1a] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
|
||||
TokenResponse umaTR = client.queryUMAToken(DEV_ROOT_CONTEXT, oidcTR, TOKEN_RESTRICTION_VRE_CONTEXT, null);
|
||||
|
||||
logger.info("*** [5.1a] UMA access token: {}", umaTR.getAccessToken());
|
||||
|
||||
TokenResponse exchangedTR = client.exchangeTokenForAccessToken(DEV_ROOT_CONTEXT, umaTR.getAccessToken(),
|
||||
CLIENT_ID, CLIENT_SECRET, CLIENT_ID);
|
||||
|
||||
logger.info("*** [5.1a] Exchanged access token: {}", exchangedTR.getAccessToken());
|
||||
TestModelUtils.checkTokenResponse(exchangedTR, false);
|
||||
Assert.assertNull(exchangedTR.getRefreshToken());
|
||||
|
||||
TestModelUtils.checkTokenIntrospectionResponse(client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
CLIENT_SECRET, exchangedTR.getAccessToken()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test52ExchangeToken4refresh() throws Exception {
|
||||
logger.info("*** [5.2] Start testing token exchange for refresh token from Keycloak...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
TokenResponse oidcTR = client.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
|
||||
TEST_USER_USERNAME, TEST_USER_PASSWORD);
|
||||
|
||||
logger.info("*** [5.2] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
|
||||
TokenResponse exchangedTR = client.exchangeTokenForRefreshToken(DEV_ROOT_CONTEXT, oidcTR.getAccessToken(),
|
||||
CLIENT_ID, CLIENT_SECRET, CLIENT_ID);
|
||||
|
||||
logger.info("*** [5.2] Exchanged access token: {}", exchangedTR.getAccessToken());
|
||||
logger.info("*** [5.2] Exchanged refresh token: {}", exchangedTR.getRefreshToken());
|
||||
TestModelUtils.checkTokenResponse(exchangedTR);
|
||||
|
||||
TestModelUtils.checkTokenIntrospectionResponse(
|
||||
client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, exchangedTR.getAccessToken()));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void test53ExchangeToken4Offline() throws Exception {
|
||||
logger.info("*** [5.3] Start testing token exchange for offline token from Keycloak...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
TokenResponse oidcTR = client.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
|
||||
TEST_USER_USERNAME, TEST_USER_PASSWORD);
|
||||
|
||||
logger.info("*** [5.3] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
|
||||
// TokenResponse exchangedTR =
|
||||
client.exchangeTokenForOfflineToken(DEV_ROOT_CONTEXT, oidcTR.getAccessToken(),
|
||||
CLIENT_ID, CLIENT_SECRET, CLIENT_ID);
|
||||
|
||||
// For the moment this part is not covered by tests
|
||||
// logger.info("*** [5.3] Exchanged access token: {}", exchangedTR.getAccessToken());
|
||||
// logger.info("*** [5.3] Exchanged refresh token: {}", exchangedTR.getRefreshToken());
|
||||
// TestModelUtils.checkTokenResponse(exchangedTR, true);
|
||||
// TestModelUtils.checkOfflineToken(exchangedTR);
|
||||
//
|
||||
// TestModelUtils.checkTokenIntrospectionResponse(client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
// CLIENT_SECRET, exchangedTR.getAccessToken()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test6GetAvatar() throws Exception {
|
||||
logger.info("*** [6] Start testing get user's avatar...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
TokenResponse oidcTR = client.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
|
||||
TEST_USER_USERNAME, TEST_USER_PASSWORD);
|
||||
|
||||
byte[] avatarData = client.getAvatarData(DEV_ROOT_CONTEXT, oidcTR);
|
||||
Assert.assertNotNull("Avatar data is null", avatarData);
|
||||
logger.info("*** [6] Avatar image of user is {} bytes", avatarData.length);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package org.gcube.common.keycloak;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.gcube.common.keycloak.model.AccessToken;
|
||||
|
@ -18,9 +21,9 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class TestModels {
|
||||
public class TestModelUtils {
|
||||
|
||||
protected static final Logger logger = LoggerFactory.getLogger(TestModels.class);
|
||||
protected static final Logger logger = LoggerFactory.getLogger(TestModelUtils.class);
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
@ -30,6 +33,22 @@ public class TestModels {
|
|||
public void tearDown() throws Exception {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenValidity() throws Exception {
|
||||
logger.info("Start testing access token valdity...");
|
||||
TokenResponse tr = new ObjectMapper().readValue(new File("src/test/resources/oidc-token-response.json"),
|
||||
TokenResponse.class);
|
||||
|
||||
RSAPublicKey publicKey = ModelUtils.createRSAPublicKey(
|
||||
new String(Files.readAllBytes(Paths.get("src/test/resources/rsa-public-key.pem"))));
|
||||
|
||||
// Valid signature
|
||||
Assert.assertFalse("Token is valid", ModelUtils.isValid(tr.getAccessToken(), publicKey));
|
||||
Assert.assertFalse("Token is not expired", ModelUtils.isValid(tr.getAccessToken(), publicKey, true));
|
||||
Assert.assertTrue("Token is valid", ModelUtils.isValid(tr.getAccessToken(), publicKey, false));
|
||||
Assert.assertFalse("Token signature is valid", ModelUtils.isValid(tr.getAccessToken().replace("ZV9hY2Nlc3", "ZV9hY2Nlcc"), publicKey));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenResponseForOIDC() throws Exception {
|
||||
logger.info("Start testing OIDC token response object binding...");
|
||||
|
@ -38,7 +57,7 @@ public class TestModels {
|
|||
|
||||
logger.debug("OIDC token response:\n{}", ModelUtils.toJSONString(tr, true));
|
||||
checkTokenResponse(tr);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -86,10 +105,16 @@ public class TestModels {
|
|||
}
|
||||
|
||||
public static void checkTokenResponse(TokenResponse tr) throws Exception {
|
||||
checkTokenResponse(tr, true);
|
||||
}
|
||||
|
||||
public static void checkTokenResponse(TokenResponse tr, boolean checkAlsoRefreshToken) throws Exception {
|
||||
Assert.assertNotNull(tr);
|
||||
Assert.assertEquals("bearer", tr.getTokenType().toLowerCase());
|
||||
Assert.assertNotNull(tr.getAccessToken());
|
||||
Assert.assertNotNull(tr.getRefreshToken());
|
||||
Assert.assertNotNull("Access token is null", tr.getAccessToken());
|
||||
if (checkAlsoRefreshToken) {
|
||||
Assert.assertNotNull("Refresh token is null", tr.getRefreshToken());
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkAccessToken(AccessToken at, String preferredUsername, boolean checkAudience) {
|
||||
|
@ -99,19 +124,33 @@ public class TestModels {
|
|||
Assert.assertEquals(preferredUsername, at.getPreferredUsername());
|
||||
}
|
||||
if (checkAudience) {
|
||||
Assert.assertNotNull(at.getAudience());
|
||||
Assert.assertNotNull("Audience is null", at.getAudience());
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkRefreshToken(RefreshToken rt) {
|
||||
logger.debug("Refresh token:\n{}", ModelUtils.toJSONString(rt, true));
|
||||
Assert.assertNotNull(rt.getOtherClaims());
|
||||
Assert.assertNotNull(rt.getAudience());
|
||||
Assert.assertNotNull("Other claims are null", rt.getOtherClaims());
|
||||
Assert.assertNotNull("Audience is null", rt.getAudience());
|
||||
}
|
||||
|
||||
public static void checkOfflineToken(TokenResponse tr) throws Exception {
|
||||
RefreshToken rt = ModelUtils.getRefreshTokenFrom(tr.getRefreshToken());
|
||||
Assert.assertEquals("Offline", rt.getType());
|
||||
Assert.assertNull("Expiration is not null", rt.getExp());
|
||||
}
|
||||
|
||||
public static void checkTokenIntrospectionResponse(TokenIntrospectionResponse tir) {
|
||||
checkTokenIntrospectionResponse(tir, true);
|
||||
}
|
||||
|
||||
public static void checkTokenIntrospectionResponse(TokenIntrospectionResponse tir, boolean mustBeActive) {
|
||||
logger.debug("Token introspection response :\n{}", ModelUtils.toJSONString(tir, true));
|
||||
Assert.assertTrue(tir.isActive());
|
||||
if (mustBeActive) {
|
||||
Assert.assertTrue("Token is not active", tir.getActive());
|
||||
} else {
|
||||
Assert.assertFalse("Token is active", tir.getActive());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjyJAmPx5K2eGYjvRmiBC8as3nF/jsYSUgBnlul9TNEdWSPuxTzntTb37xDGPmVRkNyOCJmRnBcI8GbrWz8SHJ643JTKp8yx4zDQCgLD72crb9ah/Tfu8KpDz3+FRuYLE4EvvRCGBnsFO2vSM02iTAp7nSToOCX4jCCrDMBUUJkIzuZIQUBTx8lvWl/M6LtQAqS7Gw3wsZSklRcvsR9qlCUxJW3cvhALt9wWrejSJ3LaR6TMaNa8k6Ojk6bJ3/5c6OifxYde0YjXIOaeMkkgnfoQvs4cyhvNxCXLx65+VKV4Dlts7cTgJvuodV0w/UhJGfUxgA9V6IQowmwHNBnYAMwIDAQAB
|
Loading…
Reference in New Issue