From 64c3f02996dde6198f9844cec33978c4ffe2d60f Mon Sep 17 00:00:00 2001 From: "lucio.lelii" Date: Mon, 24 May 2021 16:31:46 +0200 Subject: [PATCH] - use of AccessTokenProvider - use gcube-jackson instead of minimal-json for access token parsing [#21097] --- CHANGELOG.md | 6 ++ pom.xml | 24 +++-- .../request/RequestContextRetriever.java | 90 +++++++++---------- .../org/gcube/smartgears/utils/GcubeJwt.java | 76 ++++++++++++++++ 4 files changed, 140 insertions(+), 56 deletions(-) create mode 100644 src/main/java/org/gcube/smartgears/utils/GcubeJwt.java diff --git a/CHANGELOG.md b/CHANGELOG.md index ab89d6d..642f20b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm # Changelog for Common Smartgears +## [v3.1.0] - 2021-05-14 + +- use of AccessTokenProvider +- use gcube-jackson instead of minimal-json for access token parsing [#21097] + + ## [v3.0.2] - 2020-03-01 - check if response is already committed on error diff --git a/pom.xml b/pom.xml index 9a1f612..84b8c5b 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.gcube.core common-smartgears - 3.0.2 + 3.1.0 SmartGears @@ -19,7 +19,7 @@ org.gcube.distribution gcube-bom - 2.0.0 + 2.0.1 pom import @@ -41,11 +41,21 @@ + - com.eclipsesource.minimal-json - minimal-json - 0.9.4 + org.gcube.common + gcube-jackson-databind + + org.gcube.common + gcube-jackson-annotations + + + org.gcube.common + gcube-jackson-core + + + org.gcube.common @@ -189,7 +199,7 @@ maven-jar-plugin - + default-jar @@ -233,7 +243,7 @@ org.apache.maven.plugins maven-surefire-plugin - + diff --git a/src/main/java/org/gcube/smartgears/handlers/application/request/RequestContextRetriever.java b/src/main/java/org/gcube/smartgears/handlers/application/request/RequestContextRetriever.java index 92af912..52af00e 100644 --- a/src/main/java/org/gcube/smartgears/handlers/application/request/RequestContextRetriever.java +++ b/src/main/java/org/gcube/smartgears/handlers/application/request/RequestContextRetriever.java @@ -6,19 +6,16 @@ import static org.gcube.smartgears.Constants.token_header; import static org.gcube.smartgears.handlers.application.request.RequestError.internal_server_error; import static org.gcube.smartgears.handlers.application.request.RequestError.invalid_request_error; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Base64; -import java.util.List; import javax.xml.bind.annotation.XmlRootElement; +import org.gcube.com.fasterxml.jackson.databind.ObjectMapper; import org.gcube.common.authorization.client.exceptions.ObjectNotFound; import org.gcube.common.authorization.library.AuthorizationEntry; +import org.gcube.common.authorization.library.provider.AccessTokenProvider; import org.gcube.common.authorization.library.provider.AuthorizationProvider; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; -import org.gcube.common.authorization.library.provider.UmaJWTProvider; import org.gcube.common.authorization.library.provider.UserInfo; import org.gcube.common.authorization.library.utils.Caller; import org.gcube.common.scope.api.ScopeProvider; @@ -27,13 +24,10 @@ import org.gcube.smartgears.Constants; import org.gcube.smartgears.handlers.application.RequestEvent; import org.gcube.smartgears.handlers.application.RequestHandler; import org.gcube.smartgears.handlers.application.ResponseEvent; +import org.gcube.smartgears.utils.GcubeJwt; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.eclipsesource.json.Json; -import com.eclipsesource.json.JsonArray; -import com.eclipsesource.json.JsonObject; - @XmlRootElement(name = Constants.request_context_retriever) public class RequestContextRetriever extends RequestHandler { @@ -55,46 +49,50 @@ public class RequestContextRetriever extends RequestHandler { String authHeader = call.request().getHeader(Constants.authorization_header); - log.trace("auth header is {}",authHeader); + log.trace("authorization header is {}",authHeader); + log.trace("token header is {}",token); + log.trace("scope header is {}",scope); - String umaToken = null; + String retrievedUser = null; + String accessToken = null; if (authHeader!=null && !authHeader.isEmpty()) { if (authHeader.startsWith(BEARER_AUTH_PREFIX)) - umaToken = authHeader.substring(BEARER_AUTH_PREFIX.length()).trim(); + accessToken = authHeader.substring(BEARER_AUTH_PREFIX.length()).trim(); else if (token==null && authHeader.startsWith(BASIC_AUTH_PREFIX)) { String basicAuthToken = authHeader.substring(BASIC_AUTH_PREFIX.length()).trim(); String decodedAuth = new String(Base64.getDecoder().decode(basicAuthToken.getBytes())); - token = decodedAuth.split(":")[1]; + String[] splitAuth = decodedAuth.split(":"); + token = splitAuth[1]; + retrievedUser = splitAuth[0]; } } - - - - - //Gives priority to the token - if (umaToken!=null) { - this.retreiveAndSetInfoUmaToken(umaToken, token, call); + + //Gives priority to the umaToken + if (accessToken!=null) { + this.retreiveAndSetInfoUmaToken(accessToken, token, call); } else if (token!=null) - this.retreiveAndSetInfoGcubeToken(token, call); + this.retreiveAndSetInfoGcubeToken(token, retrievedUser, call); else if (scope!=null) ScopeProvider.instance.set(scope); - + } @Override public void handleResponse(ResponseEvent e) { SecurityTokenProvider.instance.reset(); AuthorizationProvider.instance.reset(); - UmaJWTProvider.instance.reset(); + AccessTokenProvider.instance.reset(); ScopeProvider.instance.reset(); log.debug("resetting all the Thread local for this call."); } - private void retreiveAndSetInfoGcubeToken(String token, RequestEvent call){ + private void retreiveAndSetInfoGcubeToken(String token, String retrievedUser, RequestEvent call){ log.trace("retrieving context using token {} ", token); AuthorizationEntry authEntry = null; try{ authEntry = authorizationService().get(token); + if (retrievedUser != null && authEntry.getClientInfo().getId().equals(retrievedUser)) + throw new Exception("user and token owner are not the same"); }catch(ObjectNotFound onf){ log.warn("rejecting call to {}, invalid token {}",call.context().name(),token); invalid_request_error.fire(call.context().name()+" invalid token : "+token); @@ -109,50 +107,44 @@ public class RequestContextRetriever extends RequestHandler { log.info("retrieved request authorization info {} in scope {} ", AuthorizationProvider.instance.get(), authEntry.getContext()); } - private void retreiveAndSetInfoUmaToken(String umaToken, String gcubeToken, RequestEvent call){ + private void retreiveAndSetInfoUmaToken(String accessToken, String gcubeToken, RequestEvent call){ log.debug("using UMA token for authorization"); - log.trace("retrieving context using uma token {} ", umaToken); + log.trace("retrieving context using uma token {} ", accessToken); - UmaJWTProvider.instance.set(umaToken); + AccessTokenProvider.instance.set(accessToken); SecurityTokenProvider.instance.set(gcubeToken); - parseUmaTokenAndSet(umaToken); + parseAccessTokenAndSet(accessToken); log.info("retrieved request authorization info {} in scope {} ", AuthorizationProvider.instance.get(), ScopeProvider.instance.get()); } - private void parseUmaTokenAndSet(String umaToken) { + private void parseAccessTokenAndSet(String umaToken) { String realUmaTokenEncoded = umaToken.split("\\.")[1]; String realUmaToken = new String(Base64.getDecoder().decode(realUmaTokenEncoded.getBytes())); + + ObjectMapper mapper = new ObjectMapper(); - JsonObject object = Json.parse(realUmaToken).asObject(); - String username = object.get("preferred_username").asString(); - String scope = object.getString("aud", null); - - log.trace("token related context is {}", scope); - - JsonObject resource = object.get("resource_access").asObject(); - - log.trace("resource access is {}", resource.toString()); - - JsonObject scopeObject = resource.get(scope).asObject(); + GcubeJwt jwt = null; + try { + jwt = mapper.readValue(realUmaToken, GcubeJwt.class); + }catch(Exception e){ + log.error("error decoding uma token",e); + internal_server_error.fire("error parsing access token"); + } + ScopeBean scopeBean = null; try { - String decodedName = URLDecoder.decode(scope, StandardCharsets.UTF_8.toString()); - scopeBean = new ScopeBean(decodedName); + scopeBean = new ScopeBean(jwt.getContext()); }catch(Exception e){ log.error("error decoding uma token",e); - internal_server_error.fire("error contacting authorization service"); + internal_server_error.fire("invalid context in access token"); } - - JsonArray roles = scopeObject.get("roles").asArray(); - - List userRoles = new ArrayList(); - roles.forEach((e)->userRoles.add(e.asString())); - AuthorizationProvider.instance.set(new Caller(new UserInfo(username, userRoles), "token")); + AuthorizationProvider.instance.set(new Caller(new UserInfo(jwt.getUsername(), jwt.getRoles(), jwt.getEmail(), jwt.getFirstName(), jwt.getLastName()), "token")); + ScopeProvider.instance.set(scopeBean.toString()); } diff --git a/src/main/java/org/gcube/smartgears/utils/GcubeJwt.java b/src/main/java/org/gcube/smartgears/utils/GcubeJwt.java new file mode 100644 index 0000000..524d2b9 --- /dev/null +++ b/src/main/java/org/gcube/smartgears/utils/GcubeJwt.java @@ -0,0 +1,76 @@ +package org.gcube.smartgears.utils; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.gcube.com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import org.gcube.com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class GcubeJwt { + + @JsonProperty("aud") + private String context; + + @JsonProperty("resource_access") + private Map contextAccess = new HashMap<>(); + + @JsonProperty("preferred_username") + private String username; + + @JsonProperty("given_name") + private String firstName; + + @JsonProperty("family_name") + private String lastName; + + @JsonProperty("email") + private String email; + + public List getRoles(){ + return contextAccess.get(this.context).roles; + } + + public String getContext() { + try { + return URLDecoder.decode(context, StandardCharsets.UTF_8.toString()); + }catch (UnsupportedEncodingException e) { + return context; + } + } + + public String getUsername() { + return username; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public String getEmail() { + return email; + } + + @Override + public String toString() { + return "GcubeJwt [context=" + getContext() + ", roles=" + getRoles() + ", username=" + username + + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email + "]"; + } + + public static class Roles { + + @JsonProperty("roles") + List roles = new ArrayList<>(); + + } + +}