Backporting fixes
This commit is contained in:
parent
464515262f
commit
7ac7682818
|
@ -2,7 +2,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||||
|
|
||||||
# Changelog for Authorization utils
|
# Changelog for Authorization utils
|
||||||
|
|
||||||
|
|
||||||
|
## [v1.0.1-SNAPSHOT]
|
||||||
|
|
||||||
|
- Added support for clientID [#21903]
|
||||||
|
|
||||||
|
|
||||||
## [v1.0.0]
|
## [v1.0.0]
|
||||||
|
|
||||||
- First Release
|
- First Release
|
||||||
|
|
||||||
|
|
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
<groupId>org.gcube.common</groupId>
|
<groupId>org.gcube.common</groupId>
|
||||||
<artifactId>authorization-utils</artifactId>
|
<artifactId>authorization-utils</artifactId>
|
||||||
<version>1.0.0</version>
|
<version>1.0.1-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|
|
@ -2,6 +2,9 @@ package org.gcube.common.authorization.utils.clientid;
|
||||||
|
|
||||||
import org.gcube.common.authorization.utils.secret.Secret;
|
import org.gcube.common.authorization.utils.secret.Secret;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
public interface RenewalProvider {
|
public interface RenewalProvider {
|
||||||
|
|
||||||
public Secret renew() throws Exception;
|
public Secret renew() throws Exception;
|
||||||
|
|
|
@ -105,6 +105,7 @@ public class GCubeSecret extends Secret {
|
||||||
GCubeUser gCubeUser = new GCubeUser();
|
GCubeUser gCubeUser = new GCubeUser();
|
||||||
gCubeUser.setRoles(new HashSet<>(clientInfo.getRoles()));
|
gCubeUser.setRoles(new HashSet<>(clientInfo.getRoles()));
|
||||||
gCubeUser.setUsername(clientInfo.getId());
|
gCubeUser.setUsername(clientInfo.getId());
|
||||||
|
gCubeUser.setApplication(true);
|
||||||
user = gCubeUser;
|
user = gCubeUser;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
package org.gcube.common.authorization.utils.secret;
|
package org.gcube.common.authorization.utils.secret;
|
||||||
|
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
|
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
@ -19,6 +22,7 @@ import org.gcube.common.authorization.utils.user.KeycloakUser;
|
||||||
import org.gcube.common.authorization.utils.user.User;
|
import org.gcube.common.authorization.utils.user.User;
|
||||||
import org.gcube.common.keycloak.KeycloakClientFactory;
|
import org.gcube.common.keycloak.KeycloakClientFactory;
|
||||||
import org.gcube.common.keycloak.model.AccessToken;
|
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.ModelUtils;
|
||||||
import org.gcube.common.keycloak.model.RefreshToken;
|
import org.gcube.common.keycloak.model.RefreshToken;
|
||||||
import org.gcube.common.keycloak.model.TokenResponse;
|
import org.gcube.common.keycloak.model.TokenResponse;
|
||||||
|
@ -44,6 +48,10 @@ public class JWTSecret extends Secret {
|
||||||
protected AccessToken accessToken;
|
protected AccessToken accessToken;
|
||||||
protected TokenResponse tokenResponse;
|
protected TokenResponse tokenResponse;
|
||||||
protected RenewalProvider renewalProvider;
|
protected RenewalProvider renewalProvider;
|
||||||
|
protected Set<String> roles;
|
||||||
|
protected ClientInfo clientInfo;
|
||||||
|
protected Caller caller;
|
||||||
|
protected String context;
|
||||||
|
|
||||||
public JWTSecret(String token) {
|
public JWTSecret(String token) {
|
||||||
super(10, token);
|
super(10, token);
|
||||||
|
@ -91,7 +99,7 @@ public class JWTSecret extends Secret {
|
||||||
AccessTokenProvider.instance.reset();
|
AccessTokenProvider.instance.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AccessToken getAccessToken() throws Exception {
|
protected AccessToken getAccessToken() {
|
||||||
if(accessToken==null) {
|
if(accessToken==null) {
|
||||||
String realUmaTokenEncoded = token.split("\\.")[1];
|
String realUmaTokenEncoded = token.split("\\.")[1];
|
||||||
String realUmaToken = new String(Base64.getDecoder().decode(realUmaTokenEncoded.getBytes()));
|
String realUmaToken = new String(Base64.getDecoder().decode(realUmaTokenEncoded.getBytes()));
|
||||||
|
@ -100,47 +108,68 @@ public class JWTSecret extends Secret {
|
||||||
accessToken = mapper.readValue(realUmaToken, AccessToken.class);
|
accessToken = mapper.readValue(realUmaToken, AccessToken.class);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
logger.error("Error parsing JWT token",e);
|
logger.error("Error parsing JWT token",e);
|
||||||
throw new Exception("Error parsing JWT token", e);
|
throw new RuntimeException("Error parsing JWT token", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return accessToken;
|
return accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Set<String> getRoles() throws Exception{
|
||||||
|
if(roles == null) {
|
||||||
|
Map<String,Access> 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
|
@Override
|
||||||
public ClientInfo getClientInfo() throws Exception {
|
public ClientInfo getClientInfo() throws Exception {
|
||||||
getAccessToken();
|
if(clientInfo==null) {
|
||||||
List<String> roles = new ArrayList<>(accessToken.getRealmAccess().getRoles());
|
getAccessToken();
|
||||||
ClientInfo clientInfo = new UserInfo(accessToken.getPreferredUsername(), roles, accessToken.getEmail(), accessToken.getGivenName(), accessToken.getFamilyName());
|
List<String> roles = new ArrayList<>(getRoles());
|
||||||
|
clientInfo = new UserInfo(accessToken.getPreferredUsername(), roles, accessToken.getEmail(), accessToken.getGivenName(), accessToken.getFamilyName());
|
||||||
|
}
|
||||||
return clientInfo;
|
return clientInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Caller getCaller() throws Exception {
|
public Caller getCaller() throws Exception {
|
||||||
Caller caller = new Caller(getClientInfo(), "token");
|
if(caller==null) {
|
||||||
|
caller = new Caller(getClientInfo(), "token");
|
||||||
|
}
|
||||||
return caller;
|
return caller;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getContext() throws Exception {
|
public String getContext() throws Exception {
|
||||||
String context = null;
|
if(context==null) {
|
||||||
String[] audience = getAccessToken().getAudience();
|
String[] audience = getAccessToken().getAudience();
|
||||||
for (String aud : audience) {
|
for (String aud : audience) {
|
||||||
if (aud != null && aud.compareTo("") != 0) {
|
if (aud != null && aud.compareTo("") != 0) {
|
||||||
try {
|
try {
|
||||||
context = URLDecoder.decode(context, StandardCharsets.UTF_8.toString());
|
String contextToBeValidated = URLDecoder.decode(aud, StandardCharsets.UTF_8.toString());
|
||||||
ScopeBean scopeBean = new ScopeBean(context);
|
ScopeBean scopeBean = new ScopeBean(contextToBeValidated);
|
||||||
return scopeBean.toString();
|
context = scopeBean.toString();
|
||||||
} catch (Exception e) {
|
return context;
|
||||||
logger.error("Invalid context name for audience {} in access token. Trying next one if any.", aud, e);
|
} catch (Exception e) {
|
||||||
|
logger.error("Invalid context name for audience {} in access token. Trying next one if any.", aud, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
throw new Exception("Invalid context in access token");
|
||||||
}
|
}
|
||||||
throw new Exception("Invalid context in access token");
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() throws Exception {
|
public String getUsername() throws Exception {
|
||||||
return accessToken.getPreferredUsername();
|
return getAccessToken().getPreferredUsername();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -164,7 +193,7 @@ public class JWTSecret extends Secret {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
return isExpired(accessToken);
|
return isExpired(getAccessToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -185,8 +214,9 @@ public class JWTSecret extends Secret {
|
||||||
if(user==null) {
|
if(user==null) {
|
||||||
try {
|
try {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
String accessTokenString = objectMapper.writeValueAsString(accessToken);
|
String accessTokenString = objectMapper.writeValueAsString(getAccessToken());
|
||||||
user = objectMapper.readValue(accessTokenString, KeycloakUser.class);
|
user = objectMapper.readValue(accessTokenString, KeycloakUser.class);
|
||||||
|
user.setRoles(getRoles());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@ package org.gcube.common.authorization.utils.secret;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
public class SecretUtility {
|
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 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}$";
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.gcube.common.authorization.utils.user;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -21,18 +22,6 @@ public class GCubeUser implements User {
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
protected Map<String, Object> additionalProperties;
|
protected Map<String, Object> additionalProperties;
|
||||||
|
|
||||||
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 = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonProperty("id")
|
@JsonProperty("id")
|
||||||
protected String username;
|
protected String username;
|
||||||
@JsonProperty("roles")
|
@JsonProperty("roles")
|
||||||
|
@ -52,6 +41,23 @@ public class GCubeUser implements User {
|
||||||
@JsonProperty("middle_name")
|
@JsonProperty("middle_name")
|
||||||
protected String middleName;
|
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
|
@Override
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return username;
|
return username;
|
||||||
|
@ -60,14 +66,24 @@ public class GCubeUser implements User {
|
||||||
public void setUsername(String username) {
|
public void setUsername(String username) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isApplication() {
|
||||||
|
return application;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApplication(boolean application) {
|
||||||
|
this.application = application;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<String> getRoles() {
|
public Collection<String> getRoles() {
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRoles(Set<String> roles) {
|
@Override
|
||||||
this.roles = roles;
|
public void setRoles(Collection<String> roles) {
|
||||||
|
this.roles = new HashSet<>(roles);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public String getGivenName() {
|
public String getGivenName() {
|
||||||
|
@ -115,4 +131,49 @@ public class GCubeUser implements User {
|
||||||
this.additionalProperties.put(key, 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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,21 +15,86 @@ public class KeycloakUser extends AccessToken implements User {
|
||||||
*/
|
*/
|
||||||
private static final long serialVersionUID = -7083648026885406300L;
|
private static final long serialVersionUID = -7083648026885406300L;
|
||||||
|
|
||||||
|
public static final String CLIENT_ID_PROPERTY = "clientId";
|
||||||
|
|
||||||
|
protected Collection<String> roles;
|
||||||
|
protected Boolean application;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return getId();
|
return getPreferredUsername();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isApplication() {
|
||||||
|
if(application==null) {
|
||||||
|
application = getOtherClaims().get(CLIENT_ID_PROPERTY)!=null;
|
||||||
|
}
|
||||||
|
return application;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public Collection<String> getRoles() {
|
public Collection<String> getRoles() {
|
||||||
return getRealmAccess().getRoles();
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@JsonIgnore
|
||||||
|
public void setRoles(Collection<String> roles) {
|
||||||
|
this.roles = roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAbout() {
|
public String getAbout() {
|
||||||
return "";
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,30 @@ public interface User {
|
||||||
|
|
||||||
public String getUsername();
|
public String getUsername();
|
||||||
|
|
||||||
|
public boolean isApplication();
|
||||||
|
|
||||||
public Collection<String> getRoles();
|
public Collection<String> getRoles();
|
||||||
|
|
||||||
|
public void setRoles(Collection<String> roles);
|
||||||
|
|
||||||
public String getGivenName();
|
public String getGivenName();
|
||||||
|
|
||||||
public String getFamilyName();
|
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 getEmail();
|
||||||
|
|
||||||
public String getAbout();
|
public String getAbout();
|
||||||
|
|
Loading…
Reference in New Issue