Compare commits

...

17 Commits

Author SHA1 Message Date
Mauro Mugnaini ccce7944ee
Moved from `maven-portal-bom` to `gcube-bom` 2024-05-08 13:56:23 +02:00
Mauro Mugnaini 83041827c4
Releasing `v1.3.2` 2024-04-24 10:59:46 +02:00
Mauro Mugnaini 098bfc9fef
Removed `permission` list from exchange method signature and improved it adding also `refresh-token` as requested token type and `offline_access` scope. Added also extra headers support for all the token methods. 2024-03-27 18:43:21 +01:00
Mauro Mugnaini 6da3a9f55a
Moved to `maven-portal-bom` `v3.7.0` and `openjdk11` 2024-03-26 11:40:54 +01:00
Mauro Mugnaini ed5ea15599 Merge pull request 'Porting `token_exchange` branch to `master`' (!1) from token_exchange into master
Reviewed-on: #1
2024-03-26 11:34:35 +01:00
Alfredo Oliviero 33a22f9448 rimosso header 2024-03-25 15:50:45 +01:00
Alfredo Oliviero 7c1597d479 Token exchange (#27099)
Header X-D4Science-Context in query exchange and refresh

- performQueryTokenWithPOST accepts also optional headers
- performURLEncodedPOSTSendData accepts also optional headers
- implemented queryExchangeToken
2024-03-22 15:43:59 +01:00
Mauro Mugnaini fccbedb299 Release of new version with addition of new `Catalogue-Manager` and `Catalogue-Moderator` roles to the enum of known roles (#23623) 2022-07-07 16:17:28 +02:00
Mauro Mugnaini fea71c720c Added new `Catalogue-Manager` and `Catalogue-Moderator` roles to the enum (#23623) 2022-07-06 12:54:44 +02:00
Mauro Mugnaini bbcd35e14f Updated `maven-portal-bom` to last version: 3.6.3 2021-06-24 18:25:36 +02:00
Mauro Mugnaini a37cd40b99 Releasing v.1.3.0 2021-06-24 11:06:19 +02:00
Mauro Mugnaini bee9d59f22 Internally renamed variable `token` to `tokens` since the JSON helds more tah one token 2021-06-24 11:03:46 +02:00
Mauro Mugnaini 3c201dd6f0 Internally renamed variable `token` to `tokens` since the JSON helds more tah one token 2021-06-24 11:03:31 +02:00
Mauro Mugnaini 79c45245dd Transformed in unit test class 2021-05-06 17:52:08 +02:00
Mauro Mugnaini c5a9dca54c Now audience can be provided in both encoded and not encoded form (starts with "/" check is performed). Ugly test added. 2021-05-06 17:51:46 +02:00
Mauro Mugnaini 17e503c786 Added method to retrieve UMA token by using `clientId` and `clientSecret` in a specific `audience` (aka context, that must be in `URLEncoded` form) and Javadoc for all interesting methods 2021-05-06 17:15:10 +02:00
Mauro Mugnaini 527f04fb67 Prepared for the new 1.3.0 snapshot version. Moved to `test scope` some dependencies wronlgy set as `default scope` 2021-05-06 17:08:19 +02:00
11 changed files with 386 additions and 74 deletions

View File

@ -3,23 +3,14 @@
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/classes" path="src/main/java">
@ -30,9 +21,20 @@
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>

View File

@ -1,11 +1,11 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8
org.eclipse.jdt.core.compiler.source=11

View File

@ -1,6 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="oidc-library">
<wb-resource deploy-path="/" source-path="/src/main/resources"/>
<wb-resource deploy-path="/" source-path="/src/main/java"/>
</wb-module>
</project-modules>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
<installed facet="java" version="1.8"/>
<installed facet="jst.utility" version="1.0"/>
<installed facet="java" version="11"/>
</faceted-project>

View File

@ -2,6 +2,19 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
# Changelog for "oidc-library"
## [v1.3.2]
- Header X-D4Science-Context in query exchange and refresh
- Token exchange (#27099)
- PerformQueryTokenWithPOST accepts also optional headers
- PerformURLEncodedPOSTSendData accepts also optional headers
- Moved from `maven-portal-bom` to `gcube-bom`
## [v1.3.1]
- Added `Catalogue-Manager` and `Catalogue-Moderator` roles to the enum (#23623)
## [v1.3.0]
- Added method to retrieve UMA token by using `clientId` and `clientSecret` in a specific `audience` (aka context) that can now be provided in both encoded and not encoded form (starts with "/" check is performed).
## [v1.2.1]
- "Data-Editor" role added (#20896), some logs changed to debug level

42
pom.xml
View File

@ -7,20 +7,20 @@
<parent>
<artifactId>maven-parent</artifactId>
<groupId>org.gcube.tools</groupId>
<version>1.1.0</version>
<version>1.2.0</version>
<relativePath />
</parent>
<groupId>org.gcube.common</groupId>
<artifactId>oidc-library</artifactId>
<version>1.2.1</version>
<version>1.3.2</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.gcube.distribution</groupId>
<artifactId>maven-portal-bom</artifactId>
<version>3.6.0</version>
<artifactId>gcube-bom</artifactId>
<version>2.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -33,29 +33,47 @@
<url>https://code-repo.d4science.org/gCubeSystem/${project.artifactId}</url>
</scm>
<properties>
<java.version>8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<slf4j-log4j12.version>1.6.4</slf4j-log4j12.version>
<log4j.version>1.2.16</log4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build />

View File

@ -12,6 +12,8 @@ public class D4ScienceMappings {
ACCOUNTING_MANAGER("Accounting-Manager"),
CATALOGUE_ADMIN("Catalogue-Admin"),
CATALOGUE_EDITOR("Catalogue-Editor"),
CATALOGUE_MANAGER("Catalogue-Manager"),
CATALOGUE_MODERATOR("Catalogue-Moderator"),
DATA_EDITOR("Data-Editor"),
DATA_MANAGER("Data-Manager"),
DATAMINER_MANAGER("DataMiner-Manager"),
@ -49,23 +51,4 @@ public class D4ScienceMappings {
}
public enum Scope {
BELONGS("belongs");
// TODO will be defined later
// LIST("list"),
// READ("read"),
// WRITE("write"),
// EXECUTE("execute");
private String str;
Scope(String str) {
this.str = str;
}
public String asString() {
return str;
}
}
}

View File

@ -26,7 +26,7 @@ public class JWTToken implements Serializable {
public static final String ACCOUNT_RESOURCE = "account";
private String raw;
private JSONObject token;
private JSONObject tokens;
private JSONObject payload;
public static JWTToken fromString(String tokenString) {
@ -47,7 +47,7 @@ public class JWTToken implements Serializable {
}
private void parse() throws ParseException {
token = (JSONObject) new JSONParser().parse(this.raw);
tokens = (JSONObject) new JSONParser().parse(this.raw);
String[] parts = getAccessTokenString().split("\\.");
payload = (JSONObject) new JSONParser().parse(new String(Base64.getDecoder().decode(parts[1])));
}
@ -57,11 +57,11 @@ public class JWTToken implements Serializable {
}
public String getAccessTokenString() {
return (String) token.get("access_token");
return (String) tokens.get("access_token");
}
public String getRefreshTokenString() {
return (String) token.get("refresh_token");
return (String) tokens.get("refresh_token");
}
/**

View File

@ -13,6 +13,7 @@ import java.net.ProtocolException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -58,9 +59,35 @@ public class OpenIdConnectRESTHelper {
return q;
}
/**
* Queries from the OIDC server an OIDC access token, by using provided clientId and client secret.
*
* @param clientId the client id
* @param clientSecret the client secret
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryClientToken(String clientId, String clientSecret, URL tokenURL)
throws OpenIdConnectRESTHelperException {
return queryClientToken(clientId, clientSecret, tokenURL, null);
}
/**
* Queries from the OIDC server an OIDC access token, by using provided clientId and client secret.
*
* @param clientId the client id
* @param clientSecret the client secret
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param extraHeaders extra HTTP headers to add to the request (e.g. <code>X-D4Science-Context</code> custom header), may be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryClientToken(String clientId, String clientSecret, URL tokenURL,
Map<String, String> extraHeaders)
throws OpenIdConnectRESTHelperException {
Map<String, List<String>> params = new HashMap<>();
params.put("grant_type", Arrays.asList("client_credentials"));
try {
@ -73,28 +100,41 @@ public class OpenIdConnectRESTHelper {
} catch (UnsupportedEncodingException e) {
logger.error("Cannot URL encode 'client_secret'", e);
}
return performQueryTokenWithPOST(tokenURL, null, params);
return performQueryTokenWithPOST(tokenURL, null, params, extraHeaders);
}
public static JWTToken queryToken(String clientId, URL tokenURL, String code, String scope,
String redirectURI) throws Exception {
return queryToken(clientId, tokenURL, code, scope, redirectURI, null);
}
public static JWTToken queryToken(String clientId, URL tokenURL, String code, String scope,
String redirectURI, Map<String, String> extraHeaders) throws Exception {
Map<String, List<String>> params = new HashMap<>();
params.put("client_id", Arrays.asList(URLEncoder.encode(clientId, "UTF-8")));
params.put("grant_type", Arrays.asList("authorization_code"));
params.put("scope", Arrays.asList(URLEncoder.encode(scope, "UTF-8")));
params.put("code", Arrays.asList(URLEncoder.encode(code, "UTF-8")));
params.put("redirect_uri", Arrays.asList(URLEncoder.encode(redirectURI, "UTF-8")));
return performQueryTokenWithPOST(tokenURL, null, params);
return performQueryTokenWithPOST(tokenURL, null, params, extraHeaders);
}
public static JWTToken performQueryTokenWithPOST(URL tokenURL, String authorization,
protected static JWTToken performQueryTokenWithPOST(URL tokenURL, String authorization,
Map<String, List<String>> params) throws OpenIdConnectRESTHelperException {
return performQueryTokenWithPOST(tokenURL, authorization, params, null);
}
protected static JWTToken performQueryTokenWithPOST(URL tokenURL, String authorization,
Map<String, List<String>> params, Map<String, String> headers) throws OpenIdConnectRESTHelperException {
logger.debug("Querying access token from OIDC server with URL: {}", tokenURL);
StringBuilder sb;
try {
HttpURLConnection httpURLConnection = performURLEncodedPOSTSendData(tokenURL, params, authorization);
HttpURLConnection httpURLConnection = performURLEncodedPOSTSendData(tokenURL, params, authorization,
headers);
sb = new StringBuilder();
int httpResultCode = httpURLConnection.getResponseCode();
@ -134,6 +174,12 @@ public class OpenIdConnectRESTHelper {
protected static HttpURLConnection performURLEncodedPOSTSendData(URL url, Map<String, List<String>> params,
String authorization) throws IOException, ProtocolException, UnsupportedEncodingException {
return performURLEncodedPOSTSendData(url, params, authorization, null);
}
protected static HttpURLConnection performURLEncodedPOSTSendData(URL url, Map<String, List<String>> params,
String authorization, Map<String, String> headers)
throws IOException, ProtocolException, UnsupportedEncodingException {
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
@ -145,6 +191,12 @@ public class OpenIdConnectRESTHelper {
logger.debug("Adding authorization header as: {}", authorization);
con.setRequestProperty("Authorization", authorization);
}
if (headers != null) {
for (String key : headers.keySet()) {
con.setRequestProperty(key, headers.get(key));
}
}
OutputStream os = con.getOutputStream();
String queryString = mapToQueryString(params);
@ -154,11 +206,87 @@ public class OpenIdConnectRESTHelper {
return con;
}
public static JWTToken queryUMAToken(URL tokenUrl, String authorizationToken, String audience,
/**
* Queries from the OIDC server an UMA token, by using provided clientId and client secret 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 clientId the client id
* @param clientSecret the client secret
* @param audience the audience (context) where to request the issuing of the token (URLEncoded or not)
* @param permissions a list of permissions, can be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryUMAToken(URL tokenUrl, String clientId, String clientSecret, String audience,
List<String> permissions) throws OpenIdConnectRESTHelperException {
return queryUMAToken(tokenUrl, clientId, clientSecret, audience, permissions, null);
}
/**
* Queries from the OIDC server an UMA token, by using provided clientId and client secret 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 clientId the client id
* @param clientSecret the client secret
* @param audience the audience (context) where to request the issuing of the token (URLEncoded or not)
* @param permissions a list of permissions, can be <code>null</code>
* @param extraHeaders extra HTTP headers to add to the request (e.g. <code>X-D4Science-Context</code> custom header), may be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryUMAToken(URL tokenUrl, String clientId, String clientSecret, String audience,
List<String> permissions, Map<String, String> extraHeaders) throws OpenIdConnectRESTHelperException {
return queryUMAToken(tokenUrl,
"Basic " + Base64.getEncoder().encodeToString((clientId + ":" + clientSecret).getBytes()),
audience, permissions, extraHeaders);
}
/**
* Queries from the OIDC server an UMA token, by using provided access token, 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 authorization the auth token (the access token URLEncoded by the "Bearer " string)
* @param audience the audience (context) where to request the issuing of the token (URLEncoded or not)
* @param permissions a list of permissions, can be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryUMAToken(URL tokenUrl, String authorization, String audience,
List<String> permissions) throws OpenIdConnectRESTHelperException {
return queryUMAToken(tokenUrl, authorization, audience, permissions, null);
}
/**
* Queries from the OIDC server an UMA token, by using provided access token, 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 authorization the auth token (the access token URLEncoded by the "Bearer " string)
* @param audience the audience (context) where to request the issuing of the token (URLEncoded or not)
* @param permissions a list of permissions, can be <code>null</code>
* @param extraHeaders extra HTTP headers to add to the request (e.g. <code>X-D4Science-Context</code> custom header), may be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryUMAToken(URL tokenUrl, String authorization, String audience,
List<String> permissions, Map<String, String> extraHeaders) throws OpenIdConnectRESTHelperException {
Map<String, List<String>> params = new HashMap<>();
params.put("grant_type", Arrays.asList("urn:ietf:params:oauth:grant-type:uma-ticket"));
if (audience.startsWith("/")) {
try {
logger.trace("Audience was provided in non URL encoded form, encoding it");
audience = URLEncoder.encode(audience, "UTF-8");
} catch (UnsupportedEncodingException e) {
logger.error("Cannot URL encode 'audience'", e);
}
}
try {
params.put("audience", Arrays.asList(URLEncoder.encode(audience, "UTF-8")));
} catch (UnsupportedEncodingException e) {
@ -174,18 +302,143 @@ public class OpenIdConnectRESTHelper {
}
}).collect(Collectors.toList()));
}
return performQueryTokenWithPOST(tokenUrl, authorizationToken, params);
return performQueryTokenWithPOST(tokenUrl, authorization, params, extraHeaders);
}
/**
* Queries from the OIDC server an exchanged token by using provided access token, optionally for the given audience (context)
* in URLEncoded form or not.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param authorization the auth token (the access token URLEncoded by the "Bearer " string)
* @param audience the audience (context) where to request the issuing of the token (URLEncoded or not), may be <code>null</code>
* @param clientId the client id
* @param clientSecret the client secret
* @param extraHeaders extra HTTP headers to add to the request (e.g. <code>X-D4Science-Context</code> custom header), may be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryExchangeToken(URL tokenUrl, String authorization, String audience, String client_id,
String client_secret, Map<String, String> extraHeaders) throws OpenIdConnectRESTHelperException {
return queryExchangeToken(tokenUrl, authorization, audience, client_id, client_secret,
"urn:ietf:params:oauth:token-type:access_token", null, extraHeaders);
}
/**
* Queries from the OIDC server an exchanged token by using provided access token, optionally for the given audience (context)
* in URLEncoded form or not.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param authorization the auth token (the access token URLEncoded by the "Bearer " string)
* @param audience the audience (context) where to request the issuing of the token (URLEncoded or not), may be <code>null</code>
* @param clientId the client id
* @param clientSecret the client secret
* @param withRefreshToken request also the refresh token (forced to <code>true</code> for offline requests)
* @param offline request a refresh token of offline type (TYP claim)
* @param extraHeaders extra HTTP headers to add to the request (e.g. <code>X-D4Science-Context</code> custom header), may be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryExchangeToken(URL tokenUrl, String authorization, String audience, String clientId,
String clientSecret, boolean withRefreshToken, boolean offline, Map<String, String> extraHeaders)
throws OpenIdConnectRESTHelperException {
return queryExchangeToken(tokenUrl, authorization, audience, clientId, clientSecret,
withRefreshToken || offline ? "urn:ietf:params:oauth:token-type:refresh_token"
: "urn:ietf:params:oauth:token-type:access_token",
offline ? "offline_access" : null, extraHeaders);
}
/**
* Queries from the OIDC server an exchanged token by using provided access token, optionally for the given audience (context)
* in URLEncoded form or not.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param authorization the auth token (the access token URLEncoded by the "Bearer " string)
* @param audience the audience (context) where to request the issuing of the token (URLEncoded or not), may be <code>null</code>
* @param clientId the client id
* @param clientSecret the client secret
* @param requestedTokenType the requested token type (e.g. <code>urn:ietf:params:oauth:token-type:refresh_token</code> for refresh token)
* @param scope the optional scope to request (e.g. <code>offline_access</code> for an offline token)
* @param extraHeaders extra HTTP headers to add to the request (e.g. <code>X-D4Science-Context</code> custom header), may be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryExchangeToken(URL tokenUrl, String authorization, String audience, String clientId,
String clientSecret, String requestedTokenType, String scope, Map<String, String> extraHeaders)
throws OpenIdConnectRESTHelperException {
logger.info("Querying exchange token for context: " + audience);
Map<String, List<String>> params = new HashMap<>();
params.put("subject_token", Arrays.asList(authorization));
params.put("client_id", Arrays.asList(clientId));
params.put("client_secret", Arrays.asList(clientSecret));
params.put("grant_type", Arrays.asList("urn:ietf:params:oauth:grant-type:token-exchange"));
params.put("subject_token_type", Arrays.asList("urn:ietf:params:oauth:token-type:access_token"));
params.put("requested_token_type", Arrays.asList(requestedTokenType));
if (scope != null) {
params.put("scope", Arrays.asList(scope));
}
if (audience != null) {
if (audience.startsWith("/")) {
try {
logger.trace("Audience was provided in non URL encoded form, encoding it");
audience = URLEncoder.encode(audience, "UTF-8");
} catch (UnsupportedEncodingException e) {
logger.error("Cannot URL encode 'audience'", e);
}
}
try {
params.put("audience", Arrays.asList(URLEncoder.encode(audience, "UTF-8")));
} catch (UnsupportedEncodingException e) {
logger.error("Cannot URL encode 'audience'", e);
}
}
return performQueryTokenWithPOST(tokenUrl, null, params, extraHeaders);
}
/**
* Refreshes the token from the OIDC server.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param token the token to be refreshed
* @return a new token refreshed from the previous one
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken refreshToken(URL tokenURL, JWTToken token) throws OpenIdConnectRESTHelperException {
return refreshToken(tokenURL, null, null, token);
}
/**
* Refreshes the token from the OIDC server for a specific client represented by the client id.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param clientId the client id
* @param token the token to be refreshed
* @return a new token refreshed from the previous one
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken refreshToken(URL tokenURL, String clientId, JWTToken token)
throws OpenIdConnectRESTHelperException {
return refreshToken(tokenURL, clientId, null, token);
}
/**
* Refreshes the token from the OIDC server for a specific client represented by the client id.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param clientId the client id
* @param clientSecret the client secret
* @param token the token to be refreshed
* @return a new token refreshed from the previous one
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken refreshToken(URL tokenURL, String clientId, String clientSecret, JWTToken token)
throws OpenIdConnectRESTHelperException {
@ -233,10 +486,27 @@ public class OpenIdConnectRESTHelper {
}
}
/**
* Performs the logout (SSOut) from all the sessions opened in the OIDC server.
*
* @param logoutUrl the logut endpoint {@link URL} of the OIDC server
* @param token the token used to take info from
* @return <code>true</code> if the logout is performed correctly, <code>false</code> otherwise
* @throws IOException if an I/O error occurs during the communication with the server
*/
public static boolean logout(URL logoutUrl, JWTToken token) throws IOException {
return logout(logoutUrl, null, token);
}
/**
* Performs the logout from the session related to the provided client id in the OIDC server.
*
* @param logoutUrl the logut endpoint {@link URL} of the OIDC server
* @param clientId the client id
* @param token the token used to take info from
* @return <code>true</code> if the logout is performed correctly, <code>false</code> otherwise
* @throws IOException if an I/O error occurs during the communication with the server
*/
public static boolean logout(URL logoutUrl, String clientId, JWTToken token) throws IOException {
Map<String, List<String>> params = new HashMap<>();
if (clientId == null) {
@ -276,9 +546,9 @@ public class OpenIdConnectRESTHelper {
}
if (conn.getResponseCode() == 200) {
String contentType = conn.getContentType();
logger.debug("Getting the stream to the avatar resource with MIME: {}", contentType);
InputStream is = conn.getInputStream();
buffer = new ByteArrayOutputStream();
int nRead;
@ -286,7 +556,7 @@ public class OpenIdConnectRESTHelper {
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
return buffer.toByteArray();
} else {

View File

@ -1,5 +1,8 @@
package org.gcube.oidc.rest;
import org.junit.Assert;
import org.junit.Test;;
public class JWTTokenTest {
private static String tokenString = "{\"access_token\":\"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJfNEdzbmg3eGZpQ2VNOUFFOTU4S0FqeG5hcllmZlBMbDRrVEpjajN5bThFIn0.eyJleHAiOjE1OTA2NzU4NDEsImlhdCI6MTU5MDY3NTU0MSwiYXV0aF90aW1lIjoxNTkwNjc1NTQwLCJqdGkiOiIxZDc2MjZmMi0xYjcxLTQ2NmEtOWNjNS0wNmYyNWYwODgzZDciLCJpc3MiOiJodHRwczovL251YmlzMi5pbnQuZDRzY2llbmNlLm5ldC9hdXRoL3JlYWxtcy9kNHNjaWVuY2UiLCJhdWQiOlsiJTJGZ2N1YmUiLCIlMkZnY3ViZSUyRmRldk5leHQlMkZOZXh0TmV4dCIsIiUyRmdjdWJlJTJGZGV2c2VjJTJGZGV2VlJFIiwiYWNjb3VudCJdLCJzdWIiOiIzNTQ5MjQ2MS0yZjY4LTQ1YTctOGQyNy1iOGNjNzgyOTJkNGEiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJwb3J0YWwiLCJzZXNzaW9uX3N0YXRlIjoiYzA4MzYzOWUtMjNlMy00ZWU2LTg3YjgtODRkMDcxMDBkY2Y2IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIqIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiJTJGZ2N1YmUiOnsicm9sZXMiOlsiTWVtYmVyIl19LCIlMkZnY3ViZSUyRmRldk5leHQlMkZOZXh0TmV4dCI6eyJyb2xlcyI6WyJNZW1iZXIiXX0sIiUyRmdjdWJlJTJGZGV2c2VjJTJGZGV2VlJFIjp7InJvbGVzIjpbIk1lbWJlciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJuYW1lIjoiTWF1cm8gTXVnbmFpbmkiLCJncm91cHMiOltdLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJtYXVybyIsImdpdmVuX25hbWUiOiJNYXVybyIsImZhbWlseV9uYW1lIjoiTXVnbmFpbmkiLCJlbWFpbCI6Im1hdXJvLm11Z25haW5pQG51Ymlzd2FyZS5jb20ifQ.dYHmdm23iqO3swhUllyICnzhJlH8h1pTmT8n7S7w-y2b2pNTgK98YiRspSbIC-yPzreAf_GkvsUWyVXeRKnbstiwonIeH5EjVglEF1LgppzTClqaMel1C1AcbPdccno7uIzsE0m03ErKwhzOS8o3SiZEZfELg6bH-UtdOrqnB0Hk8EGVZ7wfso-LwumMw_t600l7E_m4wuPw2UqQNHVtu714043_1cAi4YQXg-KVGzhLcwX-zZj--EJgmm8voTHTENQ-mKYuM-UCK2iZkVYOLcz4I6W97nLbk_Vx59ysTZh4J21cbh7sQRwhp5kE3itYV1ec-xHfjWDjTY-DDZNJ-Q\",\"expires_in\":300,\"refresh_expires_in\":1800,\"refresh_token\":\"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIzZjUyZDcxMS01Nzk5LTRjYjYtOGExMi02MzEyNjg2NGU0ODAifQ.eyJleHAiOjE1OTA2NzczNDEsImlhdCI6MTU5MDY3NTU0MSwianRpIjoiODdkNDAwN2EtMjQ4MC00MDliLTgyZTAtODFkOTIwZmFiM2E5IiwiaXNzIjoiaHR0cHM6Ly9udWJpczIuaW50LmQ0c2NpZW5jZS5uZXQvYXV0aC9yZWFsbXMvZDRzY2llbmNlIiwiYXVkIjoiaHR0cHM6Ly9udWJpczIuaW50LmQ0c2NpZW5jZS5uZXQvYXV0aC9yZWFsbXMvZDRzY2llbmNlIiwic3ViIjoiMzU0OTI0NjEtMmY2OC00NWE3LThkMjctYjhjYzc4MjkyZDRhIiwidHlwIjoiUmVmcmVzaCIsImF6cCI6InBvcnRhbCIsInNlc3Npb25fc3RhdGUiOiJjMDgzNjM5ZS0yM2UzLTRlZTYtODdiOC04NGQwNzEwMGRjZjYiLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIn0.8Mz_hlZ7635YRW_f1c4fHEzUzutRLxuooA0XAu3g24w\",\"token_type\":\"bearer\",\"id_token\":\"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJfNEdzbmg3eGZpQ2VNOUFFOTU4S0FqeG5hcllmZlBMbDRrVEpjajN5bThFIn0.eyJleHAiOjE1OTA2NzU4NDEsImlhdCI6MTU5MDY3NTU0MSwiYXV0aF90aW1lIjoxNTkwNjc1NTQwLCJqdGkiOiJkM2M1Mjc5NC00ZTJkLTQ4MWQtODBhNy0wMjdiZWIxNzM0YmIiLCJpc3MiOiJodHRwczovL251YmlzMi5pbnQuZDRzY2llbmNlLm5ldC9hdXRoL3JlYWxtcy9kNHNjaWVuY2UiLCJhdWQiOiJwb3J0YWwiLCJzdWIiOiIzNTQ5MjQ2MS0yZjY4LTQ1YTctOGQyNy1iOGNjNzgyOTJkNGEiLCJ0eXAiOiJJRCIsImF6cCI6InBvcnRhbCIsInNlc3Npb25fc3RhdGUiOiJjMDgzNjM5ZS0yM2UzLTRlZTYtODdiOC04NGQwNzEwMGRjZjYiLCJhY3IiOiIxIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsIm5hbWUiOiJNYXVybyBNdWduYWluaSIsImdyb3VwcyI6W10sInByZWZlcnJlZF91c2VybmFtZSI6Im1hdXJvIiwiZ2l2ZW5fbmFtZSI6Ik1hdXJvIiwiZmFtaWx5X25hbWUiOiJNdWduYWluaSIsImVtYWlsIjoibWF1cm8ubXVnbmFpbmlAbnViaXN3YXJlLmNvbSJ9.X4aY7q3cQ5TFk8phfpKlomE1kttu4hIySEfNjeasQl3lDMiGspnS2LmilSV9agFkDs2z8sdvWLUmmCpevZ_eOdDK0WgsmuYunXXOOCBbNyzzw6AmyDK4DYC2aUUfe8wNndgi7e7bf1TTA4TuFJgC_-xaWfwBrIU8NmDZyozBsms2s4oXxMXUGSb_WmJnXARnHIfWR0F72fngF7jkGs_S6UjyB3g4ZKFk1F3ctrxNT8S49Y82w6n7RqjaLkPBq_WtSXnOQG0Osagv1lkkg2FeXrE6lKZVdAsxbVFVN9epFlvn5aFB7OK1smevjYd_PQxk498rm11H4WLkXBgUqifKWg\",\"not-before-policy\":0,\"session_state\":\"c083639e-23e3-4ee6-87b8-84d07100dcf6\",\"scope\":\"openid profile email\"}";
@ -7,8 +10,14 @@ public class JWTTokenTest {
public JWTTokenTest() {
}
public static void main(String[] args) {
@Test
public void testTokenConstruction() {
JWTToken token = JWTToken.fromString(tokenString);
System.out.println(token.getAud());
Assert.assertNotNull(token.getAud());
Assert.assertNotNull(token.getAzp());
Assert.assertNotNull(token.getAccessTokenString());
Assert.assertNotNull(token.getSub());
Assert.assertNotNull(token.getExp());
}
}

View File

@ -8,34 +8,43 @@ public class RestHelperTest {
public RestHelperTest() {
}
/**
* To be re-enabled when the token is took programmatically
*/
// @Test
// @Test
public void getAvatar() throws MalformedURLException {
String accessTokenBearer = "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSSklZNEpoNF9qdDdvNmREY0NlUDFfS1l0akcxVExXVW9oMkQ2Tzk1bFNBIn0.eyJleHAiOjE1OTc0MTc4MzEsImlhdCI6MTU5NzQxNzUzMSwiYXV0aF90aW1lIjoxNTk3NDE3NTI5LCJqdGkiOiI5ZjAwMzM1Yy1jM2NlLTQ3ZTktYTRhZS05MmM4ZDc1NmUyOWMiLCJpc3MiOiJodHRwczovL2FjY291bnRzLmRldi5kNHNjaWVuY2Uub3JnL2F1dGgvcmVhbG1zL2Q0c2NpZW5jZSIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiIzZTcwMzBhNS00M2MzLTRiNjUtYWMwYS0xNTJkYmU3MDI0NTIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJwcmVwcm9kMi5kNHNjaWVuY2Uub3JnIiwic2Vzc2lvbl9zdGF0ZSI6IjAxOWEzMTVjLTY3MTQtNDQyZi1hNDE2LWM4MjAxNWFhYzFmNSIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6Ik1hdXJvIE11Z25haW5pIiwicHJlZmVycmVkX3VzZXJuYW1lIjoidGhlbWF4eDc2IiwiZ2l2ZW5fbmFtZSI6Ik1hdXJvIiwiZmFtaWx5X25hbWUiOiJNdWduYWluaSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQU9oMTRHaUFxUzZRc00xaDNXV1BqMU15VUdlRFA2VnFaQlhtOTZ4cHhZLXNndyIsImVtYWlsIjoidGhlbWF4eDc2QGdtYWlsLmNvbSJ9.WMddlUQujlpmzW07Lrk50vOyWpiT1Tp_RsBWRbzyrQnu5EQQSCq1uGOuSf7Z3VZFv8fbnzWekMZRNzhngEddzOQHgAlsgRdqNI_-ucjmb_8SfR2I5PkYJLTG0jF-Urqi-GvfJtLr2B8dBDnMDO6FLFsg1e5qb-5HkV60eEtY2Wult1PGxlkD05w-K2w513IOMkVIl25ZxKbP61-Iu1qfV_q3QFvUHl_pdqL7uKC5bkl1lqTVeuCwrXrKubHnKc-UzpHtHp8XY0Iao7LdLtON7SODYhU8EkZ860ZlFTSCszmLUpSH4t_shSk9Fiqd8wBKAet5ngmyAPzKx9TT2FK65g";
URL avatarURL = new URL("https://accounts.dev.d4science.org/auth/realms/d4science/avatar-provider/");
byte[] avatarBytes = OpenIdConnectRESTHelper.getUserAvatar(avatarURL, accessTokenBearer);
// assertNotNull(avatarBytes);
// assertNotNull(avatarBytes);
}
// @Test
// @Test
public void getUMATokenWithBasicAuth()
throws MalformedURLException, OpenIdConnectRESTHelperException, InterruptedException {
URL tokenURL = new URL(
"https://accounts.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token");
JWTToken token = OpenIdConnectRESTHelper.queryUMAToken(tokenURL, "storage-manager-trigger",
"e2591a99-b694-4dbe-8f7b-9755a3db80af",
"/gcube", null);
System.out.println(token.getAccessTokenString());
}
// @Test
public void getExp() throws MalformedURLException, OpenIdConnectRESTHelperException, InterruptedException {
URL tokenURL = new URL("https://accounts.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token");
JWTToken token = OpenIdConnectRESTHelper.queryClientToken("lr62_portal", "28726d01-9f24-4ef4-a057-3d208d96aaa0",
URL tokenURL = new URL(
"https://accounts.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token");
JWTToken token = OpenIdConnectRESTHelper.queryClientToken("robcomp", "0fec31cb-23c3-44e2-9359-d6db6784b7d3",
tokenURL);
System.out.println(token.getExpAsDate());
System.out.println(token.getAzp());
Thread.sleep((token.getExp() * 1000 - System.currentTimeMillis() + 5000));
// Thread.sleep((token.getExp() * 1000 - System.currentTimeMillis() + 5000));
System.out.println(token.isExpired());
}
public static void main(String[] args) throws Exception {
RestHelperTest rht = new RestHelperTest();
// rht.getAvatar();
// rht.getAvatar();
try {
rht.getExp();
rht.getUMATokenWithBasicAuth();
} catch (OpenIdConnectRESTHelperException e) {
if (e.hasJSONPayload()) {
System.out.println("JSON response: " + e.getResponseString());