PDNDResolver.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2024 Link.it srl (https://link.it).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3, as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openspcoop2.pdd.config;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.openspcoop2.core.commons.CoreException;
import org.openspcoop2.core.constants.Costanti;
import org.openspcoop2.core.id.IDSoggetto;
import org.openspcoop2.core.transazioni.utils.CredenzialiMittente;
import org.openspcoop2.pdd.core.autenticazione.GestoreAutenticazione;
import org.openspcoop2.pdd.core.keystore.KeystoreException;
import org.openspcoop2.pdd.core.keystore.RemoteStoreProvider;
import org.openspcoop2.pdd.core.token.InformazioniToken;
import org.openspcoop2.pdd.core.token.TokenUtilities;
import org.openspcoop2.pdd.core.transazioni.Transaction;
import org.openspcoop2.pdd.core.transazioni.TransactionContext;
import org.openspcoop2.pdd.core.transazioni.TransactionDeletedException;
import org.openspcoop2.pdd.core.transazioni.TransactionNotExistsException;
import org.openspcoop2.protocol.engine.SecurityTokenUtilities;
import org.openspcoop2.protocol.sdk.Context;
import org.openspcoop2.protocol.sdk.PDNDTokenInfo;
import org.openspcoop2.protocol.sdk.PDNDTokenInfoDetails;
import org.openspcoop2.protocol.sdk.ProtocolException;
import org.openspcoop2.protocol.sdk.SecurityToken;
import org.openspcoop2.protocol.sdk.state.RequestInfo;
import org.openspcoop2.security.SecurityException;
import org.openspcoop2.security.keystore.cache.GestoreKeystoreCache;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.certificate.remote.RemoteKeyType;
import org.openspcoop2.utils.certificate.remote.RemoteStoreClientInfo;
import org.openspcoop2.utils.certificate.remote.RemoteStoreConfig;
import org.openspcoop2.utils.json.JSONUtils;
import com.fasterxml.jackson.databind.JsonNode;
/**
* PDNDResolver
*
* @author Poli Andrea (poli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class PDNDResolver {
private Context context;
private List<RemoteStoreConfig> remoteStores;
public PDNDResolver(Context context, List<RemoteStoreConfig> remoteStores) {
this.context = context;
this.remoteStores = remoteStores;
}
public boolean isRemoteStore(String name) {
return isRemoteStore(name, this.remoteStores);
}
public static boolean isRemoteStore(String name, List<RemoteStoreConfig> remoteStores) {
for (RemoteStoreConfig rsc : remoteStores) {
if(name.equals(rsc.getStoreName())) {
return true;
}
}
return false;
}
public RemoteStoreConfig getRemoteStoreConfig(String name, IDSoggetto idDominio) throws ProtocolException {
return getRemoteStoreConfig(name, idDominio, this.remoteStores);
}
public static RemoteStoreConfig getRemoteStoreConfig(String name, IDSoggetto idDominio, List<RemoteStoreConfig> remoteStores) throws ProtocolException {
for (RemoteStoreConfig rsc : remoteStores) {
if(name.equals(rsc.getStoreName())) {
if(rsc.isMultitenant() && idDominio!=null && idDominio.getNome()!=null) {
try {
return rsc.newInstanceMultitenant(idDominio.getNome());
}catch(Exception e){
throw new ProtocolException(e.getMessage(),e);
}
}
return rsc;
}
}
return null;
}
public RemoteStoreConfig getRemoteStoreConfigByTokenPolicy(String name, IDSoggetto idDominio) throws ProtocolException {
return getRemoteStoreConfigByTokenPolicy(name, idDominio, this.remoteStores);
}
public static RemoteStoreConfig getRemoteStoreConfigByTokenPolicy(String name, IDSoggetto idDominio, List<RemoteStoreConfig> remoteStores) throws ProtocolException {
for (RemoteStoreConfig rsc : remoteStores) {
if(name.equals(rsc.getTokenPolicy())) {
if(rsc.isMultitenant() && idDominio!=null && idDominio.getNome()!=null) {
try {
return rsc.newInstanceMultitenant(idDominio.getNome());
}catch(Exception e){
throw new ProtocolException(e.getMessage(),e);
}
}
return rsc;
}
}
return null;
}
public RemoteStoreConfig enrichTokenInfo(RequestInfo requestInfo, IDSoggetto idSoggetto,
InformazioniToken informazioniToken, SecurityToken securityTokenForContext) throws ProtocolException {
return enrichTokenInfo(requestInfo, false, false, idSoggetto,
informazioniToken, securityTokenForContext);
}
public RemoteStoreConfig enrichTokenInfo(RequestInfo requestInfo, boolean sicurezzaMessaggio, boolean sicurezzaAudit, IDSoggetto idSoggetto) throws ProtocolException {
Object oInformazioniTokenNormalizzate = null;
if(this.context!=null) {
oInformazioniTokenNormalizzate = this.context.getObject(org.openspcoop2.pdd.core.token.Costanti.PDD_CONTEXT_TOKEN_INFORMAZIONI_NORMALIZZATE);
}
InformazioniToken informazioniTokenNormalizzate = null;
if(oInformazioniTokenNormalizzate instanceof InformazioniToken) {
informazioniTokenNormalizzate = (InformazioniToken) oInformazioniTokenNormalizzate;
}
SecurityToken securityTokenForContext = SecurityTokenUtilities.readSecurityToken(this.context);
return enrichTokenInfo(requestInfo, sicurezzaMessaggio, sicurezzaAudit, idSoggetto,
informazioniTokenNormalizzate, securityTokenForContext);
}
private RemoteStoreConfig enrichTokenInfo(RequestInfo requestInfo, boolean sicurezzaMessaggio, boolean sicurezzaAudit, IDSoggetto idSoggetto,
InformazioniToken informazioniToken, SecurityToken securityTokenForContext) throws ProtocolException {
OpenSPCoop2Properties op2Properties = OpenSPCoop2Properties.getInstance();
RemoteStoreConfig rsc = null;
try {
if(op2Properties.isGestoreChiaviPDNDclientInfoEnabled()) {
rsc = getRemoteStoreConfig(idSoggetto);
if(rsc==null) {
return rsc;
}
String clientId = null;
if(informazioniToken!=null) {
clientId = informazioniToken.getClientId();
}
if(clientId==null) {
return rsc;
}
// NOTA: il kid DEVE essere preso dall'eventuale token di integrità, poichè il kid nell'access token è sempre uguale ed è quello della PDND
String kid = readKid(sicurezzaMessaggio, sicurezzaAudit, securityTokenForContext, clientId);
enrichTokenInfo(requestInfo, idSoggetto,
informazioniToken, securityTokenForContext,
rsc, clientId, kid);
}
}catch(Exception e) {
throw new ProtocolException(e.getMessage(),e);
}
return rsc;
}
private void enrichTokenInfo(RequestInfo requestInfo, IDSoggetto idSoggetto,
InformazioniToken informazioniToken, SecurityToken securityTokenForContext,
RemoteStoreConfig rsc, String clientId, String kid) throws KeystoreException, SecurityException, UtilsException, TransactionNotExistsException, CoreException, TransactionDeletedException {
/**if(kid.startsWith(REMOTE_STORE_KEY_KID_STARTS_WITH_CLIENT_ID)) {*/
Object oInfoPDND = null;
if(this.context!=null) {
oInfoPDND = this.context.getObject(org.openspcoop2.pdd.core.token.Costanti.PDD_CONTEXT_TOKEN_INFORMAZIONI_PDND_CLIENT_READ);
}
if(oInfoPDND instanceof String) {
String s = (String) oInfoPDND;
if("true".equals(s)) {
// chiamata fatta dalla validazione semantica dopo che la raccolta delle informazioni sulla PDND è già stata chiamata in seguito alla validazione del token
// poiche' il kid non e' presente, e' inutile ri-effettuare la medesima invocazione
return;
}
}
/**}*/
enrichTokenInfo(securityTokenForContext, informazioniToken, requestInfo, rsc,
kid, clientId);
if(kid.startsWith(REMOTE_STORE_KEY_KID_STARTS_WITH_CLIENT_ID)) {
this.context.put(org.openspcoop2.pdd.core.token.Costanti.PDD_CONTEXT_TOKEN_INFORMAZIONI_PDND_CLIENT_READ, "true");
}
updateCredenzialiTokenPDND(requestInfo, idSoggetto, informazioniToken);
}
private void updateCredenzialiTokenPDND(RequestInfo requestInfo, IDSoggetto idSoggetto, InformazioniToken informazioniToken) throws TransactionNotExistsException, CoreException, TransactionDeletedException {
String idTransazione = null;
if(this.context!=null) {
idTransazione = (String) this.context.getObject(Costanti.ID_TRANSAZIONE);
}
Transaction transaction = TransactionContext.getTransaction(idTransazione);
CredenzialiMittente credenzialiMittente = transaction.getCredenzialiMittente();
if(credenzialiMittente==null) {
credenzialiMittente = new CredenzialiMittente();
transaction.setCredenzialiMittente(credenzialiMittente);
}
try {
GestoreAutenticazione.updateCredenzialiTokenPDND(idSoggetto, "ModIValidator", idTransazione,
informazioniToken, credenzialiMittente,
null, "ModIValidator.credenzialiPDND", requestInfo,
this.context);
}catch(Exception e) {
throw new CoreException(e.getMessage(),e);
}
}
private RemoteStoreConfig getRemoteStoreConfig(IDSoggetto idSoggetto) throws ProtocolException {
Object oTokenPolicy = null;
if(this.context!=null) {
oTokenPolicy = this.context.getObject(org.openspcoop2.pdd.core.token.Costanti.PDD_CONTEXT_TOKEN_POLICY);
}
String tokenPolicy = null;
if(oTokenPolicy instanceof String) {
tokenPolicy = (String) oTokenPolicy;
}
if(tokenPolicy==null) {
return null;
}
return this.getRemoteStoreConfigByTokenPolicy(tokenPolicy, idSoggetto);
}
public static final String REMOTE_STORE_KEY_KID_STARTS_WITH_CLIENT_ID = "ClientId--";
private String readKid(boolean sicurezzaMessaggio, boolean sicurezzaAudit, SecurityToken securityTokenForContext, String clientId) throws UtilsException {
// NOTA: il kid DEVE essere preso dall'eventuale token di integrità, poichè il kid nell'access token è sempre uguale ed è quello della PDND
String kid = null;
if(sicurezzaMessaggio) {
kid = readKidFromTokenIntegrity(securityTokenForContext);
}
if(kid==null && sicurezzaAudit) {
kid = readKidFromTokenAudit(securityTokenForContext);
}
if(kid==null) {
// Altrimenti utilizzo la struttura dati per ospitare le informazioni sul clientId
kid = REMOTE_STORE_KEY_KID_STARTS_WITH_CLIENT_ID+clientId;
}
return kid;
}
private String readKidFromTokenIntegrity(SecurityToken securityTokenForContext) throws UtilsException {
String kid = null;
if(securityTokenForContext!=null && securityTokenForContext.getIntegrity()!=null) {
kid = securityTokenForContext.getIntegrity().getKid();
if(kid==null) {
kid = securityTokenForContext.getIntegrity().getHeaderClaim("kid");
}
}
return kid;
}
private String readKidFromTokenAudit(SecurityToken securityTokenForContext) throws UtilsException {
String kid = null;
if(securityTokenForContext!=null && securityTokenForContext.getAudit()!=null) {
kid = securityTokenForContext.getAudit().getKid();
if(kid==null) {
kid = securityTokenForContext.getAudit().getHeaderClaim("kid");
}
}
return kid;
}
private void enrichTokenInfo(SecurityToken securityTokenForContext, InformazioniToken informazioniTokenNormalizzate, RequestInfo requestInfo, RemoteStoreConfig rsc,
String kid, String clientId) throws KeystoreException, SecurityException, UtilsException {
RemoteKeyType keyType = RemoteKeyType.JWK; // ignored
RemoteStoreProvider remoteStoreProvider = new RemoteStoreProvider(requestInfo, keyType);
RemoteStoreClientInfo rsci = GestoreKeystoreCache.getRemoteStoreClientInfo(requestInfo, kid, clientId, rsc, remoteStoreProvider, this.context);
if(rsci!=null &&
(rsci.getClientDetails()!=null || rsci.getOrganizationId()!=null || rsci.getOrganizationDetails()!=null)
){
if(informazioniTokenNormalizzate.getPdnd()==null) {
informazioniTokenNormalizzate.setPdnd(new HashMap<>());
}
if(rsci.getClientDetails()!=null) {
JSONUtils jsonUtils = JSONUtils.getInstance();
if(jsonUtils.isJson(rsci.getClientDetails())) {
JsonNode root = jsonUtils.getAsNode(rsci.getClientDetails());
PDNDTokenInfoDetails info = new PDNDTokenInfoDetails();
info.setId(rsci.getClientId());
info.setDetails(rsci.getClientDetails());
enrichTokenInfoAddInClaims(jsonUtils, securityTokenForContext, informazioniTokenNormalizzate, root, PDNDTokenInfo.CLIENT_INFO, info);
}
}
if(rsci.getOrganizationDetails()!=null) {
JSONUtils jsonUtils = JSONUtils.getInstance();
if(jsonUtils.isJson(rsci.getOrganizationDetails())) {
JsonNode root = jsonUtils.getAsNode(rsci.getOrganizationDetails());
PDNDTokenInfoDetails info = new PDNDTokenInfoDetails();
info.setId(rsci.getOrganizationId());
info.setDetails(rsci.getOrganizationDetails());
enrichTokenInfoAddInClaims(jsonUtils, securityTokenForContext, informazioniTokenNormalizzate, root, PDNDTokenInfo.ORGANIZATION_INFO, info);
}
}
}
}
private void enrichTokenInfoAddInClaims(JSONUtils jsonUtils, SecurityToken securityTokenForContext, InformazioniToken informazioniTokenNormalizzate, JsonNode root, String type,
PDNDTokenInfoDetails info) {
Map<String, Serializable> readClaims = jsonUtils.convertToSimpleMap(root);
if(!readClaims.isEmpty()) {
enrichTokenInfoAddInClaims(securityTokenForContext, informazioniTokenNormalizzate,
type, info,
readClaims);
}
}
private void enrichTokenInfoAddInClaims(SecurityToken securityTokenForContext, InformazioniToken informazioniTokenNormalizzate,
String type, PDNDTokenInfoDetails info,
Map<String, Serializable> readClaims) {
informazioniTokenNormalizzate.getPdnd().put(type,TokenUtilities.toHashMapSerializable(readClaims));
String prefix = PDNDTokenInfo.TOKEN_INFO_PREFIX_PDND+type+".";
Map<String, Serializable> readClaimsSerializable = new HashMap<>();
if(informazioniTokenNormalizzate.getClaims()!=null) {
for (Map.Entry<String,Serializable> entry : readClaims.entrySet()) {
String key = prefix+entry.getKey();
if(!informazioniTokenNormalizzate.getClaims().containsKey(key)) {
informazioniTokenNormalizzate.getClaims().put(key, entry.getValue());
}
if(entry.getValue() instanceof Serializable) {
readClaimsSerializable.put(entry.getKey(), entry.getValue());
}
}
}
info.setClaims(readClaimsSerializable);
if(securityTokenForContext!=null) {
if(securityTokenForContext.getPdnd()==null) {
securityTokenForContext.setPdnd(new PDNDTokenInfo());
}
securityTokenForContext.getPdnd().setInfo(type, info);
}
}
}