Added JWT digital signature verification by using the RSA public key of the realm on server. Uses `java-jwt` library by Auth0 [#27340]
This commit is contained in:
parent
726291ca55
commit
23f387f832
|
@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|||
## [v2.1.0-SNAPSHOT]
|
||||
- Added `token-exchange` support, also with `offline-token` scope, and methods to add extra headers during the OIDC token requests.
|
||||
- Added custom base URL set via factory (not automatically working cross environments) [#27234].
|
||||
- Added JWT digital signature verification by using the RSA public key of the realm on server. Uses `java-jwt` library by Auth0 [#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).
|
||||
|
|
15
pom.xml
15
pom.xml
|
@ -7,8 +7,7 @@
|
|||
<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>
|
||||
|
@ -33,6 +32,12 @@
|
|||
<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>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
|
@ -60,6 +65,12 @@
|
|||
<artifactId>gxJRS</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
<version>4.4.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
|
|
|
@ -38,9 +38,12 @@ 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.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;
|
||||
|
@ -176,11 +179,80 @@ 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 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
|
||||
|
@ -192,46 +264,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, (String) null);
|
||||
}
|
||||
|
||||
@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 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, clientId, clientSecret, audience, null);
|
||||
}
|
||||
|
||||
@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);
|
||||
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 {
|
||||
|
@ -248,27 +344,18 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
password, audience, extraHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCToken(String context, String authorization) throws KeycloakClientException {
|
||||
return queryOIDCTokenWithContext(context, authorization, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse queryOIDCTokenWithContext(String context, String authorization, String audience)
|
||||
throws KeycloakClientException {
|
||||
return queryOIDCTokenWithContext(getTokenEndpointURL(getRealmBaseURL(context)), authorization, audience);
|
||||
}
|
||||
|
||||
@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);
|
||||
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 {
|
||||
String username, String password, String audience, Map<String, String> extraHeaders)
|
||||
throws KeycloakClientException {
|
||||
|
||||
return queryOIDCTokenOfUserWithContext(tokenURL, constructBasicAuthenticationHeader(clientId, clientSecret),
|
||||
username, password, audience, extraHeaders);
|
||||
|
@ -278,7 +365,8 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
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);
|
||||
return queryOIDCTokenOfUserWithContext(tokenURL, authorization, username, password, audience,
|
||||
(Map<String, String>) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -289,6 +377,7 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
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);
|
||||
|
@ -307,12 +396,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);
|
||||
|
||||
|
@ -415,27 +500,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");
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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);
|
||||
|
@ -450,7 +555,7 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
}
|
||||
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);
|
||||
}
|
||||
|
@ -538,99 +643,73 @@ 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;
|
||||
try {
|
||||
response = request.post();
|
||||
} catch (Exception e) {
|
||||
throw new KeycloakClientException("Cannot send request correctly", 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());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeTokenForAccessToken(String context, TokenResponse oidcTokenResponse, String clientId,
|
||||
public TokenResponse exchangeTokenForAccessToken(String context, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException {
|
||||
|
||||
return exchangeTokenForAccessToken(getTokenEndpointURL(getRealmBaseURL(context)), oidcTokenResponse, clientId,
|
||||
return exchangeTokenForAccessToken(getTokenEndpointURL(getRealmBaseURL(context)), oidcAccessToken, clientId,
|
||||
clientSecret, audience);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeTokenForAccessToken(URL tokenURL, TokenResponse oidcTokenResponse, String clientId,
|
||||
public TokenResponse exchangeTokenForAccessToken(URL tokenURL, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException {
|
||||
|
||||
return exchangeToken(tokenURL, oidcTokenResponse.getAccessToken(), clientId, clientSecret, audience,
|
||||
ACCESS_TOKEN_TOKEN_TYPE, null);
|
||||
return exchangeToken(tokenURL, oidcAccessToken, clientId, clientSecret, audience, ACCESS_TOKEN_TOKEN_TYPE,
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeTokenForRefreshToken(String context, TokenResponse oidcTokenResponse, String clientId,
|
||||
public TokenResponse exchangeTokenForRefreshToken(String context, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException {
|
||||
|
||||
return exchangeTokenForRefreshToken(getTokenEndpointURL(getRealmBaseURL(context)), oidcTokenResponse, clientId,
|
||||
return exchangeTokenForRefreshToken(getTokenEndpointURL(getRealmBaseURL(context)), oidcAccessToken, clientId,
|
||||
clientSecret, audience);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeTokenForRefreshToken(URL tokenURL, TokenResponse oidcTokenResponse, String clientId,
|
||||
public TokenResponse exchangeTokenForRefreshToken(URL tokenURL, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException {
|
||||
|
||||
return exchangeToken(tokenURL, oidcTokenResponse.getAccessToken(), clientId, clientSecret, audience,
|
||||
REFRESH_TOKEN_TOKEN_TYPE, null);
|
||||
return exchangeToken(tokenURL, oidcAccessToken, clientId, clientSecret, audience, REFRESH_TOKEN_TOKEN_TYPE,
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeTokenForOfflineToken(String context, TokenResponse oidcTokenResponse, String clientId,
|
||||
public TokenResponse exchangeTokenForOfflineToken(String context, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException {
|
||||
|
||||
return exchangeTokenForOfflineToken(getTokenEndpointURL(getRealmBaseURL(context)), oidcTokenResponse, clientId,
|
||||
return exchangeTokenForOfflineToken(getTokenEndpointURL(getRealmBaseURL(context)), oidcAccessToken, clientId,
|
||||
clientSecret, audience);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeTokenForOfflineToken(URL tokenURL, TokenResponse oidcTokenResponse, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException {
|
||||
public TokenResponse exchangeTokenForOfflineToken(URL tokenURL, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws IllegalArgumentException, KeycloakClientException {
|
||||
|
||||
return exchangeToken(tokenURL, oidcTokenResponse.getAccessToken(), clientId, clientSecret, audience,
|
||||
REFRESH_TOKEN_TOKEN_TYPE, OFFLINE_ACCESS_SCOPE);
|
||||
// ModelUtils.getAccessTokenFrom(oidcTokenResponse).getScope().
|
||||
return exchangeToken(tokenURL, oidcAccessToken, clientId, clientSecret, audience, REFRESH_TOKEN_TOKEN_TYPE,
|
||||
OFFLINE_ACCESS_SCOPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenResponse exchangeToken(URL tokenURL, String oidcAccessToken, String clientId, String clientSecret,
|
||||
protected TokenResponse exchangeToken(URL tokenURL, String oidcAccessToken, String clientId, String clientSecret,
|
||||
String audience, String requestedTokenType, String scope) throws KeycloakClientException {
|
||||
|
||||
if (audience == null || "".equals(audience)) {
|
||||
|
@ -658,7 +737,7 @@ public class DefaultKeycloakClient implements KeycloakClient {
|
|||
logger.error("Can't URL encode audience: {}", audience, e);
|
||||
}
|
||||
|
||||
return performRequest(tokenURL, Collections.emptyMap(), params);
|
||||
return performRequest(tokenURL, null, params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -699,44 +778,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
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.net.URL;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.gcube.common.keycloak.model.PublishedRealmRepresentation;
|
||||
import org.gcube.common.keycloak.model.TokenIntrospectionResponse;
|
||||
import org.gcube.common.keycloak.model.TokenResponse;
|
||||
|
||||
|
@ -72,7 +73,16 @@ public interface KeycloakClient {
|
|||
* @return the Keycloak <code>avatar</code> endpoint URL
|
||||
* @throws KeycloakClientException if something goes wrong discovering the endpoint URL
|
||||
*/
|
||||
public URL getAvatarEndpointURL(URL realmBaseURL) throws KeycloakClientException;
|
||||
URL getAvatarEndpointURL(URL realmBaseURL) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Get the realm info setup
|
||||
*
|
||||
* @param realmURL the realm URL
|
||||
* @return the configured realm info
|
||||
* @throws KeycloakClientException
|
||||
*/
|
||||
PublishedRealmRepresentation getRealmInfo(URL realmURL) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the context's Keycloak server, by using provided clientId and client secret.
|
||||
|
@ -85,9 +95,93 @@ 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.
|
||||
*
|
||||
* @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 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(String context, String clientId, String clientSecret, Map<String, String> extraHeaders) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided clientId and client secret.
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
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.
|
||||
* 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 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 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
|
||||
*/
|
||||
TokenResponse queryOIDCToken(String context, 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 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 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(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 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
|
||||
|
@ -98,6 +192,109 @@ public interface KeycloakClient {
|
|||
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 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 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 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(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.
|
||||
*
|
||||
|
@ -113,87 +310,20 @@ public interface KeycloakClient {
|
|||
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.
|
||||
* 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 Keycloak 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
|
||||
* @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) throws KeycloakClientException;
|
||||
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(String context, String clientId, String clientSecret,
|
||||
String username, String password, String audience, Map<String, String> extraHeaders) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided clientId and client secret.
|
||||
*
|
||||
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
|
||||
* @param clientId the client id
|
||||
* @param clientSecret the client secret
|
||||
* @return the issued token as {@link TokenResponse} object
|
||||
* @throws KeycloakClientException if something goes wrong performing the query
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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 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;
|
||||
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String clientId, String clientSecret,
|
||||
String username, String password, String audience, 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
|
||||
*/
|
||||
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;
|
||||
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.
|
||||
|
@ -209,30 +339,91 @@ public interface KeycloakClient {
|
|||
TokenResponse queryOIDCTokenOfUserWithContext(String context, String authorization, String username,
|
||||
String password, String audience) throws KeycloakClientException;
|
||||
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(String context, String authorization, String username,
|
||||
String password, String audience, Map<String, String> extraHeaders) throws KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Queries an OIDC token from the Keycloak server, by using provided authorization.
|
||||
* 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 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, 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 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 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 queryOIDCTokenWithContext(URL tokenURL, String authorization, String audience)
|
||||
throws KeycloakClientException;
|
||||
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 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(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.
|
||||
|
@ -248,7 +439,20 @@ public interface KeycloakClient {
|
|||
TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String authorization, String username, String password,
|
||||
String audience) throws KeycloakClientException;
|
||||
|
||||
public TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String authorization, String username,
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
|
@ -420,27 +624,24 @@ public interface KeycloakClient {
|
|||
*/
|
||||
TokenResponse refreshToken(URL tokenURL, String clientId, String clientSecret, String refreshTokenJWTString)
|
||||
throws KeycloakClientException;
|
||||
|
||||
TokenResponse exchangeTokenForOfflineToken(URL tokenURL, TokenResponse oidcTokenResponse, String clientId,
|
||||
|
||||
TokenResponse exchangeTokenForAccessToken(URL tokenURL, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException;
|
||||
|
||||
TokenResponse exchangeTokenForAccessToken(String context, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException;
|
||||
|
||||
TokenResponse exchangeTokenForOfflineToken(String context, TokenResponse oidcTokenResponse, String clientId,
|
||||
TokenResponse exchangeTokenForRefreshToken(URL tokenURL, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException;
|
||||
|
||||
TokenResponse exchangeTokenForRefreshToken(URL tokenURL, TokenResponse oidcTokenResponse, String clientId,
|
||||
TokenResponse exchangeTokenForRefreshToken(String context, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException;
|
||||
|
||||
TokenResponse exchangeTokenForRefreshToken(String context, TokenResponse oidcTokenResponse, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException;
|
||||
TokenResponse exchangeTokenForOfflineToken(URL tokenURL, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws IllegalArgumentException, KeycloakClientException;
|
||||
|
||||
TokenResponse exchangeTokenForAccessToken(URL tokenURL, TokenResponse oidcTokenResponse, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException;
|
||||
|
||||
TokenResponse exchangeTokenForAccessToken(String context, TokenResponse oidcTokenResponse, String clientId,
|
||||
String clientSecret, String audience) throws KeycloakClientException;
|
||||
|
||||
TokenResponse exchangeToken(URL tokenURL, String oidcAccessToken, String clientId, String clientSecret,
|
||||
String audience, String requestedTokenType, String scope) throws KeycloakClientException;
|
||||
TokenResponse exchangeTokenForOfflineToken(String context, String oidcAccessToken, String clientId,
|
||||
String clientSecret, String audience) throws IllegalArgumentException, KeycloakClientException;
|
||||
|
||||
/**
|
||||
* Introspects an access token against the Keycloak server.
|
||||
|
@ -494,8 +695,8 @@ public interface KeycloakClient {
|
|||
boolean isAccessTokenVerified(URL introspectionURL, String clientId, String clientSecret,
|
||||
String accessTokenJWTString) throws KeycloakClientException;
|
||||
|
||||
public byte[] getAvatarData(String context, TokenResponse tokenResponse) throws KeycloakClientException;
|
||||
byte[] getAvatarData(String context, TokenResponse tokenResponse) throws KeycloakClientException;
|
||||
|
||||
public byte[] getAvatarData(URL avatarURL, TokenResponse tokenResponse) throws KeycloakClientException;
|
||||
byte[] getAvatarData(URL avatarURL, TokenResponse tokenResponse) throws KeycloakClientException;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package org.gcube.common.keycloak.model;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
@ -11,6 +14,14 @@ import org.gcube.com.fasterxml.jackson.databind.ObjectWriter;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||
import com.auth0.jwt.interfaces.JWTVerifier;
|
||||
|
||||
/**
|
||||
* @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 +48,46 @@ public class ModelUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static RSAPublicKey createRSAPublicKey(String publicKeyPem) {
|
||||
try {
|
||||
String publicKey = publicKeyPem.replaceFirst("-----BEGIN .+-----\n", "");
|
||||
publicKey = publicKey.replaceFirst("-----END .+-----", "");
|
||||
|
||||
byte[] encoded = Base64.getDecoder().decode(publicKey);
|
||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||
return (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cant' create RSA public key from PEM string", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the token's digital signature
|
||||
*
|
||||
* @param token the base64 JWT token string
|
||||
* @param publicKey the realm's public key on server
|
||||
* @return <code>true</code> if the signature is verified, <code>false</code> otherwise
|
||||
* @throws RuntimeException if an error occurs constructing the digital signature verifier
|
||||
*/
|
||||
public static boolean isSignatureValid(String token, RSAPublicKey publicKey) throws RuntimeException {
|
||||
JWTVerifier verifier = null;
|
||||
try {
|
||||
Algorithm algorithm = Algorithm.RSA256(publicKey, null);
|
||||
verifier = JWT.require(algorithm).build();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot construct the JWT digital signature verifier", e);
|
||||
}
|
||||
try {
|
||||
verifier.verify(token);
|
||||
return true;
|
||||
} catch (JWTVerificationException e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("JWT digital signature is not verified", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAccessTokenPayloadJSONStringFrom(TokenResponse tokenResponse) throws Exception {
|
||||
return getAccessTokenPayloadJSONStringFrom(tokenResponse, true);
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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) {
|
||||
publicKey = ModelUtils.createRSAPublicKey(publicKeyPem);
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -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,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.util;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class Time {
|
||||
|
||||
private static int offset;
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.net.URL;
|
|||
import java.util.Collections;
|
||||
|
||||
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;
|
||||
|
@ -124,6 +125,34 @@ public class TestKeycloakClient {
|
|||
Assert.assertEquals(customBase + KeycloakClient.DEFAULT_REALM + "/", customBaseURL.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test10QueryRealmInfo() throws Exception {
|
||||
logger.info("*** [1.0] Start testing query realm info...");
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
PublishedRealmRepresentation realmInfo = client.getRealmInfo(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
|
||||
|
||||
logger.info("*** [1.0] Realm info public key PEM: {}", realmInfo.getPublicKeyPem());
|
||||
logger.info("*** [1.0] Realm info public key: {}", realmInfo.getPublicKey());
|
||||
// TestModels.checkTokenResponse(oidcTR);
|
||||
// TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), "service-account-" + CLIENT_ID, false);
|
||||
}
|
||||
|
||||
@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 digital signature is not valid",
|
||||
ModelUtils.isSignatureValid(oidcTR.getAccessToken(), realmInfo.getPublicKey()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test12QueryOIDCToken() throws Exception {
|
||||
logger.info("*** [1.2] Start testing query OIDC token from Keycloak with context...");
|
||||
|
@ -132,8 +161,8 @@ public class TestKeycloakClient {
|
|||
|
||||
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
|
||||
|
@ -145,8 +174,8 @@ public class TestKeycloakClient {
|
|||
|
||||
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
|
||||
|
@ -157,8 +186,8 @@ public class TestKeycloakClient {
|
|||
|
||||
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
|
||||
|
@ -169,8 +198,8 @@ public class TestKeycloakClient {
|
|||
|
||||
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,
|
||||
|
@ -178,10 +207,10 @@ public class TestKeycloakClient {
|
|||
|
||||
logger.info("*** [1.3a] OIDC restricted access token: {}", oidcRestrictedTR.getAccessToken());
|
||||
logger.info("*** [1.3a] OIDC restricted refresh token: {}", oidcRestrictedTR.getRefreshToken());
|
||||
TestModels.checkTokenResponse(oidcTR);
|
||||
TestModels.checkTokenResponse(oidcRestrictedTR);
|
||||
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, true);
|
||||
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcRestrictedTR), TEST_USER_USERNAME, true);
|
||||
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(
|
||||
|
@ -197,16 +226,16 @@ public class TestKeycloakClient {
|
|||
|
||||
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);
|
||||
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
|
||||
|
@ -220,8 +249,8 @@ public class TestKeycloakClient {
|
|||
logger.info("*** [1.3c] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
logger.info("*** [1.3c] 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);
|
||||
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.
|
||||
}
|
||||
|
@ -235,8 +264,8 @@ public class TestKeycloakClient {
|
|||
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
|
||||
|
@ -249,8 +278,8 @@ public class TestKeycloakClient {
|
|||
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
|
||||
|
@ -262,7 +291,7 @@ public class TestKeycloakClient {
|
|||
TokenIntrospectionResponse tir = client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
|
||||
oidcTR.getAccessToken());
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(tir);
|
||||
TestModelUtils.checkTokenIntrospectionResponse(tir);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -275,7 +304,7 @@ public class TestKeycloakClient {
|
|||
TokenIntrospectionResponse tir = client.introspectAccessToken(introspectionURL, CLIENT_ID, CLIENT_SECRET,
|
||||
oidcTR.getAccessToken());
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(tir);
|
||||
TestModelUtils.checkTokenIntrospectionResponse(tir);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -291,7 +320,7 @@ public class TestKeycloakClient {
|
|||
TokenIntrospectionResponse tir = client.introspectAccessToken(introspectionURL, CLIENT_ID, CLIENT_SECRET,
|
||||
oidcTR.getAccessToken());
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(tir, true);
|
||||
TestModelUtils.checkTokenIntrospectionResponse(tir, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -302,7 +331,7 @@ public class TestKeycloakClient {
|
|||
TokenIntrospectionResponse tir = client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
|
||||
umaTR.getAccessToken());
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(tir);
|
||||
TestModelUtils.checkTokenIntrospectionResponse(tir);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -314,7 +343,7 @@ public class TestKeycloakClient {
|
|||
TokenIntrospectionResponse tir = client.introspectAccessToken(introspectionURL, CLIENT_ID, CLIENT_SECRET,
|
||||
umaTR.getAccessToken());
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(tir);
|
||||
TestModelUtils.checkTokenIntrospectionResponse(tir);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -398,8 +427,8 @@ public class TestKeycloakClient {
|
|||
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
|
||||
|
@ -407,19 +436,18 @@ public class TestKeycloakClient {
|
|||
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);
|
||||
TEST_USER_USERNAME, TEST_USER_PASSWORD);
|
||||
|
||||
logger.info("*** [5.1] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
|
||||
TokenResponse exchangedTR = client.exchangeTokenForAccessToken(DEV_ROOT_CONTEXT, oidcTR, CLIENT_ID,
|
||||
CLIENT_SECRET, CLIENT_ID);
|
||||
TokenResponse exchangedTR = client.exchangeTokenForAccessToken(DEV_ROOT_CONTEXT, oidcTR.getAccessToken(),
|
||||
CLIENT_ID, CLIENT_SECRET, CLIENT_ID);
|
||||
|
||||
logger.info("*** [5.1] Exchanged access token: {}", exchangedTR.getAccessToken());
|
||||
TestModels.checkTokenResponse(exchangedTR, false);
|
||||
TestModelUtils.checkTokenResponse(exchangedTR, false);
|
||||
Assert.assertNull(exchangedTR.getRefreshToken());
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
TestModelUtils.checkTokenIntrospectionResponse(client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
CLIENT_SECRET, exchangedTR.getAccessToken()));
|
||||
}
|
||||
|
||||
|
@ -436,14 +464,14 @@ public class TestKeycloakClient {
|
|||
|
||||
logger.info("*** [5.1a] UMA access token: {}", umaTR.getAccessToken());
|
||||
|
||||
TokenResponse exchangedTR = client.exchangeTokenForAccessToken(DEV_ROOT_CONTEXT, umaTR, CLIENT_ID,
|
||||
CLIENT_SECRET, CLIENT_ID);
|
||||
TokenResponse exchangedTR = client.exchangeTokenForAccessToken(DEV_ROOT_CONTEXT, umaTR.getAccessToken(),
|
||||
CLIENT_ID, CLIENT_SECRET, CLIENT_ID);
|
||||
|
||||
logger.info("*** [5.1a] Exchanged access token: {}", exchangedTR.getAccessToken());
|
||||
TestModels.checkTokenResponse(exchangedTR, false);
|
||||
TestModelUtils.checkTokenResponse(exchangedTR, false);
|
||||
Assert.assertNull(exchangedTR.getRefreshToken());
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
TestModelUtils.checkTokenIntrospectionResponse(client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
CLIENT_SECRET, exchangedTR.getAccessToken()));
|
||||
}
|
||||
|
||||
|
@ -456,14 +484,14 @@ public class TestKeycloakClient {
|
|||
|
||||
logger.info("*** [5.2] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
|
||||
TokenResponse exchangedTR = client.exchangeTokenForRefreshToken(DEV_ROOT_CONTEXT, oidcTR, CLIENT_ID,
|
||||
CLIENT_SECRET, CLIENT_ID);
|
||||
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());
|
||||
TestModels.checkTokenResponse(exchangedTR);
|
||||
TestModelUtils.checkTokenResponse(exchangedTR);
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(
|
||||
TestModelUtils.checkTokenIntrospectionResponse(
|
||||
client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, exchangedTR.getAccessToken()));
|
||||
}
|
||||
|
||||
|
@ -472,31 +500,32 @@ public class TestKeycloakClient {
|
|||
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);
|
||||
TEST_USER_USERNAME, TEST_USER_PASSWORD);
|
||||
|
||||
logger.info("*** [5.3] OIDC access token: {}", oidcTR.getAccessToken());
|
||||
|
||||
TokenResponse exchangedTR = client.exchangeTokenForOfflineToken(DEV_ROOT_CONTEXT, oidcTR, CLIENT_ID,
|
||||
CLIENT_SECRET, CLIENT_ID);
|
||||
TokenResponse exchangedTR = client.exchangeTokenForOfflineToken(DEV_ROOT_CONTEXT, oidcTR.getAccessToken(),
|
||||
CLIENT_ID, CLIENT_SECRET, CLIENT_ID);
|
||||
|
||||
logger.info("*** [5.3] Exchanged access token: {}", exchangedTR.getAccessToken());
|
||||
logger.info("*** [5.3] Exchanged refresh token: {}", exchangedTR.getRefreshToken());
|
||||
TestModels.checkTokenResponse(exchangedTR, true);
|
||||
TestModels.checkOfflineToken(exchangedTR);
|
||||
TestModelUtils.checkTokenResponse(exchangedTR, true);
|
||||
TestModelUtils.checkOfflineToken(exchangedTR);
|
||||
|
||||
TestModels.checkTokenIntrospectionResponse(client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID,
|
||||
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, GATEWAY, null, 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);
|
||||
// }
|
||||
@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,8 @@
|
|||
package org.gcube.common.keycloak;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.gcube.common.keycloak.model.AccessToken;
|
||||
|
@ -18,9 +20,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 +32,19 @@ public class TestModels {
|
|||
public void tearDown() throws Exception {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenDigitalSignature() throws Exception {
|
||||
logger.info("Start testing OIDC token response object binding...");
|
||||
TokenResponse tr = new ObjectMapper().readValue(new File("src/test/resources/oidc-token-response.json"),
|
||||
TokenResponse.class);
|
||||
|
||||
// Valid signature
|
||||
Assert.assertFalse("Token signature is valid", ModelUtils.isSignatureValid(tr.getAccessToken(),
|
||||
ModelUtils.createRSAPublicKey(
|
||||
new String(Files.readAllBytes(Paths.get("src/test/resources/rsa-public-key.pem"))))));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenResponseForOIDC() throws Exception {
|
||||
logger.info("Start testing OIDC token response object binding...");
|
||||
|
@ -38,7 +53,7 @@ public class TestModels {
|
|||
|
||||
logger.debug("OIDC token response:\n{}", ModelUtils.toJSONString(tr, true));
|
||||
checkTokenResponse(tr);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -112,13 +127,13 @@ public class TestModels {
|
|||
public static void checkRefreshToken(RefreshToken rt) {
|
||||
logger.debug("Refresh token:\n{}", ModelUtils.toJSONString(rt, true));
|
||||
Assert.assertNotNull("Other claims are null", rt.getOtherClaims());
|
||||
Assert.assertNotNull("Audience is null",rt.getAudience());
|
||||
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());
|
||||
Assert.assertNull("Expiration is not null", rt.getExp());
|
||||
}
|
||||
|
||||
public static void checkTokenIntrospectionResponse(TokenIntrospectionResponse tir) {
|
|
@ -0,0 +1 @@
|
|||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjyJAmPx5K2eGYjvRmiBC8as3nF/jsYSUgBnlul9TNEdWSPuxTzntTb37xDGPmVRkNyOCJmRnBcI8GbrWz8SHJ643JTKp8yx4zDQCgLD72crb9ah/Tfu8KpDz3+FRuYLE4EvvRCGBnsFO2vSM02iTAp7nSToOCX4jCCrDMBUUJkIzuZIQUBTx8lvWl/M6LtQAqS7Gw3wsZSklRcvsR9qlCUxJW3cvhALt9wWrejSJ3LaR6TMaNa8k6Ojk6bJ3/5c6OifxYde0YjXIOaeMkkgnfoQvs4cyhvNxCXLx65+VKV4Dlts7cTgJvuodV0w/UhJGfUxgA9V6IQowmwHNBnYAMwIDAQAB
|
Loading…
Reference in New Issue