Compare commits

..

No commits in common. "master" and "v1.1.0" have entirely different histories.

30 changed files with 332 additions and 3110 deletions

View File

@ -2,29 +2,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
# Changelog for "keycloak-client"
## [v2.2.0-SNAPSHOT]
- Added support to add scopes in requests in client and constants for d4s-context dynamic scope. (#28084)
- Added tests to demonstrate dynamic scopes for clients and for users on both private and public clients (#28084)
## [v2.1.0]
- Added `token-exchange` support, also with `offline-token` scope, and methods to add extra headers during the OIDC token requests (#27099).
- Added custom base URL set via factory (not automatically working cross environments) [#27234].
- Added JWKS server configuration retrieval, realm's info (as `PublishedRealmRepresentation` JSON containing public key) and JWT digital signature verification by using the RSA public key of the realm on server. It uses the `jjwt` library by `io.jsonwebtoken` [#27340]
## [v2.0.0]
- Removed the discovery functionality to be compatible with SmartGears.v4 and moved to the new library `keycloak-client-legacy-is` that will provide the backward compatibility. (#23478).
- Fixed typo in `AccessToken` class for `setAccessToken(..)` method (#23654)
- 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)
- Added support for the use of the custom Keycloak's D4S mapper that maps/shrink the `aud` (and optionally also the resource access) to the value requested via `X-D4Science-Context` HTTP header.
- Added support of password grant flow (corresponding to the now deprecated OAuth2 flow: Resource Owner Password Credentials grant) also for specific context/audience by using the specific D4S mapper. (#25291)
- Added new `KeycloakClientHelper` class to perform token request for user in one shot and without the need to provide the `clientId` parameter (#25291). Only `context`, `username` and `password` are required. (#25291)
## [v1.3.0-SNAPSHOT]
- Added functions to introspect and verify access tokens (both OIDC and UMA are supported) (#23326).
## [v1.2.0]
- Added OIDC token retrieve for clients [#23076] and UMA token from OIDC token as bearer auth, instead of credentials only (basic auth)
## [v1.1.0]
- Added refresh token facilities for expired tokens (#22515) and some helper methods added.

37
pom.xml
View File

@ -7,19 +7,20 @@
<parent>
<artifactId>maven-parent</artifactId>
<groupId>org.gcube.tools</groupId>
<version>1.2.0</version>
<version>1.1.0</version>
<relativePath />
</parent>
<groupId>org.gcube.common</groupId>
<artifactId>keycloak-client</artifactId>
<version>2.2.0-SNAPSHOT</version>
<version>1.1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.gcube.distribution</groupId>
<artifactId>gcube-bom</artifactId>
<version>2.4.1-SNAPSHOT</version>
<version>2.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -27,20 +28,11 @@
</dependencyManagement>
<scm>
<connection>
scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</connection>
<developerConnection>
scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</developerConnection>
<connection>scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</connection>
<developerConnection>scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</developerConnection>
<url>https://code-repo.d4science.org/gCubeSystem/${project.artifactId}</url>
</scm>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<jjwt.version>0.12.5</jjwt.version>
</properties>
<dependencies>
<dependency>
@ -69,16 +61,19 @@
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jjwt.version}</version>
<groupId>org.gcube.core</groupId>
<artifactId>common-fw-clients</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
<groupId>org.gcube.resources.discovery</groupId>
<artifactId>ic-client</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-scope-maps</artifactId>
<scope>test</scope>
</dependency>
<dependency>

View File

@ -0,0 +1,25 @@
package org.gcube.common.keycloak;
import org.gcube.common.clients.fw.plugin.Plugin;
public abstract class AbstractPlugin<S, P> implements Plugin<S, P>, KeycloakClient {
public final String name;
public AbstractPlugin(String name) {
this.name = name;
}
public String serviceClass() {
return CATEGORY;
}
public String serviceName() {
return NAME;
}
public String name() {
return name;
}
}

View File

@ -2,541 +2,33 @@ package org.gcube.common.keycloak;
import java.net.URL;
import java.util.List;
import java.util.Map;
import org.gcube.common.keycloak.model.JSONWebKeySet;
import org.gcube.common.keycloak.model.PublishedRealmRepresentation;
import org.gcube.common.keycloak.model.TokenIntrospectionResponse;
import org.gcube.common.keycloak.model.TokenResponse;
import org.gcube.common.scope.api.ScopeProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public interface KeycloakClient {
public static final String PROD_ROOT_SCOPE = "/d4science.research-infrastructures.eu";
public static final String OPEN_ID_URI_PATH = "protocol/openid-connect";
public static final String TOKEN_URI_PATH = "token";
public static final String JWK_URI_PATH = "certs";
public static final String TOKEN_INTROSPECT_URI_PATH = "introspect";
public static final String AVATAR_URI_PATH = "account-avatar";
public final static String D4S_CONTEXT_HEADER_NAME = "x-d4science-context";
public final static String D4S_IDENTITY_SCOPE = "d4s-identity";
public final static String D4S_EU_EXTENDED_PROFILE_SCOPE = "eu_extended_profile";
public static final String D4S_DYNAMIC_SCOPE_NAME = "d4s-context";
public static final String D4S_DYNAMIC_SCOPE_NAME_TOKEN_CLAIM = "d4s_context";
public static final String DEFAULT_DYNAMIC_SCOPE_SEPARATOR = ":";
Logger logger = LoggerFactory.getLogger(KeycloakClient.class);
public static String DEFAULT_REALM = "d4science";
String CATEGORY = "Auth";
String NAME = "IAM";
String DESCRIPTION = "oidc-token endpoint";
/**
* Replaces the list of the provided OIDC scopes for the next OIDC token requests
* @param scopes the list of scopes to use in the calls
* @return the client itself
*/
KeycloakClient useScopes(List<String> scopes);
/**
* Adds the provided OIDC scopes to the list of scopes to use for the next OIDC token requests
* @param scopes the list of scopes to add
* @return the client itself
*/
KeycloakClient addScopes(List<String> scopes);
/**
* Adds the dynamic scope to the list of scopes to use for the next OIDC token requests
* @param dynamicScope the dynamic scope that will be the prefix
* @param value the value of the dynamic scope
* @return the client itself
*/
KeycloakClient addDynamicScope(String dynamicScope, String value);
/**
* Removes the provided OIDC scopes from the list of scopes to use for the next OIDC token requests
* @param scopes the list of scopes to remove
* @return the client itself
*/
KeycloakClient removeScopes(List<String> scopes);
/**
* Removes all the custom OIDC scopes from the list of scopes to use the next OIDC token requests
* @return the client itself
*/
KeycloakClient removeAllScopes();
/**
* Sets a flag to use dynamic scope ({@link #D4S_DYNAMIC_SCOPE_NAME} = {@value #D4S_DYNAMIC_SCOPE_NAME}) instead of custom header ({@link #D4S_CONTEXT_HEADER_NAME} = {@value #D4S_CONTEXT_HEADER_NAME})
* when an OIDC token with context is used
* @param use use or not use dynamic scope
* @return the client itself
*/
KeycloakClient useDynamicScopeInsteadOfCustomHeaderForContextRestricion(boolean useDynamicScopeInsteadOfCustomHeaderForContextRestricion);
/**
* Returns the Keycloak base {@link URL} for the given context and the default realm (<code>d4science</code>)
* Finds the keycloak endpoint {@link URL} discovering it in the current scope provided by {@link ScopeProvider}
*
* @param context the context where the endpoint is needed (e.g. <code>/gcube</code> for DEV)
* @return the Keycloak <code>token</code> endpoint URL
* @return the keycloak endpoint URL in the current scope
* @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
*/
URL getTokenEndpointURL(URL realmBaseURL) throws KeycloakClientException;
/**
* Constructs the Keycloak <code>JWK</code> endpoint {@link URL} from the realm's base URL.
*
* @param realmBaseURL the realm's base URL to use
* @return the Keycloak <code>JWK</code> endpoint URL
* @throws KeycloakClientException if something goes wrong discovering the endpoint URL
*/
URL getJWKEndpointURL(URL realmBaseURL) throws KeycloakClientException;
/**
* Constructs the Keycloak <code>introspection</code> endpoint {@link URL} from the realm's base URL.
*
* @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
*/
URL getIntrospectionEndpointURL(URL realmBaseURL) throws KeycloakClientException;
/**
* 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;
/**
* Constructs the Keycloak <code>avatar</code> endpoint {@link URL} from the realm's base URL.
*
* @param realmBaseURL the realm's base URL to use
* @return the Keycloak <code>avatar</code> endpoint URL
* @throws KeycloakClientException if something goes wrong discovering the endpoint URL
*/
URL getAvatarEndpointURL(URL realmBaseURL) throws KeycloakClientException;
/**
* Gets the realm info setup (RSA <code>public_key</code>, <code>token-service</code> URL,
* <code>account-service</code> URL and <code>tokens-not-before</code> setting)
*
* @param realmURL the realm URL
* @return the configured realm info
* @throws KeycloakClientException if something goes wrong getting realm info
*/
PublishedRealmRepresentation getRealmInfo(URL realmURL) throws KeycloakClientException;
/**
* Loads the actual JWK from the Keycloak server
* @param jwkURL the server's jwk URL to use
* @return an object with JWK details
* @throws KeycloakClientException if something goes wrong getting JWK info
*/
JSONWebKeySet getRealmJSONWebKeySet(URL jwkURL) 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 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 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 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
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenWithContext(String context, String clientId, String clientSecret, String audience)
throws KeycloakClientException;
/**
* Queries an OIDC token from the context's Keycloak server, by using provided clientId and client secret, reducing the audience to the requested one.
* Optionally extra HTTP headers can be provided to be used in the call.
*
* The implementation uses the custom <code>x-d4science-context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param clientId the client id
* @param clientSecret the client secret
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @param extraHeaders extra HTTP headers to add to the request
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenWithContext(String context, String clientId, String clientSecret, String audience, Map<String, String> extraHeaders)
throws KeycloakClientException;
/**
* Queries an OIDC token from the Keycloak server, by using provided clientId and client secret, reducing the audience to the requested one.
*
* The implementation uses the custom <code>x-d4science-context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
*
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
* @param clientId the client id
* @param clientSecret the client secret
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenWithContext(URL tokenURL, String clientId, String clientSecret, String audience)
throws KeycloakClientException;
/**
* Queries an OIDC token from the Keycloak server, by using provided clientId and client secret, reducing the audience to the requested one.
* Optionally extra HTTP headers can be provided to be used in the call.
*
* The implementation uses the custom <code>x-d4science-context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
*
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
* @param clientId the client id
* @param clientSecret the client secret
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @param extraHeaders extra HTTP headers to add to the request
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenWithContext(URL tokenURL, String clientId, String clientSecret, String audience, Map<String, String> extraHeaders)
throws KeycloakClientException;
/**
* Queries an OIDC token from the Keycloak server, by using provided authorization, reducing the audience to the requested one.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenWithContext(String context, String authorization, String audience)
throws KeycloakClientException;
/**
* Queries an OIDC token from the Keycloak server, by using provided authorization, reducing the audience to the requested one.
* Optionally extra HTTP headers can be provided to be used in the call.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @param extraHeaders extra HTTP headers to add to the request
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenWithContext(String context, String authorization, String audience, Map<String, String> extraHeaders)
throws KeycloakClientException;
/**
* Queries an OIDC token from the Keycloak server, by using provided authorization, reducing the audience to the requested one.
*
* @param tokenURL the token endpoint {@link URL} of the OIDC server
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenWithContext(URL tokenURL, String authorization, String audience)
throws KeycloakClientException;
/**
* Queries an OIDC token from the Keycloak server, by using provided authorization, reducing the audience to the requested one.
* Optionally extra HTTP headers can be provided to be used in the call.
*
* @param tokenURL the token endpoint {@link URL} of the OIDC server
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @param extraHeaders extra HTTP headers to add to the request
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenWithContext(URL tokenURL, String authorization, String audience, Map<String, String> extraHeaders)
throws KeycloakClientException;
/**
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param clientId the client id
* @param clientSecret the client secret
* @param username the user's username
* @param password the user's password
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenOfUser(String context, String clientId, String clientSecret, String username,
String password) throws KeycloakClientException;
/**
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
* Optionally extra HTTP headers can be provided to be used in the call.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param clientId the client id
* @param clientSecret the client secret
* @param username the user's username
* @param password the user's password
* @param extraHeaders extra HTTP headers to add to the request
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenOfUser(String context, String clientId, String clientSecret, String username,
String password, Map<String, String> extraHeaders) throws KeycloakClientException;
/**
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
* @param username the user's username
* @param password the user's password
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenOfUserWithContext(String context, String authorization, String username,
String password, String audience) throws KeycloakClientException;
/**
* Queries an OIDC token for a specific user from the Keycloak server, by using provided clientId and client secret and user's username and password, reducing the audience to the requested one.
*
* The implementation uses the custom <code>x-d4science-context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param clientId the client id
* @param clientSecret the client secret
* @param username the user's username
* @param password the user's password
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenOfUserWithContext(String context, String clientId, String clientSecret, String username,
String password, String audience) throws KeycloakClientException;
/**
* Queries an OIDC token for a specific user from the Keycloak server, by using provided clientId and client secret and user's username and password, reducing the audience to the requested one.
* Optionally extra HTTP headers can be provided to be used in the call.
*
* The implementation uses the custom <code>x-d4science-context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param clientId the client id
* @param clientSecret the client secret
* @param username the user's username
* @param password the user's password
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @param extraHeaders extra HTTP headers to add to the request
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenOfUserWithContext(String context, String clientId, String clientSecret,
String username, String password, String audience, Map<String, String> extraHeaders) throws KeycloakClientException;
/**
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password, reducing the audience to the requested one.
*
* The implementation uses the custom <code>x-d4science-context</code> HTTP header that the proper mapper on Keycloak uses to reduce the audience
*
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
* @param clientId the client id
* @param clientSecret the client secret
* @param username the user's username
* @param password the user's password
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String clientId, String clientSecret, String username,
String password, String audience) throws KeycloakClientException;
/**
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password, , reducing the audience to the requested one.
* Optionally extra HTTP headers can be provided to be used in the call.
*
* @param tokenURL the token endpoint {@link URL} of the Keycloak server
* @param clientId the client id
* @param clientSecret the client secret
* @param username the user's username
* @param password the user's password
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @param extraHeaders extra HTTP headers to add to the request
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String clientId, String clientSecret,
String username, String password, String audience, Map<String, String> extraHeaders) throws KeycloakClientException;
/**
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
* Optionally extra HTTP headers can be provided to be used in the call.
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
* @param username the user's username
* @param password the user's password
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @param extraHeaders extra HTTP headers to add to the request
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenOfUserWithContext(String context, String authorization, String username,
String password, String audience, Map<String, String> extraHeaders) throws KeycloakClientException;
/**
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
*
* @param tokenURL the token endpoint {@link URL} of the OIDC server
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
* @param username the user's username
* @param password the user's password
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String authorization, String username, String password,
String audience) throws KeycloakClientException;
/**
* Queries an OIDC token for a specific user from the context's Keycloak server, by using provided clientId and client secret and user's username and password.
* Optionally extra HTTP headers can be provided to be used in the call.
*
* @param tokenURL the token endpoint {@link URL} of the OIDC server
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
* @param username the user's username
* @param password the user's password
* @param audience an optional parameter to shrink the token's audience to the requested one (e.g. a specific context), by leveraging on the custom HTTP header and corresponding mapper on Keycloak
* @param extraHeaders extra HTTP headers to add to the request
* @return the issued token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryOIDCTokenOfUserWithContext(URL tokenURL, String authorization, String username,
String password, String audience, Map<String, String> extraHeaders) throws KeycloakClientException;
URL findTokenEndpointURL() 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.
*
* @param tokenURL the token endpoint {@link URL} of the OIDC server
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param authorization the authorization to be set as header (e.g. a "Basic ...." auth or an encoded JWT access token preceded by the "Bearer " string)
* @param audience the audience (context) where to request the issuing of the ticket (URLEncoded)
* @param permissions a list of permissions, can be <code>null</code>
@ -546,49 +38,6 @@ public interface KeycloakClient {
TokenResponse queryUMAToken(URL tokenURL, String authorization, 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 context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param oidcTokenResponse the previously issued token as {@link TokenResponse} object
* @param audience the audience (context) where to request the issuing of the ticket
* @param permissions a list of permissions, can be <code>null</code>
* @return the issued token as {@link TokenResponse} object
* @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 oidcTokenResponse the previously issued token as {@link TokenResponse} object
* @param audience the audience (context) where to request the issuing of the ticket
* @param permissions a list of permissions, can be <code>null</code>
* @return the issued token as {@link TokenResponse} object
* @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
* @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 clientId, String clientSecret, 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.
@ -602,21 +51,48 @@ public interface KeycloakClient {
* @throws KeycloakClientException if something goes wrong performing the query
*/
TokenResponse queryUMAToken(URL tokenURL, String clientId, String clientSecret, String audience,
List<String> permissions) throws KeycloakClientException;
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.
* Queries an UMA token from the Keycloak server discovered in the current scope, by using provided clientId and client secret
* for the given audience (context), in URLEncoded form or not, and optionally a list of permissions.
*
* @param clientId the client id
* @param clientSecret the client secret
* @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 clientId, String clientSecret, String audience, List<String> permissions)
throws KeycloakClientException;
/**
* Queries an UMA token from the Keycloak server discovered in the current scope, by using provided clientId and client secret
* for the current scope audience (context), in URLEncoded form or not, and optionally a list of permissions.
*
* @param clientId the client id
* @param clientSecret the client secret
* @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 clientId, String clientSecret, List<String> permissions)
throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server discovered in the current scope 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;
TokenResponse refreshToken(TokenResponse tokenResponse) throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server using the refresh token JWT encoded string in the
@ -625,7 +101,7 @@ public interface KeycloakClient {
* Client id will be read from "issued for" access token's claim and client secret will be not sent.
* <br><b>NOTE</b>: For <code>public</code> clients types only.
*
* @param tokenURL the token endpoint {@link URL} of the OIDC server
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param tokenResponse the previously issued token as {@link TokenResponse} object
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
@ -633,24 +109,36 @@ public interface KeycloakClient {
TokenResponse refreshToken(URL tokenURL, 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.
* Refreshes a previously issued token from the Keycloak server discovered in the current scope using the refresh
* token JWT encoded string in the token response object and the provided client id.
*
* Client secret will be not sent.
* <br><b>NOTE</b>: For <code>public</code> clients types only.
*
* @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 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 clientId, TokenResponse tokenResponse) throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server discovered in the current scope 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)
TokenResponse refreshToken(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
@ -661,24 +149,50 @@ public interface KeycloakClient {
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.
* Refreshes a previously issued token from the Keycloak server discovered in the current scope using the 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
* Client id will be read from "issued for" refresh token's claim and client secret will be not sent.
* <br><b>NOTE</b>: For <code>public</code> clients types only.
*
* @param refreshTokenJWTString the previously issued refresh token JWT string taken from the same token response of the access token parameter
* @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)
TokenResponse refreshToken(String refreshTokenJWTString) throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server discovered in the current scope using the provided
* client id and the refresh token JWT encoded string obtained with the access token in the previous token response.
*
* Client secret will be not used.
* <br><b>NOTE</b>: For <code>public</code> clients types only.
*
* @param clientId the requestor client id
* @param refreshTokenJWTString the previously issued refresh token JWT string taken from the same token response of the access token parameter
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(String clientId, String refreshTokenJWTString) throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server discovered in the current scope using the provided
* client id and secret and the refresh token JWT encoded string obtained with the access token in the previous
* token response.
*
* @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 taken from the same token response of the access token parameter
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(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.
*
* @param tokenURL the token endpoint {@link URL} of the OIDC server
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param clientId the requestor client id
* @param clientSecret the requestor client secret, may be <code>null</code> for non-confidential clients
* @param refreshTokenJWTString the previously issued refresh token JWT string
@ -688,171 +202,4 @@ public interface KeycloakClient {
TokenResponse refreshToken(URL tokenURL, String clientId, String clientSecret, String refreshTokenJWTString)
throws KeycloakClientException;
/**
* Exchanges a token for another access token for a specific client and a specific audience
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param oidcAccessToken the original access token to exchange
* @param clientId the authorized client's id
* @param clientSecret the authorized client's secret
* @param audience the requested token audience
* @return the exchanged token response
* @throws KeycloakClientException if an error occurs during the exchange
*/
TokenResponse exchangeTokenForAccessToken(String context, String oidcAccessToken, String clientId,
String clientSecret, String audience) throws KeycloakClientException;
/**
* Exchanges a token for another access token for a specific client and a specific audience
*
* @param tokenURL the token endpoint URL
* @param oidcAccessToken the original access token to exchange
* @param clientId the authorized client's id
* @param clientSecret the authorized client's secret
* @param audience the requested token audience
* @return the exchanged token response
* @throws KeycloakClientException if an error occurs during the exchange
*/
TokenResponse exchangeTokenForAccessToken(URL tokenURL, String oidcAccessToken, String clientId,
String clientSecret, String audience) throws KeycloakClientException;
/**
* Exchanges a token for another access and a refresh tokens for a specific client and a specific audience
*
* @param context the context where the Keycloak's is needed (e.g. <code>/gcube</code> for DEV)
* @param oidcAccessToken the original access token to exchange
* @param clientId the authorized client's id
* @param clientSecret the authorized client's secret
* @param audience the requested token audience
* @return the exchanged token response
* @throws KeycloakClientException if an error occurs during the exchange
*/
TokenResponse exchangeTokenForRefreshToken(String context, String oidcAccessToken, String clientId,
String clientSecret, String audience) throws KeycloakClientException;
/**
* Exchanges a token for another access and a refresh tokens for a specific client and a specific audience
*
* @param tokenURL the token endpoint URL
* @param oidcAccessToken the original access token to exchange
* @param clientId the authorized client's id
* @param clientSecret the authorized client's secret
* @param audience the requested token audience
* @return the exchanged token response
* @throws KeycloakClientException if an error occurs during the exchange
*/
TokenResponse exchangeTokenForRefreshToken(URL tokenURL, String oidcAccessToken, String clientId,
String clientSecret, String audience) throws KeycloakClientException;
/**
* Exchanges a token for another access and an offline refresh tokens for a specific client and a specific audience
* The refresh token will be of the offline type only if the original token has the <code>offline_access</code> within its scopes
*
* @param tokenURL the token endpoint URL
* @param oidcAccessToken the original access token to exchange
* @param clientId the authorized client's id
* @param clientSecret the authorized client's secret
* @param audience the requested token audience
* @return the exchanged token response
* @throws IllegalArgumentException if the original token does'nt contains the <code>offline_access</code> scope within its scopes or if is impossible to parse the access token as JSON
* @throws KeycloakClientException if an error occurs during the exchange
*/
TokenResponse exchangeTokenForOfflineToken(String context, String oidcAccessToken, String clientId,
String clientSecret, String audience) throws IllegalArgumentException, KeycloakClientException;
/**
* Exchanges a token for another access and an offline refresh tokens for a specific client and a specific audience
* The refresh token will be of the offline type only if the original token has the scope <code>offline_access</code> within its scopes
*
* @param tokenURL the token endpoint URL
* @param oidcAccessToken the original access token to exchange
* @param clientId the authorized client's id
* @param clientSecret the authorized client's secret
* @param audience the requested token audience
* @return the exchanged token response
* @throws IllegalArgumentException if the original token does'nt contains the <code>offline_access</code> scope within its scopes or if is impossible to parse the access token as JSON
* @throws KeycloakClientException if an error occurs during the exchange
*/
TokenResponse exchangeTokenForOfflineToken(URL tokenURL, String oidcAccessToken, String clientId,
String clientSecret, String audience) throws IllegalArgumentException, KeycloakClientException;
/**
* Introspects an access token against the Keycloak server.
*
* @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.
*
* @param introspectionURL the introspection endpoint {@link URL} of the Keycloak server
* @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(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.
*
* @param introspectionURL the introspection endpoint {@link URL} of the Keycloak server
* @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(URL introspectionURL, String clientId, String clientSecret,
String accessTokenJWTString) throws KeycloakClientException;
/**
* Retrieves the user's avatar image data from Keycloak server.
* @param context the context used to compute the server endpoint in the correct environment
* @param tokenResponse the token response where to get the bearer token for the authorization header.
* @return the avatar's data bytes
* @throws KeycloakClientException if something goes wrong in the request
*/
byte[] getAvatarData(String context, TokenResponse tokenResponse) throws KeycloakClientException;
/**
* Retrieves the user's avatar image data from Keycloak server.
* @param avatarURL the server's avatar endpoint URL
* @param tokenResponse the token response where to get the bearer token for the authorization header.
* @return the avatar's data bytes
* @throws KeycloakClientException if something goes wrong in the request
*/
byte[] getAvatarData(URL avatarURL, TokenResponse tokenResponse) throws KeycloakClientException;
/**
* Retrieves the user's avatar image data from Keycloak server.
* @param avatarURL the server's avatar endpoint URL
* @param the string to user as authorization header (e.g. 'bearer xxxx')
* @return the avatar's data bytes
* @throws KeycloakClientException if something goes wrong in the request
*/
byte[] getAvatarData(URL avatarURL, String authorization) throws KeycloakClientException;
}

View File

@ -56,7 +56,7 @@ public class KeycloakClientException extends Exception {
}
public boolean hasJSONPayload() {
return getContentType() != null && getContentType().endsWith("json");
return getContentType().endsWith("json");
}
public void setResponseString(String responseString) {

View File

@ -7,32 +7,9 @@ public class KeycloakClientFactory {
protected static final Logger logger = LoggerFactory.getLogger(KeycloakClientFactory.class);
protected static String CUSTOM_BASE_URL;
public static void setCustomBaseURL(String customBaseURL) {
if (customBaseURL != null) {
logger.info("Setting custom base URL static value to {}", customBaseURL);
} else {
logger.info("Removing custom base URL static value");
}
if (customBaseURL == null || customBaseURL.endsWith("/")) {
CUSTOM_BASE_URL = customBaseURL;
} else {
CUSTOM_BASE_URL = customBaseURL += "/";
}
}
public static String getCustomBaseURL() {
return CUSTOM_BASE_URL;
}
public static KeycloakClient newInstance() {
logger.debug("Instantiating a new keycloak client instance");
DefaultKeycloakClient newInstance = new DefaultKeycloakClient();
if (getCustomBaseURL() != null) {
newInstance.setCustomBaseURL(CUSTOM_BASE_URL);
}
return newInstance;
return new DefaultKeycloakClient();
}
}

View File

@ -1,48 +0,0 @@
package org.gcube.common.keycloak;
import org.gcube.common.keycloak.model.AccessToken;
import org.gcube.common.keycloak.model.RefreshToken;
import org.gcube.common.keycloak.model.TokenResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class KeycloakClientHelper {
protected static Logger logger = LoggerFactory.getLogger(KeycloakClientHelper.class);
/**
* The public GW used to obtain the token, is not declared final since it can be changed if necessary at runtime
*/
private static String GATEWAY_CLIENT_ID = "d4science-internal-gateway";
/**
* Sets the new default GW <code>clientId</code> used for all the queries to the Keycloak server.
* Note: The operation will logged as WARN to be visible.
* @param gatewayClientId the new GW <code>clientId</code>
*/
public static void setDefaultGWClientID(String gatewayClientId) {
logger.warn("The default GW clientId will be changed to: {}", gatewayClientId);
GATEWAY_CLIENT_ID = gatewayClientId;
}
/**
* Gets a new {@link TokenResponse}, containing the {@link AccessToken} and the {@link RefreshToken} from the Keycloak server in the environment of the context represented by the <code>context</code> parameter.
* The <code>context</code> parameter is also used as audience for the token.
*
* @param context the context ot be used for discovery and for audience
* @param username the user's username
* @param password the user's password
* @return the token response from the Keycloak server
* @throws KeycloakClientException if an error occurs during the process
*/
public static TokenResponse getTokenForUser(String context, String username, String password) throws KeycloakClientException {
logger.debug("Getting new token for user '{}' in context '{}'", username, context);
logger.trace("Getting OIDC token for user '{}' using configured default GW clientId '{}'", username, GATEWAY_CLIENT_ID);
TokenResponse oidcTR = KeycloakClientFactory.newInstance().queryOIDCTokenOfUser(context, GATEWAY_CLIENT_ID,
"", username, password);
logger.trace("Getting UMA token in the '{}' context", context);
return KeycloakClientFactory.newInstance().queryUMAToken(context, oidcTR, context, null);
}
}

View File

@ -1,19 +1,3 @@
/*
* 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;
@ -26,9 +10,6 @@ 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;
@ -87,11 +68,6 @@ public class AccessToken extends IDToken {
this.verifyCaller = required;
return this;
}
@Override
public String toString() {
return getRoles() != null ? getRoles().toString() : null;
}
}
@JsonProperty("trusted-certs")
@ -175,12 +151,4 @@ public class AccessToken extends IDToken {
this.trustedCertificates = trustedCertificates;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}

View File

@ -1,26 +1,7 @@
/*
* 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";

View File

@ -1,26 +1,7 @@
/*
* 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;

View File

@ -1,38 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gcube.common.keycloak.model;
import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JSONWebKeySet {
@JsonProperty("keys")
private JWK[] keys;
public JWK[] getKeys() {
return keys;
}
public void setKeys(JWK[] keys) {
this.keys = keys;
}
}

View File

@ -1,112 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gcube.common.keycloak.model;
import java.util.HashMap;
import java.util.Map;
import org.gcube.com.fasterxml.jackson.annotation.JsonAnyGetter;
import org.gcube.com.fasterxml.jackson.annotation.JsonAnySetter;
import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JWK {
public static final String KEY_ID = "kid";
public static final String KEY_TYPE = "kty";
public static final String ALGORITHM = "alg";
public static final String PUBLIC_KEY_USE = "use";
public enum Use {
SIG("sig"),
ENCRYPTION("enc");
private String str;
Use(String str) {
this.str = str;
}
public String asString() {
return str;
}
}
@JsonProperty(KEY_ID)
private String keyId;
@JsonProperty(KEY_TYPE)
private String keyType;
@JsonProperty(ALGORITHM)
private String algorithm;
@JsonProperty(PUBLIC_KEY_USE)
private String publicKeyUse;
protected Map<String, Object> otherClaims = new HashMap<String, Object>();
public String getKeyId() {
return keyId;
}
public void setKeyId(String keyId) {
this.keyId = keyId;
}
public String getKeyType() {
return keyType;
}
public void setKeyType(String keyType) {
this.keyType = keyType;
}
public String getAlgorithm() {
return algorithm;
}
public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}
public String getPublicKeyUse() {
return publicKeyUse;
}
public void setPublicKeyUse(String publicKeyUse) {
this.publicKeyUse = publicKeyUse;
}
@JsonAnyGetter
public Map<String, Object> getOtherClaims() {
return otherClaims;
}
@JsonAnySetter
public void setOtherClaims(String name, Object value) {
otherClaims.put(name, value);
}
}

View File

@ -15,9 +15,6 @@ 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;
@ -83,7 +80,7 @@ public class JsonWebToken implements Serializable {
/**
* Tests that the token is not expired and is not-before.
*
* @return <code>true</code> if is not expired and is not-before
* @return
*/
@JsonIgnore
public boolean isActive() {
@ -101,7 +98,6 @@ public class JsonWebToken implements Serializable {
/**
* Set issuedAt to the current time
* @return the token itself
*/
@JsonIgnore
public JsonWebToken issuedNow() {
@ -187,7 +183,7 @@ public class JsonWebToken implements Serializable {
/**
* OAuth client the token was issued for.
*
* @return the issued for vale
* @return
*/
public String getIssuedFor() {
return issuedFor;
@ -201,7 +197,7 @@ public class JsonWebToken implements Serializable {
/**
* This is a map of any other claims and data that might be in the IDToken. Could be custom claims set up by the auth server
*
* @return the object's other claims
* @return
*/
@JsonAnyGetter
public Map<String, Object> getOtherClaims() {

View File

@ -1,36 +1,16 @@
package org.gcube.common.keycloak.model;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.gcube.com.fasterxml.jackson.annotation.JsonInclude.Include;
import org.gcube.com.fasterxml.jackson.core.JsonProcessingException;
import org.gcube.com.fasterxml.jackson.core.type.TypeReference;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.ObjectWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.DeserializationException;
import io.jsonwebtoken.io.Deserializer;
/**
* @author <a href="mailto:mauro.mugnaini@nubisware.com">Mauro Mugnaini</a>
*/
public class ModelUtils {
protected static final Logger logger = LoggerFactory.getLogger(ModelUtils.class);
@ -57,87 +37,12 @@ public class ModelUtils {
}
}
/**
* Creates a {@link RSAPublicKey} instance from its string PEM representation
*
* @param publicKeyPem the public key PEM string
* @return the RSA public key
* @throws Exception if it's not possible to create the RSA public key from the PEM string
*/
public static RSAPublicKey createRSAPublicKey(String publicKeyPem) throws Exception {
return (RSAPublicKey) createPublicKey(publicKeyPem, "RSA");
}
/**
* Creates a {@link PublicKey} instance from its string PEM representation
*
* @param publicKeyPem the public key PEM string
* @param algorithm the key type (e.g. RSA)
* @return the public key
* @throws Exception if it's not possible to create the public key from the PEM string
*/
public static PublicKey createPublicKey(String publicKeyPem, String algorithm) throws Exception {
try {
String publicKey = publicKeyPem.replaceFirst("-----BEGIN (.*)-----\n", "");
publicKey = publicKey.replaceFirst("-----END (.*)-----", "");
publicKey = publicKey.replaceAll("\r\n", "");
publicKey = publicKey.replaceAll("\n", "");
byte[] encoded = Base64.getDecoder().decode(publicKey);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePublic(new X509EncodedKeySpec(encoded));
} catch (Exception e) {
throw new RuntimeException("Cannot create public key from PEM string", e);
}
}
/**
* Verifies the token validity
*
* @param token the base64 JWT token string
* @param publicKey the realm's public key on server
* @return <code>true</code> if the token is valid, <code>false</code> otherwise
* @throws Exception if an error occurs constructing the verifier
*/
public static boolean isValid(String token, PublicKey publicKey) throws Exception {
return isValid(token, publicKey, true);
}
/**
* Verifies the token validity
*
* @param token the base64 JWT token string
* @param publicKey the public key to use for verification
* @param checkExpiration if <code>false</code> token expiration check is disabled
* @return <code>true</code> if the token is valid, <code>false</code> otherwise
* @throws Exception if an error occurs constructing the verifier
*/
public static boolean isValid(String token, PublicKey publicKey, boolean checkExpiration) throws Exception {
JwtParser jwtParser = Jwts.parser().json(new GcubeJacksonDeserializer()).verifyWith(publicKey).build();
try {
jwtParser.parse(token);
return true;
} catch (ExpiredJwtException e) {
// This is OK because expiration check is after the signature validation in the implementation
if (logger.isDebugEnabled()) {
logger.debug("JWT is expired: {}", e.getMessage());
}
return !checkExpiration;
} catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("JWT is not verified: {}", e.getMessage());
}
return false;
}
}
public static String getAccessTokenPayloadJSONStringFrom(TokenResponse tokenResponse) throws Exception {
return getAccessTokenPayloadJSONStringFrom(tokenResponse, true);
}
public static String getAccessTokenPayloadJSONStringFrom(TokenResponse tokenResponse, boolean prettyPrint)
throws Exception {
return toJSONString(getAccessTokenFrom(tokenResponse, Object.class), prettyPrint);
}
@ -240,23 +145,4 @@ public class ModelUtils {
return "";
}
}
public static class GcubeJacksonDeserializer implements Deserializer<Map<String,?>> {
@Override
public Map<String, ?> deserialize(byte[] bytes) throws DeserializationException {
return deserialize(new InputStreamReader(new ByteArrayInputStream(bytes)));
}
@Override
public Map<String, ?> deserialize(Reader reader) throws DeserializationException {
try {
return new ObjectMapper().readValue(reader, new TypeReference<HashMap<String,Object>>() {});
} catch (IOException e) {
throw new DeserializationException("Cannot deserialize JSON with GCube Jackson", e);
}
}
}
}

View File

@ -1,33 +1,14 @@
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";
public static final String GRANT_TYPE_PARAMETER = "grant_type";
public static final String SCOPE_PARAMETER = "scope";
public static final String CLIENT_CREDENTIALS_GRANT_TYPE = "client_credentials";
public static final String UMA_TOKEN_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:uma-ticket";
public static final String TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
public static final String AUDIENCE_PARAMETER = "audience";
public static final String REFRESH_TOKEN_GRANT_TYPE = "refresh_token";
public static final String REFRESH_TOKEN_PARAMETER = "refresh_token";
public static final String CLIENT_ID_PARAMETER = "client_id";
public static final String CLIENT_SECRET_PARAMETER = "client_secret";
public static final String TOKEN_PARAMETER = "token";
public static final String PASSWORD_GRANT_TYPE = "password";
public static final String USERNAME_PARAMETER = "username";
public static final String PASSWORD_PARAMETER = "password";
public static final String SUBJECT_TOKEN_PARAMETER = "subject_token";
public static final String SUBJECT_TOKEN_TYPE_PARAMETER = "subject_token_type";
public static final String REQUESTED_TOKEN_TYPE_PARAMETER = "requested_token_type";
public static final String ACCESS_TOKEN_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
public static final String REFRESH_TOKEN_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:refresh_token";
public static final String OFFLINE_ACCESS_SCOPE = "offline_access";
public static final String ADDRESS_SCOPE = "address";
public static final String EMAIL_SCOPE = "email";
public static final String PROFILE_SCOPE = "email";
public static final String ROLES_SCOPE = "roles";
}

View File

@ -1,107 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gcube.common.keycloak.model;
import java.security.interfaces.RSAPublicKey;
import org.gcube.com.fasterxml.jackson.annotation.JsonIgnore;
import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @author (modified by) <a href="mailto:mauro.mugnaini@nubisware.com">Mauro Mugnaini</a>
*/
public class PublishedRealmRepresentation {
protected String realm;
@JsonProperty("public_key")
protected String publicKeyPem;
@JsonProperty("token-service")
protected String tokenServiceUrl;
@JsonProperty("account-service")
protected String accountServiceUrl;
@JsonProperty("tokens-not-before")
protected int notBefore;
@JsonIgnore
protected volatile transient RSAPublicKey publicKey;
public String getRealm() {
return realm;
}
public void setRealm(String realm) {
this.realm = realm;
}
public String getPublicKeyPem() {
return publicKeyPem;
}
public void setPublicKeyPem(String publicKeyPem) {
this.publicKeyPem = publicKeyPem;
this.publicKey = null;
}
@JsonIgnore
public RSAPublicKey getPublicKey() {
if (publicKey != null)
return publicKey;
if (publicKeyPem != null) {
try {
publicKey = ModelUtils.createRSAPublicKey(publicKeyPem);
} catch (Exception e) {
e.printStackTrace();
}
}
return publicKey;
}
@JsonIgnore
public void setPublicKey(RSAPublicKey publicKey) {
this.publicKey = publicKey;
// this.publicKeyPem = PemUtils.encodeKey(publicKey);
}
public String getTokenServiceUrl() {
return tokenServiceUrl;
}
public void setTokenServiceUrl(String tokenServiceUrl) {
this.tokenServiceUrl = tokenServiceUrl;
}
public String getAccountServiceUrl() {
return accountServiceUrl;
}
public void setAccountServiceUrl(String accountServiceUrl) {
this.accountServiceUrl = accountServiceUrl;
}
public int getNotBefore() {
return notBefore;
}
public void setNotBefore(int notBefore) {
this.notBefore = notBefore;
}
}

View File

@ -1,24 +1,5 @@
/*
* 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;

View File

@ -1,43 +0,0 @@
/*
* 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;
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;
@JsonProperty
private Boolean active;
private List<Permission> permissions;
public Boolean getActive() {
return this.active;
}
public List<Permission> getPermissions() {
return this.permissions;
}
}

View File

@ -10,9 +10,6 @@ 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);
@ -63,7 +60,7 @@ public class TokenResponse implements Serializable {
return accessToken;
}
public void setAccessToken(String accessToken) {
public void setSccessToken(String accessToken) {
this.accessToken = accessToken;
}

View File

@ -1,19 +1,3 @@
/*
* 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;
@ -311,7 +295,7 @@ public class UserInfo {
/**
* This is a map of any other claims and data that might be in the UserInfo. Could be custom claims set up by the auth server
*
* @return the object's other claims
* @return
*/
@JsonAnyGetter
public Map<String, Object> getOtherClaims() {

View File

@ -1,138 +0,0 @@
/*
* 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;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
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 {
@JsonProperty("rsid")
private String resourceId;
@JsonProperty("rsname")
private String resourceName;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Set<String> scopes;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, Set<String>> claims;
public Permission() {
this(null, null, null, null);
}
public Permission(final String resourceId, final Set<String> scopes) {
this(resourceId, null, scopes, null);
}
public Permission(final String resourceId, String resourceName, final Set<String> scopes, Map<String, Set<String>> claims) {
this.resourceId = resourceId;
this.resourceName = resourceName;
this.scopes = scopes;
this.claims = claims;
}
public void setResourceId(String resourceId) {
this.resourceId = resourceId;
}
public String getResourceId() {
if (resourceId == null || "".equals(resourceId.trim())) {
return null;
}
return this.resourceId;
}
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
public String getResourceName() {
return this.resourceName;
}
public Set<String> getScopes() {
if (this.scopes == null) {
this.scopes = new HashSet<>();
}
return this.scopes;
}
public Map<String, Set<String>> getClaims() {
return claims;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !getClass().isAssignableFrom(o.getClass())) return false;
Permission that = (Permission) o;
if (getResourceId() != null || getResourceName() != null) {
if (!getResourceId().equals(that.resourceId)) {
return false;
}
if (getScopes().isEmpty() && that.getScopes().isEmpty()) {
return true;
}
} else if (that.resourceId != null) {
return false;
}
for (String scope : that.getScopes()) {
if (getScopes().contains(scope)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(resourceId);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Permission {").append("id=").append(resourceId).append(", name=").append(resourceName)
.append(", scopes=").append(scopes).append("}");
return builder.toString();
}
public void setScopes(Set<String> scopes) {
this.scopes = scopes;
}
}

View File

@ -1,19 +1,3 @@
/*
* 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;

View File

@ -1,19 +1,3 @@
/*
* 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;

View File

@ -1,19 +1,3 @@
/*
* 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;

View File

@ -1,32 +1,13 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gcube.common.keycloak.model.util;
import java.util.Date;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Time {
private static int offset;
/**
* Returns current time in seconds adjusted by adding {@link #offset} seconds.
* Returns current time in seconds adjusted by adding {@link #offset) seconds.
* @return see description
*/
public static int currentTime() {
@ -34,7 +15,7 @@ public class Time {
}
/**
* Returns current time in milliseconds adjusted by adding {@link #offset} seconds.
* Returns current time in milliseconds adjusted by adding {@link #offset) seconds.
* @return see description
*/
public static long currentTimeMillis() {

View File

@ -1,16 +1,10 @@
package org.gcube.common.keycloak;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.net.URL;
import java.util.Collections;
import org.gcube.common.keycloak.model.JSONWebKeySet;
import org.gcube.common.keycloak.model.ModelUtils;
import org.gcube.common.keycloak.model.PublishedRealmRepresentation;
import org.gcube.common.keycloak.model.TokenIntrospectionResponse;
import org.gcube.common.keycloak.model.TokenResponse;
import org.gcube.common.scope.api.ScopeProvider;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -25,639 +19,70 @@ public class TestKeycloakClient {
protected static final Logger logger = LoggerFactory.getLogger(TestKeycloakClient.class);
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";
protected static final String DEV_AVATAR_ENDPOINT = DEV_BASE_URL + "/account-avatar";
private static final String DEV_ENDPOINT = "http://accounts.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token";
private static final String CLIENT_ID = "keycloak-client";
private static final String CLIENT_SECRET = "38f76152-2b7c-418f-9b67-66f4cc2f401e";
private static final String TEST_AUDIENCE = "conductor-server";
protected static final String ACCOUNTS_DEV_INTROSPECTION_ENDPOINT = "https://accounts.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token/introspect";
protected static final String GATEWAY = "next.dev.d4science.org";
protected static final String TOKEN_RESTRICTION_VRE_CONTEXT = "%2Fgcube%2Fdevsec%2FCCP";
protected static final String CLIENT_ID = "keycloak-client-unit-test";
protected static final String CLIENT_SECRET = "ebf6f82e-9511-408e-8321-203081e472d8";
protected static final String TEST_AUDIENCE = "conductor-server";
protected static final String TEST_USER_USERNAME = "testuser";
protected static final String TEST_USER_PASSWORD = "t35tp455";
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";
private static TokenResponse tr = null;
@Before
public void setUp() throws Exception {
ScopeProvider.instance.set("/gcube");
}
@After
public void tearDown() throws Exception {
// Assure to reset the factory's default base URL
KeycloakClientFactory.setCustomBaseURL(null);
}
@Test
public void test00TokenEndpointConstruction() throws Exception {
logger.info(
"*** [0.0] Start testing Keycloak token endpoint construction from base URL computed from context...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
logger.info("*** [0.0] Constructed token URL is: {}", tokenURL);
URL devTokenURL = new URL(DEV_TOKEN_ENDPOINT);
logger.info("*** [0.0] DEV token URL is: {}", devTokenURL);
Assert.assertNotNull(tokenURL);
Assert.assertEquals(tokenURL.getProtocol(), "https");
Assert.assertEquals(devTokenURL.toString(), tokenURL.toString());
public void test1EndpointDiscovery() throws Exception {
logger.info("Start testing Keycloak endpoint discovery...");
URL url = KeycloakClientFactory.newInstance().findTokenEndpointURL();
Assert.assertNotNull(url);
Assert.assertTrue(url.getProtocol().equals("https"));
logger.info("Discovered URL is: {}", url);
}
@Test
public void test01IntrospectionEndpointConstruction() throws Exception {
logger.info("*** [0.1] Start testing Keycloak introspection endpoint construction from base URL...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
logger.info("*** [0.1] Constructed introspection URL is: {}", introspectionURL);
URL devIntrospectionURL = new URL(DEV_INTROSPECTION_ENDPOINT);
logger.info("*** [0.1] DEV introspection URL is: {}", devIntrospectionURL);
Assert.assertNotNull(introspectionURL);
Assert.assertEquals(introspectionURL.getProtocol(), "https");
Assert.assertEquals(devIntrospectionURL.toString(), introspectionURL.toString());
logger.info("Constructed URL is: {}", introspectionURL);
public void test2QueryUMATokenWithDiscoveryInCurrentScope() throws Exception {
logger.info("Start testing query UMA token from Keycloak with endpoint discovery and current scope...");
tr = KeycloakClientFactory.newInstance().queryUMAToken(CLIENT_ID, CLIENT_SECRET, null);
TestModels.checkTokenResponse(tr);
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(tr), "service-account-" + CLIENT_ID);
}
@Test
public void test02IntrospectEndpointCompute() throws Exception {
logger.info("*** [0.2] Start testing Keycloak userinfo endpoint computed from provided URL string...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
logger.info("*** [0.2] Constructed introspection URL is: {}", introspectionURL);
URL computedIntrospectionURL = client
.computeIntrospectionEndpointURL(client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT)));
logger.info("*** [0.2] Computed introspection URL is: {}", computedIntrospectionURL);
URL devIntrospectionURL = new URL(DEV_INTROSPECTION_ENDPOINT);
logger.info("*** [0.2] DEV introspection URL is: {}", devIntrospectionURL);
Assert.assertNotNull(computedIntrospectionURL);
Assert.assertEquals(computedIntrospectionURL.getProtocol(), "https");
Assert.assertEquals(introspectionURL.toString(), computedIntrospectionURL.toString());
Assert.assertEquals(devIntrospectionURL.toString(), computedIntrospectionURL.toString());
public void test3QueryUMATokenWithDiscovery() throws Exception {
logger.info("Start testing query UMA token from Keycloak with endpoint discovery...");
tr = KeycloakClientFactory.newInstance().queryUMAToken(CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
TestModels.checkTokenResponse(tr);
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(tr), "service-account-" + CLIENT_ID);
}
@Test
public void test03AvatarEndpointConstruction() throws Exception {
logger.info("*** [0.3] Start testing Keycloak avatar endpoint construction from base URL...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL avatarURL = client.getAvatarEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
logger.info("*** [0.3] Constructed avatar URL is: {}", avatarURL);
URL devAvatarURL = new URL(DEV_AVATAR_ENDPOINT);
logger.info("*** [0.3] DEV avatar URL is: {}", devAvatarURL);
Assert.assertNotNull(avatarURL);
Assert.assertEquals(avatarURL.getProtocol(), "https");
Assert.assertEquals(devAvatarURL.toString(), avatarURL.toString());
public void test4QueryUMAToken() throws Exception {
logger.info("Start testing query UMA token from Keycloak with URL...");
tr = KeycloakClientFactory.newInstance().queryUMAToken(new URL(DEV_ENDPOINT), CLIENT_ID, CLIENT_SECRET,
TEST_AUDIENCE, null);
TestModels.checkTokenResponse(tr);
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(tr), "service-account-" + CLIENT_ID);
}
@Test
public void test04CustomEndpointTest() throws Exception {
String customBase = "https://accounts.cloud.dev.d4science.org/auth/realms/";
logger.info("*** [0.4] Start testing Keycloak token endpoint construction from custom base URL {}...",
customBase);
KeycloakClientFactory.setCustomBaseURL(customBase);
KeycloakClient client = KeycloakClientFactory.newInstance();
URL customBaseURL = client.getRealmBaseURL(DEV_ROOT_CONTEXT);
logger.info("*** [0.4] Constructed token URL is: {}", customBaseURL);
URL devTokenURL = new URL(DEV_TOKEN_ENDPOINT);
logger.info("*** [0.4] DEV token URL is: {}", devTokenURL);
Assert.assertNotNull(customBaseURL);
Assert.assertEquals(customBaseURL.getProtocol(), "https");
Assert.assertEquals(customBase + KeycloakClient.DEFAULT_REALM + "/", customBaseURL.toString());
public void test5RefreshTokenWithDiscovery() throws Exception {
logger.info("Start testing refresh UMA token from Keycloak with endpoint discovery...");
TokenResponse refreshedTR = KeycloakClientFactory.newInstance().refreshToken(CLIENT_ID, CLIENT_SECRET, tr);
TestModels.checkTokenResponse(refreshedTR);
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(refreshedTR), "service-account-" + CLIENT_ID);
TestModels.checkRefreshToken(ModelUtils.getRefreshTokenFrom(refreshedTR));
}
@Test
public void test10aQueryRealmInfo() throws Exception {
logger.info("*** [1.0a] Start testing query realm info...");
KeycloakClient client = KeycloakClientFactory.newInstance();
PublishedRealmRepresentation realmInfo = client.getRealmInfo(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
logger.info("*** [1.0a] Realm info public key PEM: {}", realmInfo.getPublicKeyPem());
logger.info("*** [1.0a] Realm info public key: {}", realmInfo.getPublicKey());
@Test(expected = KeycloakClientException.class)
public void test6RefreshTokenWithDiscoveryAndClientIdFromRefreshToken() throws Exception {
logger.info("Start testing refresh UMA token with error since is not a public client...");
KeycloakClientFactory.newInstance().refreshToken(tr.getRefreshToken());
}
@Test
public void test10bQueryRealmJWK() throws Exception {
logger.info("*** [1.0b] Start testing query realm JWK...");
KeycloakClient client = KeycloakClientFactory.newInstance();
JSONWebKeySet jsonWebKeySet = client
.getRealmJSONWebKeySet(client.getJWKEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT)));
for (int i = 0; i < jsonWebKeySet.getKeys().length; i++) {
logger.info("*** [1.0b] Realm JWK public key {} algorithm: {}", i,
jsonWebKeySet.getKeys()[i].getAlgorithm());
}
}
@Test
public void test11TestAccessTokenJWTSignature() throws Exception {
logger.info("*** [1.0] Start testing access token JTW signature with model utils...");
KeycloakClient client = KeycloakClientFactory.newInstance();
PublishedRealmRepresentation realmInfo = client.getRealmInfo(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
logger.info("*** [1.0] Realm info public key PEM: {}", realmInfo.getPublicKeyPem());
logger.info("*** [1.0] Realm info public key: {}", realmInfo.getPublicKey());
TokenResponse oidcTR = client.queryOIDCToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET);
logger.info("*** [1.0] OIDC access token: {}", oidcTR.getAccessToken());
Assert.assertTrue("Access token is not valid",
ModelUtils.isValid(oidcTR.getAccessToken(), realmInfo.getPublicKey()));
}
@Test
public void test12QueryOIDCToken() throws Exception {
logger.info("*** [1.2] Start testing query OIDC token from Keycloak with context...");
TokenResponse oidcTR = KeycloakClientFactory.newInstance().queryOIDCToken(DEV_ROOT_CONTEXT, CLIENT_ID,
CLIENT_SECRET);
logger.info("*** [1.2] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.2] OIDC refresh token: {}", oidcTR.getRefreshToken());
TestModelUtils.checkTokenResponse(oidcTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), "service-account-" + CLIENT_ID, false);
}
@Test
public void test12aQueryOIDCToken() throws Exception {
logger.info("*** [1.2a] Start testing query OIDC token from Keycloak with URL...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
TokenResponse oidcTR = client.queryOIDCToken(tokenURL, CLIENT_ID, CLIENT_SECRET);
logger.info("*** [1.2a] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.2a] OIDC refresh token: {}", oidcTR.getRefreshToken());
TestModelUtils.checkTokenResponse(oidcTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), "service-account-" + CLIENT_ID, false);
}
@Test
public void test13QueryOIDCTokenOfUser() throws Exception {
logger.info("*** [1.3] Start testing query OIDC token from Keycloak with context for user...");
TokenResponse oidcTR = KeycloakClientFactory.newInstance().queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID,
CLIENT_SECRET, TEST_USER_USERNAME, TEST_USER_PASSWORD);
logger.info("*** [1.3] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.3] OIDC refresh token: {}", oidcTR.getRefreshToken());
TestModelUtils.checkTokenResponse(oidcTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, false);
}
@Test
public void test13aQueryOIDCTokenOfUserWithContext() throws Exception {
logger.info("*** [1.3a] Start testing query OIDC token for audience from Keycloak with context for user...");
TokenResponse oidcTR = KeycloakClientFactory.newInstance().queryOIDCTokenOfUser(DEV_ROOT_CONTEXT,
CLIENT_ID, CLIENT_SECRET, TEST_USER_USERNAME, TEST_USER_PASSWORD);
logger.info("*** [1.3a] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.3a] OIDC refresh token: {}", oidcTR.getRefreshToken());
TestModelUtils.checkTokenResponse(oidcTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, true);
TokenResponse oidcRestrictedTR = KeycloakClientFactory.newInstance().queryOIDCTokenOfUserWithContext(
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TEST_USER_USERNAME, TEST_USER_PASSWORD,
TOKEN_RESTRICTION_VRE_CONTEXT);
logger.info("*** [1.3a] OIDC restricted access token: {}", oidcRestrictedTR.getAccessToken());
logger.info("*** [1.3a] OIDC restricted refresh token: {}", oidcRestrictedTR.getRefreshToken());
TestModelUtils.checkTokenResponse(oidcTR);
TestModelUtils.checkTokenResponse(oidcRestrictedTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, true);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcRestrictedTR), TEST_USER_USERNAME, true);
assertTrue(ModelUtils.getAccessTokenFrom(oidcTR).getAudience().length > 1);
assertTrue(ModelUtils.getAccessTokenFrom(oidcRestrictedTR).getAudience().length == 1);
assertTrue(
TOKEN_RESTRICTION_VRE_CONTEXT.equals(ModelUtils.getAccessTokenFrom(oidcRestrictedTR).getAudience()[0]));
}
@Test
public void test13bQueryOIDCTokenAndUMAOfUser() throws Exception {
logger.info("*** [1.3b] Start testing query OIDC and UMA tokens from Keycloak with context for user...");
KeycloakClient client = KeycloakClientFactory.newInstance();
TokenResponse oidcTR = client.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
TEST_USER_USERNAME, TEST_USER_PASSWORD);
logger.info("*** [1.3b] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.3b] OIDC refresh token: {}", oidcTR.getRefreshToken());
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());
TestModelUtils.checkTokenResponse(umaTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(umaTR), TEST_USER_USERNAME, true);
}
@Test
public void test13cQueryOIDCTokenOfUserWithContextAndCustomHeader() throws Exception {
logger.info(
"*** [1.3c] Start testing query OIDC token for audience from Keycloak with context and custom headers for user...");
TokenResponse oidcTR = KeycloakClientFactory.newInstance().queryOIDCTokenOfUserWithContext(DEV_ROOT_CONTEXT,
CLIENT_ID, CLIENT_SECRET, TEST_USER_USERNAME, TEST_USER_PASSWORD, TOKEN_RESTRICTION_VRE_CONTEXT,
Collections.singletonMap("X_A_CUSTOM_HEADER", "HEADER_VALUE"));
logger.info("*** [1.3c] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.3c] OIDC refresh token: {}", oidcTR.getRefreshToken());
TestModelUtils.checkTokenResponse(oidcTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, true);
assertTrue(ModelUtils.getAccessTokenFrom(oidcTR).getAudience().length == 1);
// It is not possible to check programmatically if the header has been added to the call, See the logs if the Header is present.
}
@Test
public void test14aQueryOIDCTokenWithDynamicScope() throws Exception {
logger.info(
"*** [1.4a] Start testing query OIDC token with dynamic scope...");
TokenResponse oidcTR = KeycloakClientFactory.newInstance()
.addDynamicScope(KeycloakClient.D4S_DYNAMIC_SCOPE_NAME, TOKEN_RESTRICTION_VRE_CONTEXT)
.queryOIDCToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET);
logger.info("*** [1.4a] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.4a] OIDC refresh token: {}", oidcTR.getRefreshToken());
TestModelUtils.checkTokenResponse(oidcTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), "service-account-" + CLIENT_ID, true);
assertTrue(ModelUtils.getAccessTokenFrom(oidcTR).getAudience().length == 1);
assertEquals(TOKEN_RESTRICTION_VRE_CONTEXT, ModelUtils.getAccessTokenFrom(oidcTR).getAudience()[0]);
// It is not possible to check programmatically if the header has been added to the call, See the logs if the Header is present.
}
@Test
public void test14bQueryOIDCTokenWithDynamicScopeWithFlagMode() throws Exception {
logger.info(
"*** [1.4b] Start testing query OIDC token with dynamic scope...");
TokenResponse oidcTR = KeycloakClientFactory.newInstance()
.useDynamicScopeInsteadOfCustomHeaderForContextRestricion(true)
.queryOIDCTokenWithContext(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TOKEN_RESTRICTION_VRE_CONTEXT);
logger.info("*** [1.4b] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.4b] OIDC refresh token: {}", oidcTR.getRefreshToken());
TestModelUtils.checkTokenResponse(oidcTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), "service-account-" + CLIENT_ID, true);
assertTrue(ModelUtils.getAccessTokenFrom(oidcTR).getAudience().length == 1);
assertEquals(TOKEN_RESTRICTION_VRE_CONTEXT, ModelUtils.getAccessTokenFrom(oidcTR).getAudience()[0]);
// It is not possible to check programmatically if the header has been added to the call, See the logs if the Header is present.
}
@Test
public void test15aQueryOIDCTokenForUserWithDynamicScope() throws Exception {
logger.info(
"*** [1.5a] Start testing query OIDC token for user on private client with dynamic scope...");
TokenResponse oidcTR = KeycloakClientFactory.newInstance()
.addDynamicScope(KeycloakClient.D4S_DYNAMIC_SCOPE_NAME, TOKEN_RESTRICTION_VRE_CONTEXT)
.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TEST_USER_USERNAME,
TEST_USER_PASSWORD);
logger.info("*** [1.5a] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.4a] OIDC refresh token: {}", oidcTR.getRefreshToken());
TestModelUtils.checkTokenResponse(oidcTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, true);
assertEquals(CLIENT_ID, ModelUtils.getAccessTokenFrom(oidcTR).getIssuedFor());
assertTrue(ModelUtils.getAccessTokenFrom(oidcTR).getAudience().length == 1);
assertEquals(TOKEN_RESTRICTION_VRE_CONTEXT, ModelUtils.getAccessTokenFrom(oidcTR).getAudience()[0]);
// It is not possible to check programmatically if the header has been added to the call, See the logs if the Header is present.
}
@Test
public void test15bQueryOIDCTokenForUserWithDynamicScopeWithFlagMode() throws Exception {
logger.info(
"*** [1.5b] Start testing query OIDC token for user on private client with dynamic scope...");
TokenResponse oidcTR = KeycloakClientFactory.newInstance()
.useDynamicScopeInsteadOfCustomHeaderForContextRestricion(true)
.queryOIDCTokenOfUserWithContext(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TEST_USER_USERNAME,
TEST_USER_PASSWORD, TOKEN_RESTRICTION_VRE_CONTEXT);
logger.info("*** [1.5b] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.5b] OIDC refresh token: {}", oidcTR.getRefreshToken());
TestModelUtils.checkTokenResponse(oidcTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, true);
assertEquals(CLIENT_ID, ModelUtils.getAccessTokenFrom(oidcTR).getIssuedFor());
assertTrue(ModelUtils.getAccessTokenFrom(oidcTR).getAudience().length == 1);
assertEquals(TOKEN_RESTRICTION_VRE_CONTEXT, ModelUtils.getAccessTokenFrom(oidcTR).getAudience()[0]);
// It is not possible to check programmatically if the header has been added to the call, See the logs if the Header is present.
}
@Test
public void test16aQueryOIDCTokenForUserOnPublicClientWithDynamicScope() throws Exception {
logger.info(
"*** [1.6a] Start testing query OIDC token for user on a public client with dynamic scope...");
TokenResponse oidcTR = KeycloakClientFactory.newInstance()
.addDynamicScope(KeycloakClient.D4S_DYNAMIC_SCOPE_NAME, TOKEN_RESTRICTION_VRE_CONTEXT)
.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, GATEWAY, null, TEST_USER_USERNAME,
TEST_USER_PASSWORD);
logger.info("*** [1.6a] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.6a] OIDC refresh token: {}", oidcTR.getRefreshToken());
TestModelUtils.checkTokenResponse(oidcTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, true);
assertEquals(GATEWAY, ModelUtils.getAccessTokenFrom(oidcTR).getIssuedFor());
assertTrue(ModelUtils.getAccessTokenFrom(oidcTR).getAudience().length == 1);
assertEquals(TOKEN_RESTRICTION_VRE_CONTEXT, ModelUtils.getAccessTokenFrom(oidcTR).getAudience()[0]);
// It is not possible to check programmatically if the header has been added to the call, See the logs if the Header is present.
}
@Test
public void test16bQueryOIDCTokenForUserOnPublicClientWithDynamicScopeWithFlagMode() throws Exception {
logger.info(
"*** [1.6b] Start testing query OIDC token for user on a public client with dynamic scope...");
TokenResponse oidcTR = KeycloakClientFactory.newInstance()
.useDynamicScopeInsteadOfCustomHeaderForContextRestricion(true)
.queryOIDCTokenOfUserWithContext(DEV_ROOT_CONTEXT, GATEWAY, null, TEST_USER_USERNAME,
TEST_USER_PASSWORD, TOKEN_RESTRICTION_VRE_CONTEXT);
logger.info("*** [1.6b] OIDC access token: {}", oidcTR.getAccessToken());
logger.info("*** [1.6b] OIDC refresh token: {}", oidcTR.getRefreshToken());
TestModelUtils.checkTokenResponse(oidcTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(oidcTR), TEST_USER_USERNAME, true);
assertEquals(GATEWAY, ModelUtils.getAccessTokenFrom(oidcTR).getIssuedFor());
assertTrue(ModelUtils.getAccessTokenFrom(oidcTR).getAudience().length == 1);
assertEquals(TOKEN_RESTRICTION_VRE_CONTEXT, ModelUtils.getAccessTokenFrom(oidcTR).getAudience()[0]);
// It is not possible to check programmatically if the header has been added to the call, See the logs if the Header is present.
}
@Test
public void test24QueryUMAToken() throws Exception {
logger.info("*** [2.4] Start testing query UMA token from Keycloak with context...");
TokenResponse umaTR = KeycloakClientFactory.newInstance().queryUMAToken(DEV_ROOT_CONTEXT, CLIENT_ID,
CLIENT_SECRET, TEST_AUDIENCE, null);
logger.info("*** [2.4] UMA access token: {}", umaTR.getAccessToken());
logger.info("*** [2.4] UMA refresh token: {}", umaTR.getRefreshToken());
TestModelUtils.checkTokenResponse(umaTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(umaTR), "service-account-" + CLIENT_ID, true);
}
@Test
public void test24aQueryUMAToken() throws Exception {
logger.info("*** [2.4a] Start testing query UMA token from Keycloak with URL...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
TokenResponse umaTR = client.queryUMAToken(tokenURL, CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
logger.info("*** [2.4a] UMA access token: {}", umaTR.getAccessToken());
logger.info("*** [2.4a] UMA refresh token: {}", umaTR.getRefreshToken());
TestModelUtils.checkTokenResponse(umaTR);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(umaTR), "service-account-" + CLIENT_ID, true);
}
@Test
public void test302IntrospectOIDCAccessToken() throws Exception {
logger.info("*** [3.2] Start testing introspect OIDC access token...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
TokenResponse oidcTR = client.queryOIDCToken(tokenURL, CLIENT_ID, CLIENT_SECRET);
TokenIntrospectionResponse tir = client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
oidcTR.getAccessToken());
TestModelUtils.checkTokenIntrospectionResponse(tir);
}
@Test
public void test302aIntrospectOIDCAccessToken() throws Exception {
logger.info("*** [3.2a] Start testing introspect OIDC access token...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
TokenResponse oidcTR = client.queryOIDCToken(tokenURL, CLIENT_ID, CLIENT_SECRET);
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
TokenIntrospectionResponse tir = client.introspectAccessToken(introspectionURL, CLIENT_ID, CLIENT_SECRET,
oidcTR.getAccessToken());
TestModelUtils.checkTokenIntrospectionResponse(tir);
}
@Test
public void test302bIntrospectOIDCAccessTokenFromDifferentServer() throws Exception {
logger.info("*** [3.2b] Start testing introspect OIDC access token with different base URLs...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
TokenResponse oidcTR = client.queryOIDCToken(tokenURL, CLIENT_ID, CLIENT_SECRET);
logger.info("*** [3.2b] OIDC access token: {}", oidcTR.getAccessToken());
URL introspectionURL = new URL(ACCOUNTS_DEV_INTROSPECTION_ENDPOINT);
TokenIntrospectionResponse tir = client.introspectAccessToken(introspectionURL, CLIENT_ID, CLIENT_SECRET,
oidcTR.getAccessToken());
TestModelUtils.checkTokenIntrospectionResponse(tir, true);
}
@Test
public void test304IntrospectUMAAccessToken() throws Exception {
logger.info("*** [3.4] Start testing introspect UMA access token...");
KeycloakClient client = KeycloakClientFactory.newInstance();
TokenResponse umaTR = client.queryUMAToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
TokenIntrospectionResponse tir = client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
umaTR.getAccessToken());
TestModelUtils.checkTokenIntrospectionResponse(tir);
}
@Test
public void test304aIntrospectUMAAccessToken() throws Exception {
logger.info("*** [3.4a] Start testing introspect UMA access token...");
KeycloakClient client = KeycloakClientFactory.newInstance();
TokenResponse umaTR = client.queryUMAToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
TokenIntrospectionResponse tir = client.introspectAccessToken(introspectionURL, CLIENT_ID, CLIENT_SECRET,
umaTR.getAccessToken());
TestModelUtils.checkTokenIntrospectionResponse(tir);
}
@Test
public void test306OIDCAccessTokenVerification() throws Exception {
logger.info("*** [3.6] Start OIDC access token verification...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
TokenResponse oidcTR = client.queryOIDCToken(tokenURL, CLIENT_ID, CLIENT_SECRET);
Assert.assertTrue(
client.isAccessTokenVerified(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken()));
}
@Test
public void test306aOIDCAccessTokenVerification() throws Exception {
logger.info("*** [3.6a] Start OIDC access token verification...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL tokenURL = client.getTokenEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
TokenResponse oidcTR = client.queryOIDCToken(tokenURL, CLIENT_ID, CLIENT_SECRET);
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
Assert.assertTrue(
client.isAccessTokenVerified(introspectionURL, CLIENT_ID, CLIENT_SECRET, oidcTR.getAccessToken()));
}
@Test
public void test307OIDCAccessTokenNonVerification() throws Exception {
logger.info("*** [3.7] Start OIDC access token NON verification...");
Assert.assertFalse(KeycloakClientFactory.newInstance().isAccessTokenVerified(
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, OLD_OIDC_ACCESS_TOKEN));
}
@Test
public void test307aOIDCAccessTokenNonVerification() throws Exception {
logger.info("*** [3.7a] Start OIDC access token NON verification...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
Assert.assertFalse(
client.isAccessTokenVerified(introspectionURL, CLIENT_ID, CLIENT_SECRET, OLD_OIDC_ACCESS_TOKEN));
}
@Test
public void test309UMAAccessTokenVerification() throws Exception {
logger.info("*** [3.9] Start UMA access token verification...");
KeycloakClient client = KeycloakClientFactory.newInstance();
TokenResponse umaTR = client.queryUMAToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
Assert.assertTrue(
client.isAccessTokenVerified(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken()));
}
@Test
public void test309aUMAAccessTokenVerification() throws Exception {
logger.info("*** [3.9a] Start UMA access token verification...");
KeycloakClient client = KeycloakClientFactory.newInstance();
TokenResponse umaTR = client.queryUMAToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
Assert.assertTrue(
client.isAccessTokenVerified(introspectionURL, CLIENT_ID, CLIENT_SECRET, umaTR.getAccessToken()));
}
@Test
public void test310UMAAccessTokenNonVerification() throws Exception {
logger.info("*** [3.10] Start UMA access token NON verification...");
Assert.assertFalse(KeycloakClientFactory.newInstance().isAccessTokenVerified(
DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, OLD_UMA_ACCESS_TOKEN));
}
@Test
public void test310aUMAAccessTokenNonVerification() throws Exception {
logger.info("*** [3.10a] Start UMA access token NON verification...");
KeycloakClient client = KeycloakClientFactory.newInstance();
URL introspectionURL = client.getIntrospectionEndpointURL(client.getRealmBaseURL(DEV_ROOT_CONTEXT));
Assert.assertFalse(
client.isAccessTokenVerified(introspectionURL, CLIENT_ID, CLIENT_SECRET, OLD_UMA_ACCESS_TOKEN));
}
@Test
public void test4GetTokenOfUser() throws Exception {
logger.info("*** [4] Start testing query token from Keycloak for user...");
TokenResponse tokenResponse = KeycloakClientHelper.getTokenForUser(DEV_ROOT_CONTEXT, TEST_USER_USERNAME,
TEST_USER_PASSWORD);
logger.info("*** [4] UMA access token: {}", tokenResponse.getAccessToken());
logger.info("*** [4] UMA refresh token: {}", tokenResponse.getRefreshToken());
TestModelUtils.checkTokenResponse(tokenResponse);
TestModelUtils.checkAccessToken(ModelUtils.getAccessTokenFrom(tokenResponse), TEST_USER_USERNAME, true);
}
@Test
public void test51ExchangeToken4Access() throws Exception {
logger.info("*** [5.1] Start testing token exchange for access token from Keycloak...");
KeycloakClient client = KeycloakClientFactory.newInstance();
TokenResponse oidcTR = client.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
TEST_USER_USERNAME, TEST_USER_PASSWORD);
logger.info("*** [5.1] OIDC access token: {}", oidcTR.getAccessToken());
TokenResponse exchangedTR = client.exchangeTokenForAccessToken(DEV_ROOT_CONTEXT, oidcTR.getAccessToken(),
CLIENT_ID, CLIENT_SECRET, CLIENT_ID);
logger.info("*** [5.1] Exchanged access token: {}", exchangedTR.getAccessToken());
TestModelUtils.checkTokenResponse(exchangedTR, false);
Assert.assertNull(exchangedTR.getRefreshToken());
TestModelUtils.checkTokenIntrospectionResponse(client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID,
CLIENT_SECRET, exchangedTR.getAccessToken()));
}
@Test
public void test51aExchangeUMAToken4Access() throws Exception {
logger.info("*** [5.1a] Start testing UMA token exchange for access token from Keycloak...");
KeycloakClient client = KeycloakClientFactory.newInstance();
TokenResponse oidcTR = client.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
TEST_USER_USERNAME, TEST_USER_PASSWORD);
logger.info("*** [5.1a] OIDC access token: {}", oidcTR.getAccessToken());
TokenResponse umaTR = client.queryUMAToken(DEV_ROOT_CONTEXT, oidcTR, TOKEN_RESTRICTION_VRE_CONTEXT, null);
logger.info("*** [5.1a] UMA access token: {}", umaTR.getAccessToken());
TokenResponse exchangedTR = client.exchangeTokenForAccessToken(DEV_ROOT_CONTEXT, umaTR.getAccessToken(),
CLIENT_ID, CLIENT_SECRET, CLIENT_ID);
logger.info("*** [5.1a] Exchanged access token: {}", exchangedTR.getAccessToken());
TestModelUtils.checkTokenResponse(exchangedTR, false);
Assert.assertNull(exchangedTR.getRefreshToken());
TestModelUtils.checkTokenIntrospectionResponse(client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID,
CLIENT_SECRET, exchangedTR.getAccessToken()));
}
@Test
public void test52ExchangeToken4refresh() throws Exception {
logger.info("*** [5.2] Start testing token exchange for refresh token from Keycloak...");
KeycloakClient client = KeycloakClientFactory.newInstance();
TokenResponse oidcTR = client.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
TEST_USER_USERNAME, TEST_USER_PASSWORD);
logger.info("*** [5.2] OIDC access token: {}", oidcTR.getAccessToken());
TokenResponse exchangedTR = client.exchangeTokenForRefreshToken(DEV_ROOT_CONTEXT, oidcTR.getAccessToken(),
CLIENT_ID, CLIENT_SECRET, CLIENT_ID);
logger.info("*** [5.2] Exchanged access token: {}", exchangedTR.getAccessToken());
logger.info("*** [5.2] Exchanged refresh token: {}", exchangedTR.getRefreshToken());
TestModelUtils.checkTokenResponse(exchangedTR);
TestModelUtils.checkTokenIntrospectionResponse(
client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET, exchangedTR.getAccessToken()));
}
@Test(expected = IllegalArgumentException.class)
public void test53ExchangeToken4Offline() throws Exception {
logger.info("*** [5.3] Start testing token exchange for offline token from Keycloak...");
KeycloakClient client = KeycloakClientFactory.newInstance();
TokenResponse oidcTR = client.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
TEST_USER_USERNAME, TEST_USER_PASSWORD);
logger.info("*** [5.3] OIDC access token: {}", oidcTR.getAccessToken());
// TokenResponse exchangedTR =
client.exchangeTokenForOfflineToken(DEV_ROOT_CONTEXT, oidcTR.getAccessToken(),
CLIENT_ID, CLIENT_SECRET, CLIENT_ID);
// For the moment this part is not covered by tests
// logger.info("*** [5.3] Exchanged access token: {}", exchangedTR.getAccessToken());
// logger.info("*** [5.3] Exchanged refresh token: {}", exchangedTR.getRefreshToken());
// TestModelUtils.checkTokenResponse(exchangedTR, true);
// TestModelUtils.checkOfflineToken(exchangedTR);
//
// TestModelUtils.checkTokenIntrospectionResponse(client.introspectAccessToken(DEV_ROOT_CONTEXT, CLIENT_ID,
// CLIENT_SECRET, exchangedTR.getAccessToken()));
}
@Test
public void test6GetAvatar() throws Exception {
logger.info("*** [6] Start testing get user's avatar...");
KeycloakClient client = KeycloakClientFactory.newInstance();
TokenResponse oidcTR = client.queryOIDCTokenOfUser(DEV_ROOT_CONTEXT, CLIENT_ID, CLIENT_SECRET,
TEST_USER_USERNAME, TEST_USER_PASSWORD);
byte[] avatarData = client.getAvatarData(DEV_ROOT_CONTEXT, oidcTR);
Assert.assertNotNull("Avatar data is null", avatarData);
logger.info("*** [6] Avatar image of user is {} bytes", avatarData.length);
}
}

View File

@ -1,15 +1,11 @@
package org.gcube.common.keycloak;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.interfaces.RSAPublicKey;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.common.keycloak.model.AccessToken;
import org.gcube.common.keycloak.model.ModelUtils;
import org.gcube.common.keycloak.model.RefreshToken;
import org.gcube.common.keycloak.model.TokenIntrospectionResponse;
import org.gcube.common.keycloak.model.TokenResponse;
import org.junit.After;
import org.junit.Assert;
@ -21,9 +17,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestModelUtils {
public class TestModels {
protected static final Logger logger = LoggerFactory.getLogger(TestModelUtils.class);
protected static final Logger logger = LoggerFactory.getLogger(TestModels.class);
@Before
public void setUp() throws Exception {
@ -33,22 +29,6 @@ public class TestModelUtils {
public void tearDown() throws Exception {
}
@Test
public void testTokenValidity() throws Exception {
logger.info("Start testing access token valdity...");
TokenResponse tr = new ObjectMapper().readValue(new File("src/test/resources/oidc-token-response.json"),
TokenResponse.class);
RSAPublicKey publicKey = ModelUtils.createRSAPublicKey(
new String(Files.readAllBytes(Paths.get("src/test/resources/rsa-public-key.pem"))));
// Valid signature
Assert.assertFalse("Token is valid", ModelUtils.isValid(tr.getAccessToken(), publicKey));
Assert.assertFalse("Token is not expired", ModelUtils.isValid(tr.getAccessToken(), publicKey, true));
Assert.assertTrue("Token is valid", ModelUtils.isValid(tr.getAccessToken(), publicKey, false));
Assert.assertFalse("Token signature is valid", ModelUtils.isValid(tr.getAccessToken().replace("ZV9hY2Nlc3", "ZV9hY2Nlcc"), publicKey));
}
@Test
public void testTokenResponseForOIDC() throws Exception {
logger.info("Start testing OIDC token response object binding...");
@ -76,7 +56,7 @@ public class TestModelUtils {
AccessToken at = new ObjectMapper().readValue(new File("src/test/resources/uma-access-token.json"),
AccessToken.class);
checkAccessToken(at, null, true);
checkAccessToken(at, null);
}
@Test
@ -88,9 +68,9 @@ public class TestModelUtils {
AccessToken at2 = ModelUtils.getAccessTokenFrom("Bearer " + tr.getAccessToken());
AccessToken at3 = ModelUtils.getAccessTokenFrom("bearer " + tr.getAccessToken());
checkAccessToken(at1, null, true);
checkAccessToken(at2, null, true);
checkAccessToken(at3, null, true);
checkAccessToken(at1, null);
checkAccessToken(at2, null);
checkAccessToken(at3, null);
Assert.assertEquals(ModelUtils.toJSONString(at1), ModelUtils.toJSONString(at2));
Assert.assertEquals(ModelUtils.toJSONString(at2), ModelUtils.toJSONString(at3));
}
@ -105,52 +85,25 @@ public class TestModelUtils {
}
public static void checkTokenResponse(TokenResponse tr) throws Exception {
checkTokenResponse(tr, true);
}
public static void checkTokenResponse(TokenResponse tr, boolean checkAlsoRefreshToken) throws Exception {
Assert.assertNotNull(tr);
Assert.assertEquals("bearer", tr.getTokenType().toLowerCase());
Assert.assertNotNull("Access token is null", tr.getAccessToken());
if (checkAlsoRefreshToken) {
Assert.assertNotNull("Refresh token is null", tr.getRefreshToken());
}
Assert.assertNotNull(tr.getAccessToken());
Assert.assertNotNull(tr.getRefreshToken());
}
public static void checkAccessToken(AccessToken at, String preferredUsername, boolean checkAudience) {
public static void checkAccessToken(AccessToken at, String preferredUsername) {
logger.debug("Access token:\n{}", ModelUtils.toJSONString(at, true));
Assert.assertNotNull(at.getPreferredUsername());
if (preferredUsername != null) {
Assert.assertEquals(preferredUsername, at.getPreferredUsername());
}
if (checkAudience) {
Assert.assertNotNull("Audience is null", at.getAudience());
}
Assert.assertNotNull(at.getAudience());
}
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());
}
public static void checkOfflineToken(TokenResponse tr) throws Exception {
RefreshToken rt = ModelUtils.getRefreshTokenFrom(tr.getRefreshToken());
Assert.assertEquals("Offline", rt.getType());
Assert.assertNull("Expiration is not null", rt.getExp());
}
public static void checkTokenIntrospectionResponse(TokenIntrospectionResponse tir) {
checkTokenIntrospectionResponse(tir, true);
}
public static void checkTokenIntrospectionResponse(TokenIntrospectionResponse tir, boolean mustBeActive) {
logger.debug("Token introspection response :\n{}", ModelUtils.toJSONString(tir, true));
if (mustBeActive) {
Assert.assertTrue("Token is not active", tir.getActive());
} else {
Assert.assertFalse("Token is active", tir.getActive());
}
Assert.assertNotNull(rt.getOtherClaims());
Assert.assertNotNull(rt.getAudience());
}
}

View File

@ -12,7 +12,7 @@
</layout>
</appender>
<logger name="org.gcube.common.keycloak" additivity="false">
<logger name="org.gcube" additivity="false">
<level value="TRACE" />
<appender-ref ref="console" />
</logger>

View File

@ -1 +0,0 @@
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjyJAmPx5K2eGYjvRmiBC8as3nF/jsYSUgBnlul9TNEdWSPuxTzntTb37xDGPmVRkNyOCJmRnBcI8GbrWz8SHJ643JTKp8yx4zDQCgLD72crb9ah/Tfu8KpDz3+FRuYLE4EvvRCGBnsFO2vSM02iTAp7nSToOCX4jCCrDMBUUJkIzuZIQUBTx8lvWl/M6LtQAqS7Gw3wsZSklRcvsR9qlCUxJW3cvhALt9wWrejSJ3LaR6TMaNa8k6Ojk6bJ3/5c6OifxYde0YjXIOaeMkkgnfoQvs4cyhvNxCXLx65+VKV4Dlts7cTgJvuodV0w/UhJGfUxgA9V6IQowmwHNBnYAMwIDAQAB