RemoteStoreProviderDriver.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */

  20. package org.openspcoop2.pdd.core.keystore;

  21. import java.io.ByteArrayOutputStream;
  22. import java.security.PublicKey;
  23. import java.util.Date;
  24. import java.util.HashMap;
  25. import java.util.Map;

  26. import org.openspcoop2.core.config.driver.db.DriverConfigurazioneDB;
  27. import org.openspcoop2.core.eventi.Evento;
  28. import org.openspcoop2.pdd.config.ConfigurazionePdDReader;
  29. import org.openspcoop2.pdd.config.OpenSPCoop2Properties;
  30. import org.openspcoop2.pdd.config.PDNDConfigUtilities;
  31. import org.openspcoop2.pdd.core.eventi.GestoreEventi;
  32. import org.openspcoop2.pdd.timers.TimerException;
  33. import org.openspcoop2.pdd.timers.pdnd.TimerGestoreChiaviPDNDEvent;
  34. import org.openspcoop2.pdd.timers.pdnd.TimerGestoreChiaviPDNDLib;
  35. import org.openspcoop2.security.keystore.cache.RemoteStoreClientInfoCache;
  36. import org.openspcoop2.utils.SemaphoreLock;
  37. import org.openspcoop2.utils.UtilsException;
  38. import org.openspcoop2.utils.certificate.ArchiveLoader;
  39. import org.openspcoop2.utils.certificate.Certificate;
  40. import org.openspcoop2.utils.certificate.JWK;
  41. import org.openspcoop2.utils.certificate.KeyUtils;
  42. import org.openspcoop2.utils.certificate.remote.IRemoteStoreProvider;
  43. import org.openspcoop2.utils.certificate.remote.RemoteKeyType;
  44. import org.openspcoop2.utils.certificate.remote.RemoteStoreClientInfo;
  45. import org.openspcoop2.utils.certificate.remote.RemoteStoreConfig;
  46. import org.openspcoop2.utils.certificate.remote.RemoteStoreUtils;
  47. import org.openspcoop2.utils.date.DateManager;
  48. import org.openspcoop2.utils.date.DateUtils;
  49. import org.slf4j.Logger;

  50. /**
  51.  * RemoteStoreProviderDriver
  52.  *
  53.  * @author Poli Andrea (apoli@link.it)
  54.  * @author $Author$
  55.  * @version $Rev$, $Date$
  56.  */
  57. public class RemoteStoreProviderDriver implements IRemoteStoreProvider {

  58.     private static int keyMaxLifeMinutes = -1; // infinito
  59.     public static int getKeyMaxLifeMinutes() {
  60.         return keyMaxLifeMinutes;
  61.     }
  62.     public static void setKeyMaxLifeMinutes(int keyMaxLifeMinutes) {
  63.         RemoteStoreProviderDriver.keyMaxLifeMinutes = keyMaxLifeMinutes;
  64.     }
  65.    
  66.    
  67.     private static final Map<String, RemoteStoreProviderDriver> _providerStore = new HashMap<>();
  68.     public static synchronized void initialize(Logger log, RemoteStoreConfig remoteStoreConfig) throws KeystoreException {
  69.         String storeConfigName = getRemoteStoreConfigName(remoteStoreConfig);
  70.         RemoteStoreProviderDriver s = new RemoteStoreProviderDriver(log, remoteStoreConfig);
  71.         _providerStore.put(storeConfigName, s);
  72.     }
  73.     static RemoteStoreProviderDriver getProviderStore(String storeConfigName) throws KeystoreException{
  74.         RemoteStoreProviderDriver s = _providerStore.get(storeConfigName);
  75.         if(s==null) {
  76.             throw new KeystoreException("ProviderStore '"+storeConfigName+"' not exists");
  77.         }
  78.         return s;
  79.     }
  80.    
  81.    
  82.     private static final Map<String, org.openspcoop2.utils.Semaphore> _lockStore = new HashMap<>();
  83.     private static synchronized org.openspcoop2.utils.Semaphore initLockStore(String nomeRemoteStore){
  84.         org.openspcoop2.utils.Semaphore s = _lockStore.get(nomeRemoteStore);
  85.         if(s==null) {
  86.             Integer permits = OpenSPCoop2Properties.getInstance().getGestioneTokenValidazioneJWTLockPermits();
  87.             if(permits!=null && permits.intValue()>1) {
  88.                 s = new org.openspcoop2.utils.Semaphore("GestoreTokenValidazioneJWT_"+nomeRemoteStore, permits);
  89.             }
  90.             else {
  91.                 s = new org.openspcoop2.utils.Semaphore("GestoreTokenValidazioneJWT_"+nomeRemoteStore);
  92.             }
  93.             _lockStore.put(nomeRemoteStore, s);
  94.         }
  95.         return s;
  96.     }
  97.     private static org.openspcoop2.utils.Semaphore getLockStore(String nomeRemoteStore){
  98.         org.openspcoop2.utils.Semaphore s = _lockStore.get(nomeRemoteStore);
  99.         if(s==null) {
  100.             s = initLockStore(nomeRemoteStore);
  101.         }
  102.         return s;
  103.     }
  104.    
  105.    
  106.     static String getRemoteStoreConfigName(RemoteStoreConfig remoteStoreConfig) throws KeystoreException {
  107.         if(remoteStoreConfig==null) {
  108.             throw new KeystoreException("RemoteStoreConfig undefined");
  109.         }
  110.         String remoteStoreName = remoteStoreConfig.getStoreName();
  111.         if(remoteStoreName==null) {
  112.             throw new KeystoreException("RemoteStoreConfig name undefined");
  113.         }
  114.         return remoteStoreName;
  115.     }
  116.    
  117.    
  118.    
  119.     private DriverConfigurazioneDB driverConfigurazioneDB = null;
  120.    
  121.     private Logger log;
  122.    
  123.     private RemoteStoreConfig remoteStoreConfig;
  124.     private String keyAlgorithm;
  125.     private long remoteStoreId;
  126.    
  127.     private RemoteStoreProviderDriver(Logger log, RemoteStoreConfig remoteStoreConfig) throws KeystoreException {
  128.        
  129.         this.log = log;
  130.        
  131.         getRemoteStoreConfigName(remoteStoreConfig);
  132.         this.remoteStoreConfig = remoteStoreConfig;
  133.        
  134.         this.keyAlgorithm = remoteStoreConfig.getKeyAlgorithm();
  135.         if(this.keyAlgorithm==null) {
  136.             this.keyAlgorithm = KeyUtils.ALGO_RSA;
  137.         }
  138.            
  139.         Object oConfig = ConfigurazionePdDReader.getDriverConfigurazionePdD();
  140.         if(oConfig instanceof DriverConfigurazioneDB) {
  141.             this.driverConfigurazioneDB = (DriverConfigurazioneDB) oConfig;
  142.         }
  143.         else {
  144.             throw new KeystoreException("RemoteStoreProvider utilizzabile solamente con una configurazione su database");
  145.         }
  146.        
  147.         this.remoteStoreId = RemoteStoreProviderDriverUtils.registerIfNotExistsRemoteStore(this.driverConfigurazioneDB, this.remoteStoreConfig);
  148.     }
  149.    
  150.    
  151.     private String getPrefixKid(String keyId) {
  152.         return "Chiave con kid '"+keyId+"'";
  153.     }
  154.    
  155.     private Object readKey(RemoteKeyType remoteStoreKeyType, String keyId, ByteArrayOutputStream bout, RemoteStoreConfig remoteConfig) throws KeystoreException {
  156.        
  157.         RemoteStoreKey key = null;
  158.         try {
  159.             key = RemoteStoreProviderDriverUtils.getRemoteStoreKey(this.driverConfigurazioneDB, this.remoteStoreId, keyId);
  160.         }catch(KeystoreNotFoundException notFound) {
  161.             String msg = getPrefixKid(keyId)+" non presente su database";
  162.             this.log.debug(msg);
  163.             // ignore
  164.         }
  165.         boolean updateRequired = isUpdateRequired(key, keyId);
  166.         if(!updateRequired && key!=null && key.getKey()!=null) {
  167.             try {
  168.                 switch (remoteStoreKeyType) {
  169.                     case JWK:
  170.                         return new JWK(new String(key.getKey()));
  171.                     case PUBLIC_KEY:
  172.                         return KeyUtils.getInstance(this.keyAlgorithm).getPublicKey(key.getKey());
  173.                     case X509:
  174.                         return ArchiveLoader.load(key.getKey());
  175.                 }
  176.             }catch(Exception e) {
  177.                 throw new KeystoreException(e.getMessage(),e);
  178.             }
  179.         }
  180.        
  181.         org.openspcoop2.utils.Semaphore semaphore = getLockStore(this.remoteStoreConfig.getStoreName());
  182.         SemaphoreLock lock = null;
  183.         try {
  184.             lock = semaphore.acquire("readKey");
  185.         }catch(Exception e) {
  186.             throw new KeystoreException(e.getMessage(),e);
  187.         }
  188.         try (ByteArrayOutputStream boutInternal = new ByteArrayOutputStream()){
  189.            
  190.             ByteArrayOutputStream b = bout!=null ? bout : boutInternal;
  191.            
  192.             RemoteStoreConfig remoteConfigUse = (remoteConfig!=null && remoteConfig.isMultitenant()) ? remoteConfig : this.remoteStoreConfig;
  193.            
  194.             Object objectKey = null;
  195.             switch (remoteStoreKeyType) {
  196.             case JWK:
  197.                 objectKey = RemoteStoreUtils.readJWK(keyId, remoteConfigUse, b);
  198.                 break;
  199.             case PUBLIC_KEY:
  200.                 objectKey = RemoteStoreUtils.readPublicKey(keyId, remoteConfigUse, b);
  201.                 break;
  202.             case X509:
  203.                 objectKey = RemoteStoreUtils.readX509(keyId, remoteConfigUse, b);
  204.                 break;
  205.             }
  206.            
  207.             saveKey(updateRequired, keyId, b);
  208.            
  209.             return objectKey;
  210.            
  211.         }catch(Exception e) {
  212.             throw new KeystoreException(e.getMessage(),e);
  213.         }finally {
  214.             semaphore.release(lock, "readKey");
  215.         }
  216.        
  217.     }
  218.     private void saveKey(boolean updateRequired, String keyId, ByteArrayOutputStream b) throws KeystoreException, TimerException {
  219.         if(updateRequired) {
  220.             String msg = getPrefixKid(keyId)+" ottenuta da remote store config, aggiornamento entry sul db ...";
  221.             this.log.debug(msg);
  222.             int rows = RemoteStoreProviderDriverUtils.updateRemoteStoreKey(this.driverConfigurazioneDB, this.remoteStoreId, keyId, b.toByteArray());
  223.             msg = getPrefixKid(keyId)+" ottenuta da remote store config, aggiornata entry sul db (updateRows:"+rows+")";
  224.             this.log.debug(msg);
  225.         }
  226.         else {
  227.             String msg = getPrefixKid(keyId)+" ottenuta da remote store config, registrazione sul db ...";
  228.             this.log.debug(msg);
  229.             int rows = RemoteStoreProviderDriverUtils.addRemoteStoreKey(this.driverConfigurazioneDB, this.remoteStoreId, keyId, b.toByteArray());
  230.             msg = getPrefixKid(keyId)+" ottenuta da remote store config, registrata entry sul db (updateRows:"+rows+")";
  231.             this.log.debug(msg);
  232.            
  233.             if(OpenSPCoop2Properties.getInstance().isGestoreChiaviPDNDEventiAdd()) {
  234.                 Evento evento = TimerGestoreChiaviPDNDLib.buildEvento(TimerGestoreChiaviPDNDEvent.EVENT_TYPE_ADDED, keyId, "La chiave è stata aggiunta al repository locale");
  235.                 registerEvent(evento, keyId);
  236.             }
  237.         }
  238.     }
  239.     private void registerEvent(Evento evento, String keyId) {
  240.         try {
  241.             GestoreEventi.getInstance().log(evento);
  242.         }catch(Exception e) {
  243.             String msgError = "Registrazione evento per kid '"+keyId+"' (eventType:"+TimerGestoreChiaviPDNDEvent.EVENT_TYPE_ADDED+") non riuscita: "+e.getMessage();
  244.             this.log.error(msgError,e);
  245.         }
  246.     }
  247.     private boolean isUpdateRequired(RemoteStoreKey key, String keyId) {
  248.         if(key!=null && key.isInvalid()) {
  249.             String msg = getPrefixKid(keyId)+" non è valida";
  250.             this.log.debug(msg);
  251.             return true;
  252.         }
  253.         if(key!=null && keyMaxLifeMinutes>0 && key.getDataAggiornamento()!=null) {
  254.             long maxLifeSeconds = keyMaxLifeMinutes * 60l;
  255.             long maxLifeMs = maxLifeSeconds * 1000l;
  256.             Date tooOld = new Date(DateManager.getTimeMillis()-maxLifeMs);
  257.             if(key.getDataAggiornamento().before(tooOld)) {
  258.                 String msg = getPrefixKid(keyId)+" è più vecchia di "+keyMaxLifeMinutes+" minuti (data aggiornamento: "+DateUtils.getSimpleDateFormatMs().format(key.getDataAggiornamento())+")";
  259.                 this.log.debug(msg);
  260.                 return true;
  261.             }
  262.         }
  263.         return false;
  264.     }
  265.    
  266.     @Override
  267.     public JWK readJWK(String keyId, RemoteStoreConfig remoteConfig) throws UtilsException {
  268.         try {
  269.             return (JWK) readKey(RemoteKeyType.JWK, keyId, null, remoteConfig);
  270.         }catch(Exception e) {
  271.             throw new UtilsException(e.getMessage(),e);
  272.         }
  273.     }
  274.     @Override
  275.     public JWK readJWK(String keyId, RemoteStoreConfig remoteConfig, ByteArrayOutputStream bout) throws UtilsException {
  276.         try {
  277.             return (JWK) readKey(RemoteKeyType.JWK, keyId, bout, remoteConfig);
  278.         }catch(Exception e) {
  279.             throw new UtilsException(e.getMessage(),e);
  280.         }
  281.     }
  282.     @Override
  283.     public Certificate readX509(String keyId, RemoteStoreConfig remoteConfig) throws UtilsException {
  284.         try {
  285.             return (Certificate) readKey(RemoteKeyType.X509, keyId, null, remoteConfig);
  286.         }catch(Exception e) {
  287.             throw new UtilsException(e.getMessage(),e);
  288.         }
  289.     }
  290.     @Override
  291.     public Certificate readX509(String keyId, RemoteStoreConfig remoteConfig, ByteArrayOutputStream bout)
  292.             throws UtilsException {
  293.         try {
  294.             return (Certificate) readKey(RemoteKeyType.X509, keyId, bout, remoteConfig);
  295.         }catch(Exception e) {
  296.             throw new UtilsException(e.getMessage(),e);
  297.         }
  298.     }
  299.     @Override
  300.     public PublicKey readPublicKey(String keyId, RemoteStoreConfig remoteConfig) throws UtilsException {
  301.         try {
  302.             return (PublicKey) readKey(RemoteKeyType.PUBLIC_KEY, keyId, null, remoteConfig);
  303.         }catch(Exception e) {
  304.             throw new UtilsException(e.getMessage(),e);
  305.         }
  306.     }
  307.     @Override
  308.     public PublicKey readPublicKey(String keyId, RemoteStoreConfig remoteConfig, ByteArrayOutputStream bout)
  309.             throws UtilsException {
  310.         try {
  311.             return (PublicKey) readKey(RemoteKeyType.PUBLIC_KEY, keyId, bout, remoteConfig);
  312.         }catch(Exception e) {
  313.             throw new UtilsException(e.getMessage(),e);
  314.         }
  315.     }
  316.    
  317.    
  318.    
  319.     @Override
  320.     public RemoteStoreClientInfo readClientInfo(String keyId, String clientId, RemoteStoreConfig remoteConfig, org.openspcoop2.utils.Map<Object> context)
  321.             throws UtilsException {
  322.         try {
  323.             return readClientInfoEngine(keyId, clientId, remoteConfig, context);
  324.         }catch(Exception e) {
  325.             throw new UtilsException(e.getMessage(),e);
  326.         }
  327.     }
  328.     private static boolean createEntryIfNotExists = true;
  329.     public static void setCreateEntryIfNotExists(boolean createEntryIfNotExists) {
  330.         RemoteStoreProviderDriver.createEntryIfNotExists = createEntryIfNotExists;
  331.     }
  332.     private RemoteStoreClientInfo readClientInfoEngine(String keyId, String clientId, RemoteStoreConfig remoteConfig, org.openspcoop2.utils.Map<Object> context) throws KeystoreException {
  333.        
  334.         RemoteStoreClientDetails clientDetails = null;
  335.         try {
  336.             clientDetails = RemoteStoreProviderDriverUtils.getRemoteStoreClientDetails(this.driverConfigurazioneDB, this.remoteStoreId, keyId, this.log, createEntryIfNotExists);
  337.             if(clientDetails==null) {
  338.                 throw new KeystoreNotFoundException("Client details not found");
  339.             }
  340.         }catch(KeystoreNotFoundException notFound) {
  341.             String msg = getPrefixKidClientDetails(keyId)+" non presente su database";
  342.             this.log.error(msg);
  343.             return null;
  344.         }
  345.         boolean updateRequired = isUpdateRequired(clientDetails, keyId, clientId);
  346.         if(!updateRequired) {
  347.             return clientDetails.getClientInfo();
  348.         }
  349.        
  350.         org.openspcoop2.utils.Semaphore semaphore = getLockStore(this.remoteStoreConfig.getStoreName());
  351.         SemaphoreLock lock = null;
  352.         try {
  353.             lock = semaphore.acquire("readClientDetails");
  354.         }catch(Exception e) {
  355.             throw new KeystoreException(e.getMessage(),e);
  356.         }
  357.         try {
  358.            
  359.             OpenSPCoop2Properties propertiesReader = OpenSPCoop2Properties.getInstance();
  360.            
  361.             RemoteStoreConfig remoteConfigUse = (remoteConfig!=null && remoteConfig.isMultitenant()) ? remoteConfig : this.remoteStoreConfig;
  362.            
  363.             String clientJson = PDNDConfigUtilities.readClientDetails(remoteConfigUse, propertiesReader, context, clientId, this.log);
  364.             String organizationId = null;
  365.             String organizationJson = null;
  366.             if(clientJson!=null) {
  367.                 organizationId = PDNDConfigUtilities.readOrganizationId(propertiesReader, context, clientJson, this.log);
  368.                 if(organizationId!=null) {
  369.                     organizationJson = PDNDConfigUtilities.readOrganizationDetails(remoteConfigUse, propertiesReader, context, organizationId, this.log);
  370.                 }
  371.             }
  372.            
  373.             clientDetails.setDataAggiornamento(DateManager.getDate());
  374.            
  375.             if(clientDetails.getClientInfo()==null) {
  376.                 clientDetails.setClientInfo(new RemoteStoreClientInfo());
  377.             }
  378.             clientDetails.getClientInfo().setClientId(clientId);
  379.             clientDetails.getClientInfo().setClientDetails(clientJson);
  380.             clientDetails.getClientInfo().setOrganizationId(organizationId);
  381.             clientDetails.getClientInfo().setOrganizationDetails(organizationJson);
  382.            
  383.             String msg = getPrefixKidClientDetails(keyId)+" ottenuta da remote store config, aggiornamento entry sul db ...";
  384.             this.log.debug(msg);
  385.             int rows = RemoteStoreProviderDriverUtils.updateRemoteStoreClientDetails(this.driverConfigurazioneDB, this.remoteStoreId, keyId, clientDetails);
  386.             msg = getPrefixKidClientDetails(keyId)+" ottenuta da remote store config, aggiornata entry sul db (updateRows:"+rows+")";
  387.             this.log.debug(msg);
  388.                        
  389.             return clientDetails.getClientInfo();
  390.            
  391.         }catch(Exception e) {
  392.             throw new KeystoreException(e.getMessage(),e);
  393.         }finally {
  394.             semaphore.release(lock, "readClientDetails");
  395.         }
  396.        
  397.     }
  398.    
  399.     private String getPrefixKidClientDetails(String keyId) {
  400.         return "ClientDetails con kid '"+keyId+"'";
  401.     }
  402.    
  403.     private boolean isUpdateRequired(RemoteStoreClientDetails clientDetails, String keyId, String clientId) {
  404.         if(clientDetails!=null && clientDetails.isInvalid()) {
  405.             String msg = getPrefixKidClientDetails(keyId)+" non è valida";
  406.             this.log.debug(msg);
  407.             return true;
  408.         }
  409.         if(clientDetails!=null && clientDetails.getClientInfo()==null) {
  410.             String msg = getPrefixKidClientDetails(keyId)+" client info non presente";
  411.             this.log.debug(msg);
  412.             return true;
  413.         }
  414.         if(clientDetails!=null && clientDetails.getClientInfo().getClientId()==null) {
  415.             String msg = getPrefixKidClientDetails(keyId)+" client id non presente";
  416.             this.log.debug(msg);
  417.             return true;
  418.         }
  419.         if(clientDetails!=null && !clientDetails.getClientInfo().getClientId().equals(clientId)) {
  420.             String msg = getPrefixKidClientDetails(keyId)+" client id differente da quello presente su database";
  421.             this.log.debug(msg);
  422.             return true;
  423.         }
  424.         return isUpdateRequiredByMaxLife(clientDetails, keyId);
  425.     }
  426.    
  427.     private boolean isUpdateRequiredByMaxLife(RemoteStoreClientDetails clientDetails, String keyId) {
  428.         if(clientDetails!=null) {
  429.             int maxLife =  clientDetails.isInfoComplete() ? RemoteStoreClientInfoCache.getClientDetailsMaxLifeMinutes() : RemoteStoreClientInfoCache.getClientDetailsCacheFallbackMaxLifeMinutes();
  430.             if(maxLife>0 && clientDetails.getDataAggiornamento()!=null) {
  431.                 long maxLifeSeconds = maxLife * 60l;
  432.                 long maxLifeMs = maxLifeSeconds * 1000l;
  433.                 Date tooOld = new Date(DateManager.getTimeMillis()-maxLifeMs);
  434.                 if(clientDetails.getDataAggiornamento().before(tooOld)) {
  435.                     String msg = getPrefixKidClientDetails(keyId)+" è più vecchia di "+maxLife+" minuti (data aggiornamento: "+DateUtils.getSimpleDateFormatMs().format(clientDetails.getDataAggiornamento())+")(info-complete:"+clientDetails.isInfoComplete()+")";
  436.                     this.log.debug(msg);
  437.                     return true;
  438.                 }
  439.             }
  440.         }
  441.            
  442.         return false;
  443.     }
  444. }