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.utils.SemaphoreLock;
  36. import org.openspcoop2.utils.UtilsException;
  37. import org.openspcoop2.utils.certificate.ArchiveLoader;
  38. import org.openspcoop2.utils.certificate.Certificate;
  39. import org.openspcoop2.utils.certificate.JWK;
  40. import org.openspcoop2.utils.certificate.KeyUtils;
  41. import org.openspcoop2.utils.certificate.remote.IRemoteStoreProvider;
  42. import org.openspcoop2.utils.certificate.remote.RemoteKeyType;
  43. import org.openspcoop2.utils.certificate.remote.RemoteStoreClientInfo;
  44. import org.openspcoop2.utils.certificate.remote.RemoteStoreConfig;
  45. import org.openspcoop2.utils.certificate.remote.RemoteStoreUtils;
  46. import org.openspcoop2.utils.date.DateManager;
  47. import org.openspcoop2.utils.date.DateUtils;
  48. import org.slf4j.Logger;

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

  57.     private static int keyMaxLifeMinutes = -1; // infinito
  58.     public static int getKeyMaxLifeMinutes() {
  59.         return keyMaxLifeMinutes;
  60.     }
  61.     public static void setKeyMaxLifeMinutes(int keyMaxLifeMinutes) {
  62.         RemoteStoreProviderDriver.keyMaxLifeMinutes = keyMaxLifeMinutes;
  63.     }
  64.    
  65.    
  66.     private static int clientDetailsMaxLifeMinutes = -1; // infinito
  67.     public static int getClientDetailsMaxLifeMinutes() {
  68.         return clientDetailsMaxLifeMinutes;
  69.     }
  70.     public static void setClientDetailsMaxLifeMinutes(int clientDetailsMaxLifeMinutes) {
  71.         RemoteStoreProviderDriver.clientDetailsMaxLifeMinutes = clientDetailsMaxLifeMinutes;
  72.     }


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