authorization-utils/src/main/java/org/gcube/common/authorization/utils/secret/JWTSecret.java

177 lines
5.1 KiB
Java

package org.gcube.common.authorization.utils.secret;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.common.authorization.library.provider.AccessTokenProvider;
import org.gcube.common.authorization.library.provider.ClientInfo;
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.RefreshToken;
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);
/**
*
*/
public static long TOLERANCE = 200;
protected AccessToken accessToken;
protected RefreshToken refreshToken;
protected RenewalProvider renewalProvider;
public JWTSecret(String token) {
super(10, token);
}
private String getTokenString() {
try {
boolean expired = false;
getAccessToken();
if(Time.currentTimeMillis()>=(accessToken.getExp()-TOLERANCE)) {
expired = true;
if(refreshToken!=null) {
ObjectMapper mapper = new ObjectMapper();
KeycloakClientFactory.newInstance().refreshToken(getUsername(), mapper.writeValueAsString(refreshToken));
expired = false;
}
}
if(expired && renewalProvider!=null) {
JWTSecret renewed = (JWTSecret) renewalProvider.renew();
this.token = renewed.token;
this.accessToken = getAccessToken();
}
}catch (Exception e) {
// TODO log
}
return token;
}
@Override
public void setToken() throws Exception {
AccessTokenProvider.instance.set(getTokenString());
}
@Override
public void resetToken() throws Exception {
AccessTokenProvider.instance.reset();
}
protected AccessToken getAccessToken() throws Exception {
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 Exception("Error parsing JWT token", e);
}
}
return accessToken;
}
@Override
public ClientInfo getClientInfo() throws Exception {
getAccessToken();
List<String> roles = new ArrayList<>(accessToken.getRealmAccess().getRoles());
ClientInfo 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");
return caller;
}
@Override
public String getContext() throws Exception {
String 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();
} 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");
}
@Override
public String getUsername() throws Exception {
return accessToken.getPreferredUsername();
}
@Override
public Map<String, String> getHTTPAuthorizationHeaders() {
Map<String, String> authorizationHeaders = new HashMap<>();
authorizationHeaders.put("Authorization", "Bearer " + getTokenString());
return authorizationHeaders;
}
public void setRenewalProvider(RenewalProvider renewalProvider) {
this.renewalProvider = renewalProvider;
}
public void setRefreshToken(RefreshToken refreshToken) {
this.refreshToken = refreshToken;
}
protected boolean isExpired(AccessToken accessToken) {
return Time.currentTimeMillis()>accessToken.getExp();
}
@Override
public boolean isExpired() {
return isExpired(accessToken);
}
@Override
public boolean isRefreshable() {
return refreshToken!=null && isExpired(refreshToken);
}
@Override
public User getUser() {
if(user==null) {
try {
ObjectMapper objectMapper = new ObjectMapper();
String accessTokenString = objectMapper.writeValueAsString(accessToken);
user = objectMapper.readValue(accessTokenString, KeycloakUser.class);
} catch (Exception e) {
throw new RuntimeException();
}
}
return user;
}
}