diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a8d785..72a0590 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,11 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [v3.0.0-SNAPSHOT] - [2022-05-06]
+
+- merge AuthorizationUtils
+- removed AccessTokenProvider, AuthorizationProvider and SecurityTokenProvider
+
## [v2.5.0] - [2022-04-20]
- Deprecated AccessTokenProvider, AuthorizationProvider and SecurityTokenProvider [#22871]
diff --git a/pom.xml b/pom.xml
index c5e8b6a..4470b2f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,41 +1,58 @@
-
- 4.0.0
- org.gcube.common
- common-authorization
- 2.5.0
- authorization service common library
-
- maven-parent
- org.gcube.tools
- 1.1.0
-
-
-
- scm:git:https://code-repo.d4science.org/gCubeSystem/common-authorization.git
- scm:git:https://code-repo.d4science.org/gCubeSystem/common-authorization.git
- https://code-repo.d4science.org/gCubeSystem/common-authorization
-
+
+ 4.0.0
+ org.gcube.common
+ common-authorization
+ 3.0.0-SNAPSHOT
+ authorization service common library
-
-
- org.gcube.core
- common-scope
- [1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT)
-
-
- junit
- junit
- 4.11
- test
-
-
-
- org.slf4j
- slf4j-api
- 1.7.5
-
-
+
+ maven-parent
+ org.gcube.tools
+ 1.1.0
+
-
+
+ scm:git:https://code-repo.d4science.org/gCubeSystem/common-authorization.git
+ scm:git:https://code-repo.d4science.org/gCubeSystem/common-authorization.git
+ https://code-repo.d4science.org/gCubeSystem/common-authorization
+
+
+
+
+ org.gcube.common
+ gcube-jackson-core
+ 2.8.11
+
+
+ org.gcube.common
+ gcube-jackson-databind
+ 2.8.11
+
+
+ org.gcube.common
+ keycloak-client
+ [1.0.0,2.0.0-SNAPSHOT)
+
+
+ org.gcube.core
+ common-scope
+ [1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT)
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.5
+
+
+
+
diff --git a/src/main/java/org/gcube/common/authorization/library/provider/AccessTokenProvider.java b/src/main/java/org/gcube/common/authorization/library/provider/AccessTokenProvider.java
deleted file mode 100644
index e118d33..0000000
--- a/src/main/java/org/gcube/common/authorization/library/provider/AccessTokenProvider.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.gcube.common.authorization.library.provider;
-
-@Deprecated
-public class AccessTokenProvider {
-
- public static AccessTokenProvider instance = new AccessTokenProvider();
-
- private static final InheritableThreadLocal threadToken = new InheritableThreadLocal() {
-
- @Override
- protected String initialValue() {
- return null;
- }
-
- };
-
- private AccessTokenProvider() {
- }
-
- public String get() {
- return threadToken.get();
- }
-
- public void set(String jwt) {
- threadToken.set(jwt);
- }
-
- public void reset() {
- threadToken.remove();
- }
-}
diff --git a/src/main/java/org/gcube/common/authorization/library/provider/AuthorizationProvider.java b/src/main/java/org/gcube/common/authorization/library/provider/AuthorizationProvider.java
deleted file mode 100644
index eed2fe2..0000000
--- a/src/main/java/org/gcube/common/authorization/library/provider/AuthorizationProvider.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.gcube.common.authorization.library.provider;
-
-import org.gcube.common.authorization.library.utils.Caller;
-
-@Deprecated
-public class AuthorizationProvider {
-
- public static AuthorizationProvider instance = new AuthorizationProvider();
-
- // Thread local variable containing each thread's ID
- private static final InheritableThreadLocal threadAuth =
- new InheritableThreadLocal() {
-
- @Override protected Caller initialValue() {
- return null;
- }
-
- };
-
- private AuthorizationProvider(){}
-
- public Caller get(){
- Caller info = threadAuth.get();
- return info;
- }
-
- public void set(Caller info){
- threadAuth.set(info);
- }
-
- public void reset(){
- threadAuth.remove();
- }
-
-}
diff --git a/src/main/java/org/gcube/common/authorization/library/provider/SecurityTokenProvider.java b/src/main/java/org/gcube/common/authorization/library/provider/SecurityTokenProvider.java
deleted file mode 100644
index c80d323..0000000
--- a/src/main/java/org/gcube/common/authorization/library/provider/SecurityTokenProvider.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.gcube.common.authorization.library.provider;
-
-@Deprecated
-public class SecurityTokenProvider {
-
- public static SecurityTokenProvider instance = new SecurityTokenProvider();
-
- //private static Logger logger = LoggerFactory.getLogger(SecurityTokenProvider.class);
-
- // Thread local variable containing each thread's ID
- private static final InheritableThreadLocal threadToken =
- new InheritableThreadLocal() {
-
- @Override protected String initialValue() {
- return null;
- }
-
- };
-
- private SecurityTokenProvider(){}
-
- public String get(){
- return threadToken.get();
- }
-
- public void set(String authorizationToken){
- threadToken.set(authorizationToken);
- }
-
- public void reset(){
- threadToken.remove();
- }
-}
diff --git a/src/main/java/org/gcube/common/authorization/utils/clientid/ClientIDManager.java b/src/main/java/org/gcube/common/authorization/utils/clientid/ClientIDManager.java
new file mode 100644
index 0000000..81b92f5
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/clientid/ClientIDManager.java
@@ -0,0 +1,48 @@
+package org.gcube.common.authorization.utils.clientid;
+
+import org.gcube.common.authorization.utils.secret.JWTSecret;
+import org.gcube.common.authorization.utils.secret.Secret;
+import org.gcube.common.keycloak.KeycloakClientFactory;
+import org.gcube.common.keycloak.model.TokenResponse;
+
+/**
+ * @author Luca Frosini (ISTI - CNR)
+ */
+public class ClientIDManager implements RenewalProvider {
+
+ protected final String clientID;
+ protected final String clientSecret;
+
+ public ClientIDManager(String clientID, String clientSecret) {
+ this.clientID = clientID;
+ this.clientSecret = clientSecret;
+ }
+
+ public Secret getSecret() throws Exception {
+ TokenResponse tokenResponse = KeycloakClientFactory.newInstance().queryUMAToken(clientID, clientSecret, null);
+
+ JWTSecret jwtSecret = new JWTSecret(tokenResponse.getAccessToken());
+ jwtSecret.setRenewalProvider(this);
+
+ jwtSecret.setTokenResponse(tokenResponse);
+
+ return jwtSecret;
+ }
+
+ public Secret getSecret(String context) throws Exception {
+ TokenResponse tokenResponse = KeycloakClientFactory.newInstance().queryUMAToken(clientID, clientSecret, context, null);
+
+ JWTSecret jwtSecret = new JWTSecret(tokenResponse.getAccessToken());
+ jwtSecret.setRenewalProvider(this);
+
+ jwtSecret.setTokenResponse(tokenResponse);
+
+ return jwtSecret;
+ }
+
+ @Override
+ public Secret renew() throws Exception {
+ return getSecret();
+ }
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/utils/clientid/RenewalProvider.java b/src/main/java/org/gcube/common/authorization/utils/clientid/RenewalProvider.java
new file mode 100644
index 0000000..003b29f
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/clientid/RenewalProvider.java
@@ -0,0 +1,11 @@
+package org.gcube.common.authorization.utils.clientid;
+
+import org.gcube.common.authorization.utils.secret.Secret;
+
+/**
+ * @author Luca Frosini (ISTI - CNR)
+ */
+public interface RenewalProvider {
+
+ public Secret renew() throws Exception;
+}
diff --git a/src/main/java/org/gcube/common/authorization/utils/manager/SecretHolder.java b/src/main/java/org/gcube/common/authorization/utils/manager/SecretHolder.java
new file mode 100644
index 0000000..a41a821
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/manager/SecretHolder.java
@@ -0,0 +1,89 @@
+package org.gcube.common.authorization.utils.manager;
+
+import java.util.Collection;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.gcube.common.authorization.utils.secret.Secret;
+import org.gcube.common.authorization.utils.user.User;
+import org.gcube.common.scope.api.ScopeProvider;
+
+/**
+ * @author Luca Frosini (ISTI - CNR)
+ */
+public class SecretHolder {
+
+ private SortedSet secrets;
+
+ public SecretHolder() {
+ this.secrets = new TreeSet();
+ }
+
+ public SecretHolder(Secret secret) {
+ this.secrets = new TreeSet();
+ addSecret(secret);
+ }
+
+ public SecretHolder(Collection secrets) {
+ this.secrets = new TreeSet(secrets);
+ }
+
+ public void addSecret(Secret secret) {
+ if(secret!=null) {
+ secrets.add(secret);
+ }
+ }
+
+ public void addSecrets(Collection secrets) {
+ for(Secret secret : secrets){
+ addSecret(secret);
+ }
+ }
+
+ public void set() throws Exception {
+ for(Secret secret : secrets) {
+ secret.set();
+ }
+ }
+
+ public SortedSet getSecrets() {
+ return secrets;
+ }
+
+ public User getUser() {
+ for(Secret secret : secrets) {
+ try {
+ return secret.getUser();
+ }catch (Exception e) {
+ // trying the next one
+ }
+ }
+ return null;
+ }
+
+ public String getContext() {
+ for(Secret secret : secrets) {
+ try {
+ return secret.getContext();
+ }catch (Exception e) {
+ // trying the next one
+ }
+ }
+ return ScopeProvider.instance.get();
+ }
+
+ public void reset() {
+ boolean first = true;
+ for(Secret secret : secrets) {
+ try {
+ secret.reset();
+ }catch (Exception e) {
+ // trying the next one
+ }
+ }
+ if(first) {
+ ScopeProvider.instance.reset();
+ }
+ }
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/utils/manager/SecretManager.java b/src/main/java/org/gcube/common/authorization/utils/manager/SecretManager.java
new file mode 100644
index 0000000..7ea3dd9
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/manager/SecretManager.java
@@ -0,0 +1,116 @@
+package org.gcube.common.authorization.utils.manager;
+
+import java.util.Collection;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.gcube.common.authorization.utils.provider.SecretProvider;
+import org.gcube.common.authorization.utils.secret.Secret;
+import org.gcube.common.authorization.utils.secret.SecretUtility;
+import org.gcube.common.authorization.utils.user.User;
+
+/**
+ * @author Luca Frosini (ISTI - CNR)
+ */
+public class SecretManager {
+
+ private SecretHolder initialSecretHolder;
+ private SecretHolder currentSecretHolder;
+
+ public SecretManager() {
+ initialSecretHolder = new SecretHolder();
+ currentSecretHolder = initialSecretHolder;
+ }
+
+ public synchronized void addSecretViaProvider(SecretProvider secretProvider) {
+ if (currentSecretHolder != initialSecretHolder) {
+ throw new RuntimeException("You can't add a Secret in a session. You must terminate the session first.");
+ }
+ Secret secret = secretProvider.getSecret();
+ currentSecretHolder.addSecret(secret);
+ }
+
+ public synchronized void addSecret(Secret secret) {
+ if (currentSecretHolder != initialSecretHolder) {
+ throw new RuntimeException("You can't add a Secret in a session. You must terminate the session first.");
+ }
+ currentSecretHolder.addSecret(secret);
+ }
+
+ public synchronized void startSession(Secret secret) throws Exception {
+ if (currentSecretHolder != initialSecretHolder) {
+ throw new RuntimeException("You are already in a session. You must terminate the session first.");
+ }
+ initialSecretHolder.reset();
+ currentSecretHolder = new SecretHolder(secret);
+ currentSecretHolder.set();
+ }
+
+ public synchronized void startSession(Collection secrets) throws Exception {
+ if (currentSecretHolder != initialSecretHolder) {
+ throw new RuntimeException("You are already in a session. You must terminate the session first.");
+ }
+ initialSecretHolder.reset();
+ currentSecretHolder = new SecretHolder(secrets);
+ currentSecretHolder.set();
+ }
+
+ public synchronized void startSession(SecretHolder secretHolder) throws Exception {
+ if (currentSecretHolder != initialSecretHolder) {
+ throw new RuntimeException("You are already in a session. You must terminate the session first.");
+ }
+ initialSecretHolder.reset();
+ currentSecretHolder = secretHolder;
+ currentSecretHolder.set();
+ }
+
+ public synchronized void endSession() {
+ if (currentSecretHolder != initialSecretHolder) {
+ currentSecretHolder.reset();
+ try {
+ initialSecretHolder.set();
+ }catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ currentSecretHolder = initialSecretHolder;
+ }
+ }
+
+ public synchronized void set() throws Exception {
+ if (currentSecretHolder != initialSecretHolder) {
+ throw new Exception("You are in a session. You must terminate the session first.");
+ }
+ currentSecretHolder.set();
+ }
+
+ public synchronized void reset() {
+ currentSecretHolder.reset();
+ if (initialSecretHolder != currentSecretHolder) {
+ initialSecretHolder.reset();
+ }
+ }
+
+ public synchronized String getContext() {
+ return currentSecretHolder.getContext();
+ }
+
+ public synchronized User getUser() {
+ return currentSecretHolder.getUser();
+ }
+
+ /**
+ * @return a copy of the current secret holder
+ * to avoid modification to the original
+ */
+ public synchronized SecretHolder getCurrentSecretHolder() {
+ SecretHolder secretHolder = new SecretHolder();
+ SortedSet secrets = new TreeSet<>();
+ SortedSet originalSecrets = currentSecretHolder.getSecrets();
+ for(Secret s : originalSecrets) {
+ Secret secret = SecretUtility.getSecretByTokenString(s.getToken());
+ secrets.add(secret);
+ }
+ secretHolder.addSecrets(secrets);
+ return secretHolder;
+ }
+}
diff --git a/src/main/java/org/gcube/common/authorization/utils/manager/SecretManagerProvider.java b/src/main/java/org/gcube/common/authorization/utils/manager/SecretManagerProvider.java
new file mode 100644
index 0000000..42cc309
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/manager/SecretManagerProvider.java
@@ -0,0 +1,39 @@
+package org.gcube.common.authorization.utils.manager;
+
+/**
+ * @author Luca Frosini (ISTI - CNR)
+ */
+public class SecretManagerProvider {
+
+ public static SecretManagerProvider instance = new SecretManagerProvider();
+
+ // Thread local variable containing each thread's ID
+ private static final InheritableThreadLocal thread = new InheritableThreadLocal() {
+
+ @Override
+ protected SecretManager initialValue() {
+ return null;
+ }
+
+ };
+
+ private SecretManagerProvider(){}
+
+ public SecretManager get(){
+ SecretManager secretManager = thread.get();
+ return secretManager;
+ }
+
+ public void set(SecretManager secretManager){
+ thread.set(secretManager);
+ }
+
+ public void reset(){
+ SecretManager secretManager = thread.get();
+ if(secretManager!=null) {
+ secretManager.reset();
+ }
+ thread.remove();
+ }
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/utils/provider/SecretProvider.java b/src/main/java/org/gcube/common/authorization/utils/provider/SecretProvider.java
new file mode 100644
index 0000000..c0cc8f0
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/provider/SecretProvider.java
@@ -0,0 +1,12 @@
+package org.gcube.common.authorization.utils.provider;
+
+import org.gcube.common.authorization.utils.secret.Secret;
+
+/**
+ * @author Luca Frosini (ISTI - CNR)
+ */
+public interface SecretProvider {
+
+ public Secret getSecret();
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/utils/secret/GCubeSecret.java b/src/main/java/org/gcube/common/authorization/utils/secret/GCubeSecret.java
new file mode 100644
index 0000000..464d9b9
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/secret/GCubeSecret.java
@@ -0,0 +1,103 @@
+package org.gcube.common.authorization.utils.secret;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.gcube.common.authorization.client.Constants;
+import org.gcube.common.authorization.library.AuthorizationEntry;
+import org.gcube.common.authorization.library.ClientType;
+import org.gcube.common.authorization.library.exception.AuthorizationException;
+import org.gcube.common.authorization.library.provider.ClientInfo;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.authorization.utils.user.GCubeUser;
+import org.gcube.common.authorization.utils.user.User;
+
+/**
+ * @author Luca Frosini (ISTI - CNR)
+ */
+public class GCubeSecret extends Secret {
+
+ public static final String GCUBE_TOKEN_REGEX = "^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}-[a-fA-F0-9]{8,9}){1}$";
+
+ protected AuthorizationEntry authorizationEntry;
+
+ @Override
+ protected void check(String token) throws AuthorizationException {
+ super.check(token);
+ if(!Pattern.matches(GCubeSecret.GCUBE_TOKEN_REGEX, token)) {
+ throw new AuthorizationException("The GUCBE token must comply with the regex " + GCUBE_TOKEN_REGEX);
+ }
+ }
+
+ public GCubeSecret(String token) {
+ super(20, token);
+ }
+
+ protected AuthorizationEntry getAuthorizationEntry() {
+ if(authorizationEntry==null) {
+ try {
+ authorizationEntry = Constants.authorizationService().get(token);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return authorizationEntry;
+ }
+
+ @Override
+ public ClientInfo getClientInfo() throws Exception {
+ return getAuthorizationEntry().getClientInfo();
+ }
+
+ @Override
+ public Caller getCaller() throws Exception {
+ ClientInfo clientInfo = getClientInfo();
+ String qualifier = authorizationEntry.getQualifier();
+ Caller caller = new Caller(clientInfo, qualifier);
+ return caller;
+ }
+
+ @Override
+ public String getContext() {
+ return getAuthorizationEntry().getContext();
+ }
+
+ @Override
+ public Map getHTTPAuthorizationHeaders() {
+ Map authorizationHeaders = new HashMap<>();
+ authorizationHeaders.put(org.gcube.common.authorization.client.Constants.TOKEN_HEADER_ENTRY, token);
+ return authorizationHeaders;
+ }
+
+ @Override
+ public boolean isExpired() {
+ return false;
+ }
+
+ @Override
+ public boolean isRefreshable() {
+ return false;
+ }
+
+ public User getUser() {
+ if(user==null) {
+ try {
+ ClientInfo clientInfo = getClientInfo();
+ ClientType clientType = clientInfo.getType();
+
+ GCubeUser gCubeUser = new GCubeUser();
+ gCubeUser.setRoles(new HashSet<>(clientInfo.getRoles()));
+ gCubeUser.setUsername(clientInfo.getId());
+ gCubeUser.setApplication(clientType!=ClientType.USER);
+ user = gCubeUser;
+ }
+ catch (Exception e) {
+ throw new RuntimeException();
+ }
+ }
+ return user;
+ }
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/utils/secret/JWTSecret.java b/src/main/java/org/gcube/common/authorization/utils/secret/JWTSecret.java
new file mode 100644
index 0000000..b77dd58
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/secret/JWTSecret.java
@@ -0,0 +1,220 @@
+package org.gcube.common.authorization.utils.secret;
+
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
+import org.gcube.common.authorization.library.provider.ClientInfo;
+import org.gcube.common.authorization.library.provider.ExternalServiceInfo;
+import org.gcube.common.authorization.library.provider.UserInfo;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.authorization.utils.clientid.RenewalProvider;
+import org.gcube.common.authorization.utils.user.KeycloakUser;
+import org.gcube.common.authorization.utils.user.User;
+import org.gcube.common.keycloak.KeycloakClientFactory;
+import org.gcube.common.keycloak.model.AccessToken;
+import org.gcube.common.keycloak.model.AccessToken.Access;
+import org.gcube.common.keycloak.model.ModelUtils;
+import org.gcube.common.keycloak.model.RefreshToken;
+import org.gcube.common.keycloak.model.TokenResponse;
+import org.gcube.common.keycloak.model.util.Time;
+import org.gcube.common.scope.impl.ScopeBean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Luca Frosini (ISTI - CNR)
+ */
+public class JWTSecret extends Secret {
+
+ private static final Logger logger = LoggerFactory.getLogger(JWTSecret.class);
+
+ /**
+ * The interval of time expressed in milliseconds used as guard to refresh the token before that it expires .
+ * TimeUnit has been used to in place of just
+ * using the number to have a clearer code
+ */
+ public static final long TOLERANCE = TimeUnit.MILLISECONDS.toMillis(200);
+
+ protected AccessToken accessToken;
+ protected TokenResponse tokenResponse;
+ protected RenewalProvider renewalProvider;
+ protected Set roles;
+ protected ClientInfo clientInfo;
+ protected Caller caller;
+ protected String context;
+
+ public JWTSecret(String token) {
+ super(10, token);
+ }
+
+ private String getTokenString() {
+ try {
+ boolean expired = false;
+ getAccessToken();
+
+ if(Time.currentTimeMillis()>=(accessToken.getExp()-TOLERANCE)) {
+ expired = true;
+ if(tokenResponse!=null) {
+ try {
+ KeycloakClientFactory.newInstance().refreshToken(getUsername(), tokenResponse);
+ expired = false;
+ }catch (Exception e) {
+ logger.warn("Unable to refresh the token with RefreshToken. Going to try to renew it if possible.", e);
+ }
+ }
+ }
+
+ if(expired && renewalProvider!=null) {
+ try {
+ JWTSecret renewed = (JWTSecret) renewalProvider.renew();
+ this.token = renewed.token;
+ this.accessToken = getAccessToken();
+ }catch (Exception e) {
+ logger.warn("Unable to renew the token with the RenewalProvider. I'll continue using the old token.", e);
+ }
+ }
+ }catch (Exception e) {
+ logger.error("Unexpected error in the procedure to evaluate/refresh the current token. I'll continue using the old token.", e);
+ }
+ return token;
+ }
+
+
+ protected AccessToken getAccessToken() {
+ if(accessToken==null) {
+ String realUmaTokenEncoded = token.split("\\.")[1];
+ String realUmaToken = new String(Base64.getDecoder().decode(realUmaTokenEncoded.getBytes()));
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ accessToken = mapper.readValue(realUmaToken, AccessToken.class);
+ }catch(Exception e){
+ logger.error("Error parsing JWT token",e);
+ throw new RuntimeException("Error parsing JWT token", e);
+ }
+ }
+ return accessToken;
+ }
+
+ protected Set getRoles() throws Exception{
+ if(roles == null) {
+ Map accesses = getAccessToken().getResourceAccess();
+ String context = getContext();
+ Access access = accesses.get(URLEncoder.encode(context, StandardCharsets.UTF_8.toString()));
+ if(access != null) {
+ roles = access.getRoles();
+ }else {
+ roles = new HashSet<>();
+ }
+ }
+ return roles;
+ }
+
+ @Override
+ public ClientInfo getClientInfo() throws Exception {
+ if(clientInfo==null) {
+ User user = getUser();
+ if(user.isApplication()) {
+ clientInfo = new ExternalServiceInfo(user.getUsername(), "unknown");
+ }else {
+ clientInfo = new UserInfo(user.getUsername(), new ArrayList<>(user.getRoles()), user.getEmail(), user.getGivenName(), user.getFamilyName());
+ }
+ }
+ return clientInfo;
+ }
+
+ @Override
+ public Caller getCaller() throws Exception {
+ if(caller==null) {
+ caller = new Caller(getClientInfo(), "token");
+ }
+ return caller;
+ }
+
+ @Override
+ public String getContext() {
+ if(context==null) {
+ String[] audience = getAccessToken().getAudience();
+ for (String aud : audience) {
+ if (aud != null && aud.compareTo("") != 0) {
+ try {
+ String contextToBeValidated = URLDecoder.decode(aud, StandardCharsets.UTF_8.toString());
+ ScopeBean scopeBean = new ScopeBean(contextToBeValidated);
+ context = scopeBean.toString();
+ return context;
+ } catch (Exception e) {
+ logger.error("Invalid context name for audience {} in access token. Trying next one if any.", aud, e);
+ }
+ }
+ }
+ throw new RuntimeException("Invalid context in access token");
+ }
+ return context;
+ }
+
+ @Override
+ public String getUsername() throws Exception {
+ return getAccessToken().getPreferredUsername();
+ }
+
+ @Override
+ public Map getHTTPAuthorizationHeaders() {
+ Map authorizationHeaders = new HashMap<>();
+ authorizationHeaders.put("Authorization", "Bearer " + getTokenString());
+ return authorizationHeaders;
+ }
+
+ public void setRenewalProvider(RenewalProvider renewalProvider) {
+ this.renewalProvider = renewalProvider;
+ }
+
+ public void setTokenResponse(TokenResponse tokenResponse) {
+ this.tokenResponse = tokenResponse;
+ }
+
+ protected boolean isExpired(AccessToken accessToken) {
+ return Time.currentTimeMillis()>accessToken.getExp();
+ }
+
+ @Override
+ public boolean isExpired() {
+ return isExpired(getAccessToken());
+ }
+
+ @Override
+ public boolean isRefreshable() {
+ if(tokenResponse!=null) {
+ try {
+ RefreshToken refreshToken = ModelUtils.getRefreshTokenFrom(tokenResponse);
+ return isExpired(refreshToken);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public User getUser() {
+ if(user==null) {
+ try {
+ ObjectMapper objectMapper = new ObjectMapper();
+ String accessTokenString = objectMapper.writeValueAsString(getAccessToken());
+ user = objectMapper.readValue(accessTokenString, KeycloakUser.class);
+ user.setRoles(getRoles());
+ } catch (Exception e) {
+ throw new RuntimeException();
+ }
+ }
+ return user;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/gcube/common/authorization/utils/secret/Secret.java b/src/main/java/org/gcube/common/authorization/utils/secret/Secret.java
new file mode 100644
index 0000000..c424a8d
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/secret/Secret.java
@@ -0,0 +1,106 @@
+package org.gcube.common.authorization.utils.secret;
+
+import java.util.Map;
+import java.util.Objects;
+
+import org.gcube.common.authorization.library.ClientType;
+import org.gcube.common.authorization.library.exception.AuthorizationException;
+import org.gcube.common.authorization.library.provider.ClientInfo;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.authorization.utils.user.User;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Luca Frosini (ISTI - CNR)
+ */
+public abstract class Secret implements Comparable {
+
+ protected static final Logger logger = LoggerFactory.getLogger(Secret.class);
+
+ protected int priority;
+ protected String token;
+ protected User user;
+
+ protected void check(String token) throws AuthorizationException {
+ if(token == null) {
+ throw new AuthorizationException("token cannot be null");
+ }
+ if(token.compareTo("")==0) {
+ throw new AuthorizationException("token cannot be an empty string");
+ }
+ }
+
+ protected Secret(int priority, String token) {
+ this.priority = priority;
+ check(token);
+ this.token = token;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void set() throws Exception {
+ ScopeProvider.instance.set(getContext());
+ }
+
+ public abstract ClientInfo getClientInfo() throws Exception;
+
+ public abstract Caller getCaller() throws Exception;
+
+ public abstract String getContext();
+
+ public String getUsername() throws Exception {
+ return getClientInfo().getId();
+ }
+
+ public boolean isApplication() throws Exception {
+ return getClientInfo().getType() == ClientType.EXTERNALSERVICE;
+ }
+
+ public abstract Map getHTTPAuthorizationHeaders();
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(priority, token);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Secret other = (Secret) obj;
+ return priority == other.priority && Objects.equals(token, other.token);
+ }
+
+ @Override
+ public int compareTo(Secret obj) {
+ if (this == obj) {
+ return 0;
+ }
+ if (obj == null) {
+ return priority;
+ }
+ if (getClass() != obj.getClass()) {
+ return priority;
+ }
+ return token.compareTo(obj.token);
+ }
+
+ public void reset() {
+ ScopeProvider.instance.reset();
+ }
+
+ public abstract boolean isExpired();
+
+ public abstract boolean isRefreshable();
+
+ public abstract User getUser();
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/utils/secret/SecretUtility.java b/src/main/java/org/gcube/common/authorization/utils/secret/SecretUtility.java
new file mode 100644
index 0000000..3f9672d
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/secret/SecretUtility.java
@@ -0,0 +1,20 @@
+package org.gcube.common.authorization.utils.secret;
+
+import java.util.regex.Pattern;
+
+/**
+ * @author Luca Frosini (ISTI - CNR)
+ */
+public class SecretUtility {
+
+ public static final String UUID_REGEX = "^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}-[a-fA-F0-9]{8,9}){1}$";
+
+ public static Secret getSecretByTokenString(String token) {
+ if(Pattern.matches(UUID_REGEX, token)) {
+ return new GCubeSecret(token);
+ }else {
+ return new JWTSecret(token);
+ }
+ }
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/utils/user/GCubeUser.java b/src/main/java/org/gcube/common/authorization/utils/user/GCubeUser.java
new file mode 100644
index 0000000..1339fe8
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/user/GCubeUser.java
@@ -0,0 +1,179 @@
+package org.gcube.common.authorization.utils.user;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.gcube.com.fasterxml.jackson.annotation.JsonAnyGetter;
+import org.gcube.com.fasterxml.jackson.annotation.JsonAnySetter;
+import org.gcube.com.fasterxml.jackson.annotation.JsonIgnore;
+import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * @author Luca Frosini (ISTI - CNR)
+ */
+public class GCubeUser implements User {
+
+ /**
+ * Used to allow to have any additional properties
+ */
+ @JsonIgnore
+ protected Map additionalProperties;
+
+ @JsonProperty("id")
+ protected String username;
+ @JsonProperty("roles")
+ protected Set roles;
+
+ @JsonProperty("given_name")
+ protected String givenName;
+ @JsonProperty("family_name")
+ protected String familyName;
+ @JsonProperty("email")
+ protected String eMail;
+ @JsonProperty("job_title")
+ protected String jobTitle;
+
+ @JsonProperty("picture")
+ protected String picture;
+ @JsonProperty("middle_name")
+ protected String middleName;
+
+ @JsonIgnore
+ protected boolean application;
+
+ public GCubeUser() {
+ this.additionalProperties = new HashMap<>();
+
+ // This info are not always present. Setting an empty string to avoid null
+ this.givenName = "";
+ this.familyName = "";
+ this.eMail = "";
+ this.jobTitle = "";
+ this.picture = "";
+ this.middleName = "";
+ this.application = false;
+ }
+
+
+ @Override
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ @Override
+ public boolean isApplication() {
+ return application;
+ }
+
+ public void setApplication(boolean application) {
+ this.application = application;
+ }
+
+ @Override
+ public Collection getRoles() {
+ return roles;
+ }
+
+ @Override
+ public void setRoles(Collection roles) {
+ this.roles = new HashSet<>(roles);
+ }
+ @Override
+ public String getGivenName() {
+ return givenName;
+ }
+
+ @Override
+ public String getFamilyName() {
+ return familyName;
+ }
+
+ @Override
+ public String getEmail() {
+ return eMail;
+ }
+
+ @Override
+ public String getAbout() {
+ return jobTitle;
+ }
+
+ public String getPicture() {
+ return picture;
+ }
+
+ public String getMiddleName() {
+ return middleName;
+ }
+
+ @JsonAnyGetter
+ public Map getAdditionalProperties() {
+ return additionalProperties;
+ }
+
+ public void setAdditionalProperties(Map additionalProperties) {
+ this.additionalProperties = additionalProperties;
+ }
+
+ public Object getAdditionalProperty(String key) {
+ return additionalProperties.get(key);
+ }
+
+ @JsonAnySetter
+ public void setAdditionalProperty(String key, Object value) {
+ this.additionalProperties.put(key, value);
+ }
+
+ @Override
+ public String getFullName() {
+ return getFullName(false);
+ }
+
+ @Override
+ public String getFullName(boolean nameSurname) {
+ if(isApplication()) {
+ return getUsername();
+ }
+
+ StringBuffer stringBuffer = new StringBuffer();
+ boolean found = false;
+ String surname = getFamilyName();
+ String name = getGivenName();
+
+ if(nameSurname) {
+ if(name!=null && name.trim().length()>0) {
+ stringBuffer.append(name.trim());
+ found = true;
+ }
+ if(surname!=null && surname.trim().length()>0) {
+ if(found) {
+ stringBuffer.append(" ");
+ }
+ stringBuffer.append(surname.trim());
+ found = true;
+ }
+ }else {
+ if(surname!=null && surname.trim().length()>0) {
+ stringBuffer.append(surname.trim());
+ found = true;
+ }
+ if(name!=null && name.trim().length()>0) {
+ if(found) {
+ stringBuffer.append(" ");
+ }
+ stringBuffer.append(name.trim());
+ found = true;
+ }
+ }
+
+ return stringBuffer.toString();
+ }
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/utils/user/KeycloakUser.java b/src/main/java/org/gcube/common/authorization/utils/user/KeycloakUser.java
new file mode 100644
index 0000000..e604cc5
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/user/KeycloakUser.java
@@ -0,0 +1,100 @@
+package org.gcube.common.authorization.utils.user;
+
+import java.util.Collection;
+
+import org.gcube.com.fasterxml.jackson.annotation.JsonIgnore;
+import org.gcube.common.keycloak.model.AccessToken;
+
+/**
+ * @author Luca Frosini (ISTI-CNR)
+ */
+public class KeycloakUser extends AccessToken implements User {
+
+ /**
+ * Generated Serial Version UID
+ */
+ private static final long serialVersionUID = -7083648026885406300L;
+
+ public static final String CLIENT_ID_PROPERTY = "clientId";
+
+ protected Collection roles;
+ protected Boolean application;
+
+ @Override
+ @JsonIgnore
+ public String getUsername() {
+ return getPreferredUsername();
+ }
+
+ @Override
+ public boolean isApplication() {
+ if(application==null) {
+ application = getOtherClaims().get(CLIENT_ID_PROPERTY)!=null;
+ }
+ return application;
+ }
+
+ @Override
+ @JsonIgnore
+ public Collection getRoles() {
+ return roles;
+ }
+
+ @Override
+ @JsonIgnore
+ public void setRoles(Collection roles) {
+ this.roles = roles;
+ }
+
+ @Override
+ public String getAbout() {
+ return "";
+ }
+
+ @Override
+ public String getFullName() {
+ return getFullName(false);
+ }
+
+ @Override
+ public String getFullName(boolean nameSurname) {
+ if(isApplication()) {
+ String clientID = (String) getOtherClaims().getOrDefault("clientId", getUsername());
+ return clientID;
+ }
+
+ StringBuffer stringBuffer = new StringBuffer();
+ boolean found = false;
+ String surname = getFamilyName();
+ String name = getGivenName();
+
+ if(nameSurname) {
+ if(name!=null && name.trim().length()>0) {
+ stringBuffer.append(name.trim());
+ found = true;
+ }
+ if(surname!=null && surname.trim().length()>0) {
+ if(found) {
+ stringBuffer.append(" ");
+ }
+ stringBuffer.append(surname.trim());
+ found = true;
+ }
+ }else {
+ if(surname!=null && surname.trim().length()>0) {
+ stringBuffer.append(surname.trim());
+ found = true;
+ }
+ if(name!=null && name.trim().length()>0) {
+ if(found) {
+ stringBuffer.append(" ");
+ }
+ stringBuffer.append(name.trim());
+ found = true;
+ }
+ }
+
+ return stringBuffer.toString();
+ }
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/utils/user/User.java b/src/main/java/org/gcube/common/authorization/utils/user/User.java
new file mode 100644
index 0000000..80b738f
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/utils/user/User.java
@@ -0,0 +1,40 @@
+package org.gcube.common.authorization.utils.user;
+
+import java.util.Collection;
+
+/**
+ * @author Luca Frosini (ISTI-CNR)
+ */
+public interface User {
+
+ public String getUsername();
+
+ public boolean isApplication();
+
+ public Collection getRoles();
+
+ public void setRoles(Collection roles);
+
+ public String getGivenName();
+
+ public String getFamilyName();
+
+ /**
+ * @return the full name in the form 'Surname Name' for a person
+ * or the application identifier for an application;
+ */
+ public String getFullName();
+
+ /**
+ * @param nameSurname when true the fullname will be formatted as 'Name Surname',
+ * when false the fullname will be formatted as 'Surname Name',
+ * @return the full name according to nameSurname boolean for a person
+ * or the application identifier for an application;
+ */
+ public String getFullName(boolean nameSurname);
+
+ public String getEmail();
+
+ public String getAbout();
+
+}