Added predictive infrastructure URL support based on context (and on context and realm if the target realm is not the default one) and overloaded all methods that take the URL as argument with the context (#23655)

This commit is contained in:
Mauro Mugnaini 2022-07-15 18:19:35 +02:00
parent 9577388a09
commit 168a1d4b35
4 changed files with 363 additions and 47 deletions

View File

@ -3,7 +3,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
# Changelog for "keycloak-client"
## [v2.0.0-SNAPSHOT]
- Removed the discovery functionality to be compatible with SmartGears.v4 and moved to the new library `keycloak-client-legacy-is` that will provide the backward compatibility. (#23478). Fixed typo in `AccessToken` class for `setAccessToken(..)` method (#23654)
- Removed the discovery functionality to be compatible with SmartGears.v4 and moved to the new library `keycloak-client-legacy-is` that will provide the backward compatibility. (#23478).
- Fixed typo in `AccessToken` class for `setAccessToken(..)` method (#23654)
- Added predictive infrastructure URL support based on context (and on context and realm if the target realm is not the default one) and overloaded all methods that take the URL as argument with the context (#23655)
## [v1.3.0-SNAPSHOT]
- Added functions to introspect and verify access tokens (both OIDC and UMA are supported) (#23326).

View File

@ -35,6 +35,29 @@ public class DefaultKeycloakClient implements KeycloakClient {
protected static Logger logger = LoggerFactory.getLogger(KeycloakClient.class);
public static final String BASE_URL = "https://url.d4science.org/auth/realms/";
@Override
public URL getRealmBaseURL(String context) throws KeycloakClientException {
return getRealmBaseURL(context, DEFAULT_REALM);
}
@Override
public URL getRealmBaseURL(String context, String realm) throws KeycloakClientException {
String urlString = BASE_URL + realm + "/";
if (!context.startsWith(PROD_ROOT_SCOPE)) {
String root = context.split("/")[1];
urlString = urlString.replace("url", "url." + root.replaceAll("\\.", "-"));
}
try {
return new URL(urlString);
} catch (MalformedURLException e) {
// That should be almost impossible
logger.warn("Cannot create base URL from string: {}", urlString, e);
return null;
}
}
@Override
public URL getTokenEndpointURL(URL realmBaseURL) throws KeycloakClientException {
logger.debug("Constructing token endpoint URL starting from base URL: {}", realmBaseURL);
@ -89,6 +112,13 @@ public class DefaultKeycloakClient implements KeycloakClient {
}
}
@Override
public TokenResponse queryOIDCToken(String context, String clientId, String clientSecret)
throws KeycloakClientException {
return queryOIDCToken(getTokenEndpointURL(getRealmBaseURL(context)), clientId, clientSecret);
}
@Override
public TokenResponse queryOIDCToken(URL tokenURL, String clientId, String clientSecret)
throws KeycloakClientException {
@ -100,6 +130,11 @@ public class DefaultKeycloakClient implements KeycloakClient {
return "Basic " + Base64.getEncoder().encodeToString((clientId + ":" + clientSecret).getBytes());
}
@Override
public TokenResponse queryOIDCToken(String context, String authorization) throws KeycloakClientException {
return queryOIDCToken(getTokenEndpointURL(getRealmBaseURL(context)), authorization);
}
@Override
public TokenResponse queryOIDCToken(URL tokenURL, String authorization) throws KeycloakClientException {
logger.debug("Querying OIDC token from Keycloak server with URL: {}", tokenURL);
@ -110,6 +145,13 @@ public class DefaultKeycloakClient implements KeycloakClient {
return performRequest(tokenURL, authorization, params);
}
@Override
public TokenResponse queryUMAToken(String context, TokenResponse oidcTokenResponse, String audience,
List<String> permissions) throws KeycloakClientException {
return queryUMAToken(getTokenEndpointURL(getRealmBaseURL(context)), oidcTokenResponse, audience, permissions);
}
@Override
public TokenResponse queryUMAToken(URL tokenURL, TokenResponse oidcTokenResponse, String audience,
List<String> permissions) throws KeycloakClientException {
@ -121,6 +163,14 @@ public class DefaultKeycloakClient implements KeycloakClient {
return "Bearer " + oidcTokenResponse.getAccessToken();
}
@Override
public TokenResponse queryUMAToken(String context, String clientId, String clientSecret, String audience,
List<String> permissions) throws KeycloakClientException {
return queryUMAToken(getTokenEndpointURL(getRealmBaseURL(context)), clientId, clientSecret, audience,
permissions);
}
@Override
public TokenResponse queryUMAToken(URL tokenURL, String clientId, String clientSecret, String audience,
List<String> permissions) throws KeycloakClientException {
@ -130,6 +180,13 @@ public class DefaultKeycloakClient implements KeycloakClient {
audience, permissions);
}
@Override
public TokenResponse queryUMAToken(String context, String authorization, String audience,
List<String> permissions) throws KeycloakClientException {
return queryUMAToken(getTokenEndpointURL(getRealmBaseURL(context)), authorization, audience, permissions);
}
@Override
public TokenResponse queryUMAToken(URL tokenURL, String authorization, String audience,
List<String> permissions) throws KeycloakClientException {
@ -180,9 +237,9 @@ public class DefaultKeycloakClient implements KeycloakClient {
String queryString = params.entrySet().stream()
.flatMap(p -> p.getValue().stream().map(v -> p.getKey() + "=" + v))
.reduce((p1, p2) -> p1 + "&" + p2).orElse("");
logger.trace("query string is {}", queryString);
request = GXHTTPStringRequest.newRequest(tokenURL.toString())
.header("Content-Type", "application/x-www-form-urlencoded").withBody(queryString);
@ -207,7 +264,7 @@ public class DefaultKeycloakClient implements KeycloakClient {
throw new KeycloakClientException("Cannot construct token response object correctly", e);
}
} else {
throw KeycloakClientException.create("Unable to get token", response.getHTTPCode(),
throw KeycloakClientException.create("Unable to get token", response.getHTTPCode(),
response.getHeaderFields()
.getOrDefault("content-type", Collections.singletonList("unknown/unknown")).get(0),
response.getMessage());
@ -226,11 +283,23 @@ public class DefaultKeycloakClient implements KeycloakClient {
return audience;
}
@Override
public TokenResponse refreshToken(String context, TokenResponse tokenResponse) throws KeycloakClientException {
return refreshToken(getTokenEndpointURL(getRealmBaseURL(context)), tokenResponse);
}
@Override
public TokenResponse refreshToken(URL tokenURL, TokenResponse tokenResponse) throws KeycloakClientException {
return refreshToken(tokenURL, null, null, tokenResponse);
}
@Override
public TokenResponse refreshToken(String context, String clientId, String clientSecret, TokenResponse tokenResponse)
throws KeycloakClientException {
return refreshToken(getTokenEndpointURL(getRealmBaseURL(context)), clientId, clientSecret, tokenResponse);
}
@Override
public TokenResponse refreshToken(URL tokenURL, String clientId, String clientSecret, TokenResponse tokenResponse)
throws KeycloakClientException {
@ -246,6 +315,15 @@ public class DefaultKeycloakClient implements KeycloakClient {
return refreshToken(tokenURL, clientId, clientSecret, tokenResponse.getRefreshToken());
}
@Override
public TokenResponse refreshToken(String context, String clientId, String clientSecret,
String refreshTokenJWTString)
throws KeycloakClientException {
return refreshToken(getTokenEndpointURL(getRealmBaseURL(context)), clientId, clientSecret,
refreshTokenJWTString);
}
@Override
public TokenResponse refreshToken(URL tokenURL, String clientId, String clientSecret, String refreshTokenJWTString)
throws KeycloakClientException {
@ -306,6 +384,14 @@ public class DefaultKeycloakClient implements KeycloakClient {
}
}
@Override
public TokenIntrospectionResponse introspectAccessToken(String context, String clientId, String clientSecret,
String accessTokenJWTString) throws KeycloakClientException {
return introspectAccessToken(getIntrospectionEndpointURL(getRealmBaseURL(context)), clientId, clientSecret,
accessTokenJWTString);
}
@Override
public TokenIntrospectionResponse introspectAccessToken(URL introspectionURL, String clientId, String clientSecret,
String accessTokenJWTString) throws KeycloakClientException {
@ -362,6 +448,14 @@ public class DefaultKeycloakClient implements KeycloakClient {
}
}
@Override
public boolean isAccessTokenVerified(String context, String clientId, String clientSecret,
String accessTokenJWTString) throws KeycloakClientException {
return isAccessTokenVerified(getIntrospectionEndpointURL(getRealmBaseURL(context)), clientId, clientSecret,
accessTokenJWTString);
}
@Override
public boolean isAccessTokenVerified(URL introspectionURL, String clientId, String clientSecret,
String accessTokenJWTString) throws KeycloakClientException {

View File

@ -8,13 +8,36 @@ import org.gcube.common.keycloak.model.TokenResponse;
public interface KeycloakClient {
public static final String PROD_ROOT_SCOPE = "/d4science.research-infrastructures.eu";
public static final String OPEN_ID_URI_PATH = "protocol/openid-connect";
public static final String TOKEN_URI_PATH = "token";
public static final String TOKEN_INTROSPECT_URI_PATH = "introspect";
public static String DEFAULT_REALM = "d4science";
/**
* Returns the Keycloak base {@link URL} for the given context and the default realm (<code>d4science</code>)
*
* @param context the context where the endpoint is needed (e.g. <code>/gcube</code> for DEV)
* @return the Keycloak <code>token</code> endpoint URL
* @throws KeycloakClientException if something goes wrong discovering the endpoint URL
*/
URL getRealmBaseURL(String context) throws KeycloakClientException;
/**
* Returns the Keycloak base {@link URL} for the given context and in the given realm.
*
* @param context the context where the endpoint is needed (e.g. <code>/gcube</code> for DEV)
* @param realm the realm to use to construct the base URL
* @return the Keycloak <code>token</code> endpoint URL
* @throws KeycloakClientException if something goes wrong discovering the endpoint URL
*/
URL getRealmBaseURL(String context, String realm) throws KeycloakClientException;
/**
* Constructs the Keycloak <code>token</code> endpoint {@link URL} from the realm's base URL.
*
* @param realmBaseURL the realm's base URL to use
* @return the Keycloak <code>token</code> endpoint URL
* @throws KeycloakClientException if something goes wrong discovering the endpoint URL
*/
@ -23,6 +46,7 @@ public interface KeycloakClient {
/**
* Constructs the Keycloak <code>introspection</code> endpoint {@link URL} from the realm's base URL.
*
* @param realmBaseURL the realm's base URL to use
* @return the Keycloak <code>introspection</code> endpoint URL
* @throws KeycloakClientException if something goes wrong discovering the endpoint URL
*/
@ -31,11 +55,23 @@ public interface KeycloakClient {
/**
* Compute the keycloak <code>introspection</code> endpoint {@link URL} starting from the provided token endpoint.
*
* @param tokenEndpointURL the token endpoint to use in the compute
* @return the keycloak <code>introspection</code> endpoint URL
* @throws KeycloakClientException if something goes wrong discovering the endpoint URL
*/
URL computeIntrospectionEndpointURL(URL tokenEndpointURL) 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
* @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) throws KeycloakClientException;
/**
* Queries an OIDC token from the Keycloak server, by using provided clientId and client secret.
*
@ -47,6 +83,17 @@ public interface KeycloakClient {
*/
TokenResponse queryOIDCToken(URL tokenURL, String clientId, String clientSecret) 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.
*
@ -57,6 +104,20 @@ public interface KeycloakClient {
*/
TokenResponse queryOIDCToken(URL tokenURL, String authorization) throws KeycloakClientException;
/**
* Queries an UMA token from the Keycloak server, by using provided authorization, for the given audience (context),
* in URLEncoded form or not, and optionally a list of permissions.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
* @param audience the audience (context) where to request the issuing of the ticket (URLEncoded)
* @param permissions a list of permissions, can be <code>null</code>
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryUMAToken(String context, String authorization, String audience, List<String> permissions)
throws KeycloakClientException;
/**
* Queries an UMA token from the Keycloak server, by using provided authorization, for the given audience (context),
* in URLEncoded form or not, and optionally a list of permissions.
@ -75,6 +136,35 @@ public interface KeycloakClient {
* Queries an UMA token from the Keycloak server, by using access-token provided by the {@link TokenResponse} object
* for the given audience (context), in URLEncoded form or not, and optionally a list of permissions.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param tokenResponse the previously issued token as {@link TokenResponse} object
* @param audience the audience (context) where to request the issuing of the ticket
* @param permissions a list of permissions, can be <code>null</code>
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryUMAToken(String context, TokenResponse oidcTokenResponse, String audience,
List<String> permissions) throws KeycloakClientException;
/**
* Queries an UMA token from the Keycloak server, by using access-token provided by the {@link TokenResponse} object
* for the given audience (context), in URLEncoded form or not, and optionally a list of permissions.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param tokenResponse the previously issued token as {@link TokenResponse} object
* @param audience the audience (context) where to request the issuing of the ticket
* @param permissions a list of permissions, can be <code>null</code>
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryUMAToken(URL tokenURL, TokenResponse oidcTokenResponse, String audience,
List<String> permissions) throws KeycloakClientException;
/**
* Queries an UMA token from the Keycloak server, by using provided clientId and client secret for the given audience
* (context), in URLEncoded form or not, and optionally a list of permissions.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param clientId the client id
* @param clientSecret the client secret
* @param audience the audience (context) where to request the issuing of the ticket
@ -82,7 +172,7 @@ public interface KeycloakClient {
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryUMAToken(URL tokenURL, TokenResponse oidcTokenResponse, String audience,
TokenResponse queryUMAToken(String context, String clientId, String clientSecret, String audience,
List<String> permissions) throws KeycloakClientException;
/**
@ -100,6 +190,20 @@ public interface KeycloakClient {
TokenResponse queryUMAToken(URL tokenURL, String clientId, String clientSecret, String audience,
List<String> permissions) throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server using the refresh token JWT encoded string in the
* token response object.
*
* Client id will be read from "issued for" access token's claim and client secret will be not sent.
* <br><b>NOTE</b>: For <code>public</code> clients types only.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param tokenResponse the previously issued token as {@link TokenResponse} object
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(String context, TokenResponse tokenResponse) throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server using the refresh token JWT encoded string in the
* token response object.
@ -118,6 +222,21 @@ public interface KeycloakClient {
* Refreshes a previously issued token from the Keycloak server using the refresh token JWT encoded string in the
* token response object and the provided client id and secret.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param clientId the requestor client id, may be <code>null</code> and in this case will be take from the access token "issued for" claim
* @param clientSecret the requestor client secret, may be <code>null</code> for non-confidential clients
* @param tokenResponse the previously issued token as {@link TokenResponse} object
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(String context, String clientId, String clientSecret, TokenResponse tokenResponse)
throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server using the refresh token JWT encoded string in the
* token response object and the provided client id and secret.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param clientId the requestor client id, may be <code>null</code> and in this case will be take from the access token "issued for" claim
* @param clientSecret the requestor client secret, may be <code>null</code> for non-confidential clients
* @param tokenResponse the previously issued token as {@link TokenResponse} object
@ -127,6 +246,20 @@ public interface KeycloakClient {
TokenResponse refreshToken(URL tokenURL, String clientId, String clientSecret, TokenResponse tokenResponse)
throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server by using the client id and secret
* and the refresh token JWT encoded string obtained with the access token in the previous token response.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param clientId the requestor client id
* @param clientSecret the requestor client secret, may be <code>null</code> for non-confidential clients
* @param refreshTokenJWTString the previously issued refresh token JWT string
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(String context, String clientId, String clientSecret, String refreshTokenJWTString)
throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server by using the client id and secret
* and the refresh token JWT encoded string obtained with the access token in the previous token response.
@ -141,6 +274,19 @@ public interface KeycloakClient {
TokenResponse refreshToken(URL tokenURL, String clientId, String clientSecret, String refreshTokenJWTString)
throws KeycloakClientException;
/**
* Introspects an access token against the Keycloak server.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param clientId the requestor client id
* @param clientSecret the requestor client secret
* @param accessTokenJWTString the access token to verify
* @return a {@link TokenIntrospectionResponse} object with the introspection results; in particular, the <code>active</code> field represents the token validity
* @throws KeycloakClientException if something goes wrong performing the verification
*/
TokenIntrospectionResponse introspectAccessToken(String context, String clientId, String clientSecret,
String accessTokenJWTString) throws KeycloakClientException;
/**
* Introspects an access token against the Keycloak server.
*
@ -151,7 +297,21 @@ public interface KeycloakClient {
* @return a {@link TokenIntrospectionResponse} object with the introspection results; in particular, the <code>active</code> field represents the token validity
* @throws KeycloakClientException if something goes wrong performing the verification
*/
TokenIntrospectionResponse introspectAccessToken(URL introspectionURL, String clientId, String clientSecret, String accessTokenJWTString) throws KeycloakClientException;
TokenIntrospectionResponse introspectAccessToken(URL introspectionURL, String clientId, String clientSecret,
String accessTokenJWTString) throws KeycloakClientException;
/**
* Verifies an access token against the Keycloak server.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param clientId the requestor client id
* @param clientSecret the requestor client secret
* @param accessTokenJWTString the access token to verify
* @return <code>true</code> if the token is active, <code>false</code> otherwise
* @throws KeycloakClientException if something goes wrong performing the verification
*/
boolean isAccessTokenVerified(String context, String clientId, String clientSecret, String accessTokenJWTString)
throws KeycloakClientException;
/**
* Verifies an access token against the Keycloak server.
@ -163,6 +323,7 @@ public interface KeycloakClient {
* @return <code>true</code> if the token is active, <code>false</code> otherwise
* @throws KeycloakClientException if something goes wrong performing the verification
*/
boolean isAccessTokenVerified(URL introspectionURL, String clientId, String clientSecret, String accessTokenJWTString) throws KeycloakClientException;
boolean isAccessTokenVerified(URL introspectionURL, String clientId, String clientSecret,
String accessTokenJWTString) throws KeycloakClientException;
}

View File

@ -19,7 +19,8 @@ public class TestKeycloakClient {
protected static final Logger logger = LoggerFactory.getLogger(TestKeycloakClient.class);
protected static final String DEV_BASE_URL = "https://accounts.dev.d4science.org/auth/realms/d4science";
protected static final String DEV_ROOT_CONTEXT = "/gcube";
protected static final String DEV_BASE_URL = "https://url.gcube.d4science.org/auth/realms/d4science";
protected static final String DEV_TOKEN_ENDPOINT = DEV_BASE_URL + "/protocol/openid-connect/token";
protected static final String DEV_INTROSPECTION_ENDPOINT = DEV_TOKEN_ENDPOINT + "/introspect";
@ -29,6 +30,8 @@ public class TestKeycloakClient {
protected static final String OLD_OIDC_ACCESS_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSSklZNEpoNF9qdDdvNmREY0NlUDFfS1l0akcxVExXVW9oMkQ2Tzk1bFNBIn0.eyJleHAiOjE2NTI5Nzk4NDUsImlhdCI6MTY1Mjk3OTU0NSwianRpIjoiMzQ2MjgwMWItODg4NS00YTM4LWJkNDUtNWExM2U1MGE5MGU5IiwiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5kZXYuZDRzY2llbmNlLm9yZy9hdXRoL3JlYWxtcy9kNHNjaWVuY2UiLCJhdWQiOlsiJTJGZ2N1YmUiLCIlMkZnY3ViZSUyRmRldnNlYyUyRmRldlZSRSIsImFjY291bnQiXSwic3ViIjoiYTQ3ZGZlMTYtYjRlZC00NGVkLWExZDktOTdlY2Q1MDQzNjBjIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoia2V5Y2xvYWstY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6ImQ4MDk3MDBmLWEyNDUtNDI3Zi1hYzhjLTQxYjFkZDNkYTQ3MCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsIkluZnJhc3RydWN0dXJlLUNsaWVudCIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiJTJGZ2N1YmUiOnsicm9sZXMiOlsiTWVtYmVyIl19LCJrZXljbG9hay1jbGllbnQiOnsicm9sZXMiOlsidW1hX3Byb3RlY3Rpb24iXX0sIiUyRmdjdWJlJTJGZGV2c2VjJTJGZGV2VlJFIjp7InJvbGVzIjpbIk1lbWJlciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SWQiOiJrZXljbG9hay1jbGllbnQiLCJjbGllbnRIb3N0IjoiOTMuNjYuMTg1Ljc1IiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQta2V5Y2xvYWstY2xpZW50IiwiY2xpZW50QWRkcmVzcyI6IjkzLjY2LjE4NS43NSJ9.FQu4ox2HWeqeaY7nHYVGeJVpkJOcASfOb8tbOUeG-GB6sMjRB2S8PjLLaw63r_c42yxKszP04XdxGqIWqXTtoD9QCiUHTT5yJTkIpio4tMMGHth9Fbx-9dwk0yy_IFi1_OsCvZFmOQRdjMuUkj1lSqslCzAw-2E5q1Zt415-au5pEVJYNTFqIsG72ChJwh6eq1Dh1XBy8krb7YVPQyIwxO_awgAYO5hbsdvXYlRfCrnB38kk2V6-CQ-XYoL1m7xIB-gjhKCiFvDmmntQSRCZFgb0qi8eOmh9FdzPxZgx7yPJwAAj17dS4B_gz9FpZBVciNzpA6Lf4P2bqvoD9-R6ow";
protected static final String OLD_UMA_ACCESS_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSSklZNEpoNF9qdDdvNmREY0NlUDFfS1l0akcxVExXVW9oMkQ2Tzk1bFNBIn0.eyJleHAiOjE2NTI5ODA0NzgsImlhdCI6MTY1Mjk4MDE3OCwianRpIjoiNjBkNzU3MGMtZmQxOC00NGQ1LTg1MzUtODhlMmFmOGQ1ZTgwIiwiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5kZXYuZDRzY2llbmNlLm9yZy9hdXRoL3JlYWxtcy9kNHNjaWVuY2UiLCJhdWQiOiJjb25kdWN0b3Itc2VydmVyIiwic3ViIjoiYTQ3ZGZlMTYtYjRlZC00NGVkLWExZDktOTdlY2Q1MDQzNjBjIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoia2V5Y2xvYWstY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjI3NDUyN2M5LWNkZjMtNGM2Yi1iNTUxLTFmMTRkZGE5ZGVlZiIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiSW5mcmFzdHJ1Y3R1cmUtQ2xpZW50Il19LCJhdXRob3JpemF0aW9uIjp7InBlcm1pc3Npb25zIjpbeyJzY29wZXMiOlsiZ2V0Il0sInJzaWQiOiIyNDlmZDQ2OS03OWM1LTRiODUtYjE5NS1mMjliM2ViNjAzNDUiLCJyc25hbWUiOiJtZXRhZGF0YSJ9LHsic2NvcGVzIjpbImdldCIsInN0YXJ0IiwidGVybWluYXRlIl0sInJzaWQiOiJhNmYzZWFkZS03NDA0LTRlNWQtOTA3MC04MDBhZGI1YWFjNGUiLCJyc25hbWUiOiJ3b3JrZmxvdyJ9LHsicnNpZCI6IjFiNmMwMGI3LTkxMzktNGVhYS1hYWM3LTIwMjMxZmVlMDVhNSIsInJzbmFtZSI6IkRlZmF1bHQgUmVzb3VyY2UifV19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJjbGllbnRJZCI6ImtleWNsb2FrLWNsaWVudCIsImNsaWVudEhvc3QiOiI5My42Ni4xODUuNzUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1rZXljbG9hay1jbGllbnQiLCJjbGllbnRBZGRyZXNzIjoiOTMuNjYuMTg1Ljc1In0.Hh62E56R-amHwoDPFQEylMvrvmNzWnC_4bDI7_iQYAPJ5YzCNH9d7zcdGaQ96kRmps_JRc2Giv_1W9kYorOhlXl-5QLDrSoqrqFxrNpEGG5r5jpNJbusbu4wNUKiCt_GMnM1UmztgXiQeuggNGkmeBIjotj0eubnmIbUV9ukHj3v7Z5PwNKKX3BCpsghd1u8lg6Nfqk_Oho4GXUfdaFY_AR3SNqzVI_9YLhND_a03MNNWlnfOvj8T4nDCKBZIs91tVyiu98d2TjnQt8PdlVwokMP3LA58m0Khy2cmUm1KF2k0zlzP8MxV9wTxNrpovMr-PnbtEPZ_IlVQIzHwjHfwQ";
protected static URL tokenURL;
protected static URL introspectionURL;
protected static TokenResponse oidcTR = null;
protected static TokenResponse umaTR = null;
@ -43,49 +46,34 @@ public class TestKeycloakClient {
@Test
public void test00TokenEndpointConstruction() throws Exception {
logger.info("*** [0.0] Start testing Keycloak token endpoint construction from base URL...");
URL url = KeycloakClientFactory.newInstance().getTokenEndpointURL(new URL(DEV_BASE_URL));
Assert.assertNotNull(url);
Assert.assertTrue(url.getProtocol().equals("https"));
Assert.assertEquals(new URL(DEV_TOKEN_ENDPOINT), url);
logger.info("Constructed URL is: {}", url);
}
@Test
public void test00ATokenEndpointConstructionWithTrailingSlash() throws Exception {
logger.info("*** [0.0] Start testing Keycloak token endpoint construction from base URL with trailing slash...");
URL url = KeycloakClientFactory.newInstance().getTokenEndpointURL(new URL(DEV_BASE_URL + "/"));
Assert.assertNotNull(url);
Assert.assertTrue(url.getProtocol().equals("https"));
Assert.assertEquals(new URL(DEV_TOKEN_ENDPOINT), url);
logger.info("Constructed URL is: {}", url);
KeycloakClient client = KeycloakClientFactory.newInstance();
tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
Assert.assertNotNull(tokenURL);
Assert.assertTrue(tokenURL.getProtocol().equals("https"));
Assert.assertEquals(new URL(DEV_TOKEN_ENDPOINT), tokenURL);
logger.info("Constructed URL is: {}", tokenURL);
}
@Test
public void test01IntrospectionEndpointConstruction() throws Exception {
logger.info("*** [0.0] Start testing Keycloak introspection endpoint construction from base URL...");
URL url = KeycloakClientFactory.newInstance().getIntrospectionEndpointURL(new URL(DEV_BASE_URL));
Assert.assertNotNull(url);
Assert.assertTrue(url.getProtocol().equals("https"));
Assert.assertEquals(new URL(DEV_INTROSPECTION_ENDPOINT), url);
logger.info("Constructed URL is: {}", url);
}
@Test
public void test01AIntrospectionEndpointConstructionWithTrailingSlash() throws Exception {
logger.info("*** [0.0] Start testing Keycloak introspection endpoint construction from base URL with trailing slash...");
URL url = KeycloakClientFactory.newInstance().getIntrospectionEndpointURL(new URL(DEV_BASE_URL + "/"));
Assert.assertNotNull(url);
Assert.assertTrue(url.getProtocol().equals("https"));
Assert.assertEquals(new URL(DEV_INTROSPECTION_ENDPOINT), url);
logger.info("Constructed URL is: {}", url);
KeycloakClient client = KeycloakClientFactory.newInstance();
introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
Assert.assertNotNull(introspectionURL);
Assert.assertTrue(introspectionURL.getProtocol().equals("https"));
Assert.assertEquals(new URL(DEV_INTROSPECTION_ENDPOINT), introspectionURL);
logger.info("Constructed URL is: {}", introspectionURL);
}
@Test
public void test02IntrospectEndpointCompute() throws Exception {
logger.info("*** [0.2] Start testing Keycloak userinfo endpoint computed from provided URL...");
URL url = KeycloakClientFactory.newInstance().computeIntrospectionEndpointURL(new URL(DEV_TOKEN_ENDPOINT));
KeycloakClient client = KeycloakClientFactory.newInstance();
URL url = client
.computeIntrospectionEndpointURL(client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT)));
Assert.assertNotNull(url);
Assert.assertTrue(url.getProtocol().equals("https"));
Assert.assertEquals(introspectionURL, url);
Assert.assertEquals(new URL(DEV_INTROSPECTION_ENDPOINT), url);
logger.info("Computed URL is: {}", url);
}
@ -93,7 +81,19 @@ public class TestKeycloakClient {
@Test
public void test12QueryOIDCToken() throws Exception {
logger.info("*** [1.2] Start testing query OIDC token from Keycloak with URL...");
oidcTR = KeycloakClientFactory.newInstance().queryOIDCToken(new URL(DEV_TOKEN_ENDPOINT), CLIENT_ID,
oidcTR = KeycloakClientFactory.newInstance().queryOIDCToken(DEV_ROOT_CONTEXT, CLIENT_ID,
CLIENT_SECRET);
logger.info("*** [1.2] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.2] OIDC refresh token: {}", oidcTR.getRefreshToken());
TestModels.checkTokenResponse(oidcTR);
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), "service-account-" + CLIENT_ID, false);
}
@Test
public void test12aQueryOIDCToken() throws Exception {
logger.info("*** [1.2] Start testing query OIDC token from Keycloak with URL...");
oidcTR = KeycloakClientFactory.newInstance().queryOIDCToken(tokenURL, CLIENT_ID,
CLIENT_SECRET);
logger.info("*** [1.2] OIDC access token: {}", oidcTR.getAccessToken());
@ -105,7 +105,20 @@ public class TestKeycloakClient {
@Test
public void test24QueryUMAToken() throws Exception {
logger.info("*** [2.4] Start testing query UMA token from Keycloak with URL...");
umaTR = KeycloakClientFactory.newInstance().queryUMAToken(new URL(DEV_TOKEN_ENDPOINT), CLIENT_ID, CLIENT_SECRET,
umaTR = KeycloakClientFactory.newInstance().queryUMAToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
TEST_AUDIENCE, null);
logger.info("*** [2.4] UMA access token: {}", umaTR.getAccessToken());
logger.info("*** [2.4] UMA refresh token: {}", umaTR.getRefreshToken());
TestModels.checkTokenResponse(umaTR);
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(umaTR), "service-account-" + CLIENT_ID, true);
}
@Test
public void test24aQueryUMAToken() throws Exception {
logger.info("*** [2.4] Start testing query UMA token from Keycloak with URL...");
umaTR = KeycloakClientFactory.newInstance().queryUMAToken(tokenURL, CLIENT_ID, CLIENT_SECRET,
TEST_AUDIENCE, null);
logger.info("*** [2.4] UMA access token: {}", umaTR.getAccessToken());
@ -119,7 +132,16 @@ public class TestKeycloakClient {
public void test302IntrospectOIDCAccessToken() throws Exception {
logger.info("*** [3.2] Start testing introspect OIDC access token...");
TokenIntrospectionResponse tir = KeycloakClientFactory.newInstance().introspectAccessToken(
new URL(DEV_INTROSPECTION_ENDPOINT), CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken());
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken());
TestModels.checkTokenIntrospectionResponse(tir);
}
@Test
public void test302aIntrospectOIDCAccessToken() throws Exception {
logger.info("*** [3.2] Start testing introspect OIDC access token...");
TokenIntrospectionResponse tir = KeycloakClientFactory.newInstance().introspectAccessToken(
introspectionURL, CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken());
TestModels.checkTokenIntrospectionResponse(tir);
}
@ -128,7 +150,16 @@ public class TestKeycloakClient {
public void test304IntrospectUMAAccessToken() throws Exception {
logger.info("*** [3.4] Start testing introspect UMA access token...");
TokenIntrospectionResponse tir = KeycloakClientFactory.newInstance().introspectAccessToken(
new URL(DEV_INTROSPECTION_ENDPOINT), CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken());
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken());
TestModels.checkTokenIntrospectionResponse(tir);
}
@Test
public void test304aIntrospectUMAAccessToken() throws Exception {
logger.info("*** [3.4] Start testing introspect UMA access token...");
TokenIntrospectionResponse tir = KeycloakClientFactory.newInstance().introspectAccessToken(
introspectionURL, CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken());
TestModels.checkTokenIntrospectionResponse(tir);
}
@ -137,27 +168,55 @@ public class TestKeycloakClient {
public void test306OIDCAccessTokenVerification() throws Exception {
logger.info("*** [3.6] Start OIDC access token verification...");
Assert.assertTrue(KeycloakClientFactory.newInstance().isAccessTokenVerified(
new URL(DEV_INTROSPECTION_ENDPOINT), CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken()));
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken()));
}
@Test
public void test306aOIDCAccessTokenVerification() throws Exception {
logger.info("*** [3.6] Start OIDC access token verification...");
Assert.assertTrue(KeycloakClientFactory.newInstance().isAccessTokenVerified(
introspectionURL, CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken()));
}
@Test
public void test307OIDCAccessTokenNonVerification() throws Exception {
logger.info("*** [3.7] Start OIDC access token NON verification...");
Assert.assertFalse(KeycloakClientFactory.newInstance().isAccessTokenVerified(
new URL(DEV_INTROSPECTION_ENDPOINT), CLIENT_ID, CLIENT_SECRET, OLD_OIDC_ACCESS_TOKEN));
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, OLD_OIDC_ACCESS_TOKEN));
}
@Test
public void test307aOIDCAccessTokenNonVerification() throws Exception {
logger.info("*** [3.7] Start OIDC access token NON verification...");
Assert.assertFalse(KeycloakClientFactory.newInstance().isAccessTokenVerified(
introspectionURL, CLIENT_ID, CLIENT_SECRET, OLD_OIDC_ACCESS_TOKEN));
}
@Test
public void test309UMAAccessTokenVerification() throws Exception {
logger.info("*** [3.9] Start UMA access token verification...");
Assert.assertTrue(KeycloakClientFactory.newInstance().isAccessTokenVerified(
new URL(DEV_INTROSPECTION_ENDPOINT), CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken()));
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken()));
}
@Test
public void test309aUMAAccessTokenVerification() throws Exception {
logger.info("*** [3.9] Start UMA access token verification...");
Assert.assertTrue(KeycloakClientFactory.newInstance().isAccessTokenVerified(
introspectionURL, CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken()));
}
@Test
public void test310UMAAccessTokenNonVerification() throws Exception {
logger.info("*** [3.10] Start UMA access token NON verification...");
Assert.assertFalse(KeycloakClientFactory.newInstance().isAccessTokenVerified(
new URL(DEV_INTROSPECTION_ENDPOINT), CLIENT_ID, CLIENT_SECRET, OLD_UMA_ACCESS_TOKEN));
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, OLD_UMA_ACCESS_TOKEN));
}
@Test
public void test310aUMAAccessTokenNonVerification() throws Exception {
logger.info("*** [3.10] Start UMA access token NON verification...");
Assert.assertFalse(KeycloakClientFactory.newInstance().isAccessTokenVerified(
introspectionURL, CLIENT_ID, CLIENT_SECRET, OLD_UMA_ACCESS_TOKEN));
}
}