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
|
||||
|
||||
|
||||
## [v1.0.1-SNAPSHOT]
|
||||
|
||||
- Added support for clientID [#21903]
|
||||
|
||||
|
||||
## [v1.0.0]
|
||||
|
||||
- First Release
|
||||
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
|||
|
||||
<groupId>org.gcube.common</groupId>
|
||||
<artifactId>authorization-utils</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<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;
|
||||
|
||||
/**
|
||||
* @author Luca Frosini (ISTI - CNR)
|
||||
*/
|
||||
public interface RenewalProvider {
|
||||
|
||||
public Secret renew() throws Exception;
|
||||
|
|
|
@ -105,6 +105,7 @@ public class GCubeSecret extends Secret {
|
|||
GCubeUser gCubeUser = new GCubeUser();
|
||||
gCubeUser.setRoles(new HashSet<>(clientInfo.getRoles()));
|
||||
gCubeUser.setUsername(clientInfo.getId());
|
||||
gCubeUser.setApplication(true);
|
||||
user = gCubeUser;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
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.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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.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;
|
||||
|
@ -44,6 +48,10 @@ public class JWTSecret extends Secret {
|
|||
protected AccessToken accessToken;
|
||||
protected TokenResponse tokenResponse;
|
||||
protected RenewalProvider renewalProvider;
|
||||
protected Set<String> roles;
|
||||
protected ClientInfo clientInfo;
|
||||
protected Caller caller;
|
||||
protected String context;
|
||||
|
||||
public JWTSecret(String token) {
|
||||
super(10, token);
|
||||
|
@ -91,7 +99,7 @@ public class JWTSecret extends Secret {
|
|||
AccessTokenProvider.instance.reset();
|
||||
}
|
||||
|
||||
protected AccessToken getAccessToken() throws Exception {
|
||||
protected AccessToken getAccessToken() {
|
||||
if(accessToken==null) {
|
||||
String realUmaTokenEncoded = token.split("\\.")[1];
|
||||
String realUmaToken = new String(Base64.getDecoder().decode(realUmaTokenEncoded.getBytes()));
|
||||
|
@ -100,36 +108,55 @@ public class JWTSecret extends Secret {
|
|||
accessToken = mapper.readValue(realUmaToken, AccessToken.class);
|
||||
}catch(Exception 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;
|
||||
}
|
||||
|
||||
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
|
||||
public ClientInfo getClientInfo() throws Exception {
|
||||
if(clientInfo==null) {
|
||||
getAccessToken();
|
||||
List<String> roles = new ArrayList<>(accessToken.getRealmAccess().getRoles());
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Caller getCaller() throws Exception {
|
||||
Caller caller = new Caller(getClientInfo(), "token");
|
||||
if(caller==null) {
|
||||
caller = new Caller(getClientInfo(), "token");
|
||||
}
|
||||
return caller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContext() throws Exception {
|
||||
String context = null;
|
||||
if(context==null) {
|
||||
String[] audience = getAccessToken().getAudience();
|
||||
for (String aud : audience) {
|
||||
if (aud != null && aud.compareTo("") != 0) {
|
||||
try {
|
||||
context = URLDecoder.decode(context, StandardCharsets.UTF_8.toString());
|
||||
ScopeBean scopeBean = new ScopeBean(context);
|
||||
return scopeBean.toString();
|
||||
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);
|
||||
}
|
||||
|
@ -137,10 +164,12 @@ public class JWTSecret extends Secret {
|
|||
}
|
||||
throw new Exception("Invalid context in access token");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() throws Exception {
|
||||
return accessToken.getPreferredUsername();
|
||||
return getAccessToken().getPreferredUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -164,7 +193,7 @@ public class JWTSecret extends Secret {
|
|||
|
||||
@Override
|
||||
public boolean isExpired() {
|
||||
return isExpired(accessToken);
|
||||
return isExpired(getAccessToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -185,8 +214,9 @@ public class JWTSecret extends Secret {
|
|||
if(user==null) {
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String accessTokenString = objectMapper.writeValueAsString(accessToken);
|
||||
String accessTokenString = objectMapper.writeValueAsString(getAccessToken());
|
||||
user = objectMapper.readValue(accessTokenString, KeycloakUser.class);
|
||||
user.setRoles(getRoles());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ 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}$";
|
||||
|
|
|
@ -2,6 +2,7 @@ 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;
|
||||
|
||||
|
@ -21,18 +22,6 @@ public class GCubeUser implements User {
|
|||
@JsonIgnore
|
||||
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")
|
||||
protected String username;
|
||||
@JsonProperty("roles")
|
||||
|
@ -52,6 +41,23 @@ public class GCubeUser implements User {
|
|||
@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;
|
||||
|
@ -61,13 +67,23 @@ public class GCubeUser implements User {
|
|||
this.username = username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplication() {
|
||||
return application;
|
||||
}
|
||||
|
||||
public void setApplication(boolean application) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Set<String> roles) {
|
||||
this.roles = roles;
|
||||
@Override
|
||||
public void setRoles(Collection<String> roles) {
|
||||
this.roles = new HashSet<>(roles);
|
||||
}
|
||||
@Override
|
||||
public String getGivenName() {
|
||||
|
@ -115,4 +131,49 @@ public class GCubeUser implements User {
|
|||
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,16 +15,35 @@ public class KeycloakUser extends AccessToken implements User {
|
|||
*/
|
||||
private static final long serialVersionUID = -7083648026885406300L;
|
||||
|
||||
public static final String CLIENT_ID_PROPERTY = "clientId";
|
||||
|
||||
protected Collection<String> roles;
|
||||
protected Boolean application;
|
||||
|
||||
@Override
|
||||
@JsonIgnore
|
||||
public String getUsername() {
|
||||
return getId();
|
||||
return getPreferredUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplication() {
|
||||
if(application==null) {
|
||||
application = getOtherClaims().get(CLIENT_ID_PROPERTY)!=null;
|
||||
}
|
||||
return application;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonIgnore
|
||||
public Collection<String> getRoles() {
|
||||
return getRealmAccess().getRoles();
|
||||
return roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonIgnore
|
||||
public void setRoles(Collection<String> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -32,4 +51,50 @@ public class KeycloakUser extends AccessToken implements User {
|
|||
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 boolean isApplication();
|
||||
|
||||
public Collection<String> getRoles();
|
||||
|
||||
public void setRoles(Collection<String> 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();
|
||||
|
|
Loading…
Reference in New Issue