DigestServiceParamsDriver.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2025 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.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.Instant;
import org.openspcoop2.core.config.driver.DriverConfigurazioneException;
import org.openspcoop2.core.config.driver.db.DriverConfigurazioneDB;
import org.openspcoop2.core.constants.CostantiDB;
import org.openspcoop2.core.id.IDServizio;
import org.openspcoop2.pdd.core.GestoreMessaggi;
import org.openspcoop2.utils.SemaphoreLock;
import org.openspcoop2.utils.TipiDatabase;
import org.openspcoop2.utils.Utilities;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.digest.DigestType;
import org.openspcoop2.utils.id.serial.InfoStatistics;
import org.openspcoop2.utils.semaphore.Semaphore;
import org.openspcoop2.utils.semaphore.SemaphoreConfiguration;
import org.openspcoop2.utils.semaphore.SemaphoreMapping;
import org.openspcoop2.utils.sql.ISQLQueryObject;
import org.openspcoop2.utils.sql.SQLObjectFactory;
import org.openspcoop2.utils.sql.SQLQueryObjectException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DigestServiceParamsDriver
*
* @author Burlon Tommaso (tommaso.burlon@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class DigestServiceParamsDriver {
private static final String DB_LOCK_ID = "ServiziDigestParamsUpdate";
private static final org.openspcoop2.utils.Semaphore THREAD_LOCK = new org.openspcoop2.utils.Semaphore("DigestServiceParamsDriver-threadLock");
private Logger logger = LoggerFactory.getLogger(getClass());
private DriverConfigurazioneDB driverConfigurazioneDB;
private String getKey(IDServizio idServizio, Long serialNumber) {
return "DigestServiceParams" + idServizio.toString() + "-" + serialNumber;
}
private Long getIdServizio(Connection conn, String tipoDB, IDServizio idServizio) throws SQLQueryObjectException, SQLException {
ISQLQueryObject query = SQLObjectFactory.createSQLQueryObject(tipoDB);
query.addFromTable(CostantiDB.SOGGETTI);
query.addSelectField("id");
query.setANDLogicOperator(true);
query.addWhereCondition(CostantiDB.SOGGETTI_COLUMN_NOME_SOGGETTO + "= ?");
query.addWhereCondition(CostantiDB.SOGGETTI_COLUMN_TIPO_SOGGETTO + "= ?");
Long idSoggetto = null;
try( PreparedStatement stmt = conn.prepareStatement(query.createSQLQuery())) {
int index = 1;
stmt.setString(index++, idServizio.getSoggettoErogatore().getNome());
stmt.setString(index++, idServizio.getSoggettoErogatore().getTipo());
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
idSoggetto = rs.getLong("id");
}
}
}
if (idSoggetto == null)
return null;
query = SQLObjectFactory.createSQLQueryObject(tipoDB);
query.addFromTable(CostantiDB.SERVIZI);
query.addSelectField("id");
query.setANDLogicOperator(true);
query.addWhereCondition(CostantiDB.SERVIZI_COLUMN_NOME_SERVIZIO + "= ?");
query.addWhereCondition(CostantiDB.SERVIZI_COLUMN_TIPO_SERVIZIO + "= ?");
query.addWhereCondition(CostantiDB.SERVIZI_COLUMN_VERSIONE_SERVIZIO + "= ?");
query.addWhereCondition(CostantiDB.SERVIZI_COLUMN_ID_SOGGETTO_REF + "= ?");
try( PreparedStatement stmt = conn.prepareStatement(query.createSQLQuery())) {
int index = 1;
stmt.setString(index++, idServizio.getNome());
stmt.setString(index++, idServizio.getTipo());
stmt.setInt(index++, idServizio.getVersione());
stmt.setLong(index++, idSoggetto);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return rs.getLong("id");
}
}
}
return null;
}
private DigestServiceParams paramsFromResultSet(IDServizio idServizio, ResultSet rs) throws SQLException {
DigestServiceParams params = new DigestServiceParams();
params.setIdServizio(idServizio);
params.setSerialNumber(rs.getLong(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_SERIAL_NUMBER));
params.setDataRegistrazione(rs.getTimestamp(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_DATE).toInstant());
params.setDurata(rs.getInt(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_PERIOD));
params.setSeed(rs.getString(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_SEED).getBytes());
params.setDigestAlgorithm(DigestType.valueOf(rs.getString(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_ALGORITHM)));
return params;
}
public DigestServiceParamsDriver(DriverConfigurazioneDB driverConfigurazioneDB) {
this.driverConfigurazioneDB = driverConfigurazioneDB;
}
/**
* Rimuove tutte le informazioni crittografiche per la generazione dei digest per un determinato
* servizio.
* @param idServizio servizio di cui rimuovere le informazioni crittografiche
* @return
* @throws DriverConfigurazioneException nel caso la rimozione non avvenga correttamente
*/
public boolean removeEntries(IDServizio idServizio) throws DriverConfigurazioneException {
Connection conn = null;
String tipoDB = this.driverConfigurazioneDB.getTipoDB();
try {
conn = this.driverConfigurazioneDB.getConnection("removeEntries");
// ottengo l'id di riferimento del servizio
Long idServizioRef = this.getIdServizio(conn, tipoDB, idServizio);
if (idServizioRef == null)
return false;
ISQLQueryObject query = SQLObjectFactory.createSQLQueryObject(tipoDB);
query.addDeleteTable(CostantiDB.SERVIZI_DIGEST_PARAMS);
query.addWhereCondition(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_ID_SERVIZIO_REF + "= ?");
try (PreparedStatement stmt = conn.prepareStatement(query.createSQLDelete())) {
stmt.setLong(1, idServizioRef);
stmt.execute();
}
} catch (SQLQueryObjectException | SQLException e) {
throw new DriverConfigurazioneException("Errore nella rimozione di un record di tipo DigestServiceParam", e);
} finally {
this.driverConfigurazioneDB.closeConnection(conn);
}
return true;
}
/**
* Rimuove partendo dalle informazioni piu vecchie tutte le informazioni crittografiche relative ad un determinato
* servizio fino ad averne al piu n
* @param idServizio: id del servizio relativo
* @param n: numero di informazioni massimo da tenere in memoria
* @return
* @throws DriverConfigurazioneException: nel caso la rimozione non vada a buon fine
*/
public boolean removeOldEntries(IDServizio idServizio, int n) throws DriverConfigurazioneException {
Connection conn = null;
String tipoDB = this.driverConfigurazioneDB.getTipoDB();
try {
conn = this.driverConfigurazioneDB.getConnection("removeEntries");
// ottengo l'id di riferimento del servizio
Long idServizioRef = this.getIdServizio(conn, tipoDB, idServizio);
if (idServizioRef == null)
return false;
// ottengo il timestamp della n-esimo record
Timestamp lastQuery = null;
ISQLQueryObject query = SQLObjectFactory.createSQLQueryObject(tipoDB);
query.addFromTable(CostantiDB.SERVIZI_DIGEST_PARAMS);
query.addSelectField(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_DATE);
query.addWhereCondition(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_ID_SERVIZIO_REF + "= ?");
query.addOrderBy(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_DATE, false);
query.setANDLogicOperator(true);
query.setLimit(1);
query.setOffset(n);
try (PreparedStatement stmt = conn.prepareStatement(query.createSQLQuery())) {
stmt.setLong(1, idServizioRef);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
lastQuery = rs.getTimestamp(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_DATE);
}
}
}
// se non esiste ci sono meno di n records
if (lastQuery == null)
return true;
// elimino tutti i record con un timestamp precedente
query = SQLObjectFactory.createSQLQueryObject(tipoDB);
query.addDeleteTable(CostantiDB.SERVIZI_DIGEST_PARAMS);
query.setANDLogicOperator(true);
query.addWhereCondition(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_ID_SERVIZIO_REF + "= ?");
query.addWhereCondition(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_DATE + "<= ?");
try (PreparedStatement stmt = conn.prepareStatement(query.createSQLDelete())) {
int index = 1;
stmt.setLong(index++, idServizioRef);
stmt.setTimestamp(index++, lastQuery);
stmt.execute();
}
} catch (SQLQueryObjectException | SQLException e) {
throw new DriverConfigurazioneException("Errore nell'aggiunta di un record di tipo DigestServiceParam", e);
} finally {
this.driverConfigurazioneDB.closeConnection(conn);
}
return true;
}
/**
* Aggiunge una nuova informazioni crittografica, questa funzione deve essere chiamata usando il lock fornito dalla classe,
* nel caso l'aggiunta vada a buon fine la nuova informazione aggiunta verra registrata in cache come informazione piu
* recente
* @param params: informazione crittografica da aggiungere
* @return
* @throws DriverConfigurazioneException: nel caso l'aggiunta non vada a buon fine
*/
public boolean addNewEntry(DigestServiceParams params) throws DriverConfigurazioneException {
Connection conn = null;
String tipoDB = this.driverConfigurazioneDB.getTipoDB();
try {
conn = this.driverConfigurazioneDB.getConnection("addEntry");
// ottengo l'id di riferimento del servizio
Long idServizioRef = this.getIdServizio(conn, tipoDB, params.getIdServizio());
if (idServizioRef == null)
return false;
// aggiungo il nuovo elemento nel db
ISQLQueryObject query = SQLObjectFactory.createSQLQueryObject(tipoDB);
query.addInsertTable(CostantiDB.SERVIZI_DIGEST_PARAMS);
if (params.getSerialNumber() != null)
query.addInsertField(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_SERIAL_NUMBER, "?");
query.addInsertField(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_ALGORITHM, "?");
query.addInsertField(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_DATE, "?");
query.addInsertField(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_ID_SERVIZIO_REF, "?");
query.addInsertField(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_PERIOD, "?");
query.addInsertField(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_SEED, "?");
try (PreparedStatement stmt = conn.prepareStatement(query.createSQLInsert())) {
int index = 1;
if (params.getSerialNumber() != null)
stmt.setLong(index++, params.getSerialNumber());
stmt.setString(index++, params.getDigestAlgorithm().toString());
stmt.setTimestamp(index++, Timestamp.from(params.getDataRegistrazione()));
stmt.setLong(index++, idServizioRef);
stmt.setInt(index++, params.getDurata());
stmt.setString(index++, new String(params.getSeed()));
stmt.execute();
}
} catch (SQLQueryObjectException | SQLException e) {
throw new DriverConfigurazioneException (e);
} finally {
this.driverConfigurazioneDB.closeConnection(conn);
}
// aggiungo i parametri in cache (se ottengo un exception posso ignorarla in quanto non invalida il processo)
try {
ConfigurazionePdDReader.getCache().put(getKey(params.getIdServizio(), null), params);
} catch (UtilsException e) {
this.logger.warn("Errore nell'aggiunta di un valore in cache", e);
}
return true;
}
public boolean isValid(DigestServiceParams param) {
if (param == null)
return false;
Instant now = Instant.now();
Instant expiration = param
.getDataRegistrazione()
.plus(Duration.ofDays(param.getDurata()));
return now.isBefore(expiration);
}
/**
* Funzione che se possible ritorna l'informazione crittografica piu recente se e solo se e' ancora valida
* @param idServizio
* @return null se l'informazione crittografica piu recente non risulta valida
* @throws DriverConfigurazioneException
*/
public DigestServiceParams getValidEntry(IDServizio idServizio) throws DriverConfigurazioneException {
DigestServiceParams param = this.getLastEntry(idServizio);
if (isValid(param))
return param;
return null;
}
/**
* Funzione che ritorna l'informazione crittografica piu recente
* @param idServizio
* @return
* @throws DriverConfigurazioneException
*/
public DigestServiceParams getLastEntry(IDServizio idServizio) throws DriverConfigurazioneException {
return this.getEntry(idServizio, null);
}
/**
* Metodo che ritorna l'informazione crittografica relativa ad un servizio dato un determinato numero seriale
* @param idServizio
* @param serialNumber
* @return
* @throws DriverConfigurazioneException
*/
public DigestServiceParams getEntry(IDServizio idServizio, Long serialNumber) throws DriverConfigurazioneException {
Connection conn = null;
String tipoDB = this.driverConfigurazioneDB.getTipoDB();
DigestServiceParams param = null;
try {
// cerco di ottenere i parametri dalla cache se e solo se il numero seriale e' definito o se il record piu recente e' ancora valido
param = (DigestServiceParams) ConfigurazionePdDReader.getRawObjectCache(getKey(idServizio, serialNumber));
if (param != null && (serialNumber != null || isValid(param)))
return param;
conn = this.driverConfigurazioneDB.getConnection("getEntry");
// ottengo l'id di riferimento del servizio
Long idServizioRef = this.getIdServizio(conn, tipoDB, idServizio);
if (idServizioRef == null)
throw new DriverConfigurazioneException("idServizio riferito non presente nella tabella servizio");
ISQLQueryObject query = SQLObjectFactory.createSQLQueryObject(tipoDB);
query.addFromTable(CostantiDB.SERVIZI_DIGEST_PARAMS);
query.setANDLogicOperator(true);
query.addWhereCondition(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_ID_SERVIZIO_REF + "= ?");
if (serialNumber != null)
query.addWhereCondition(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_SERIAL_NUMBER + "= ?");
query.addOrderBy(CostantiDB.SERVIZI_DIGEST_PARAMS_COLUMN_DATE, false);
query.setLimit(1);
try (PreparedStatement stmt = conn.prepareStatement(query.createSQLQuery())) {
int index = 1;
stmt.setLong(index++, idServizioRef);
if (serialNumber != null)
stmt.setLong(index++, serialNumber);
stmt.execute();
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
param = this.paramsFromResultSet(idServizio, rs);
}
}
}
} catch (SQLQueryObjectException | SQLException e) {
throw new DriverConfigurazioneException("Errore nella get del DigestServiceParam", e);
} finally {
this.driverConfigurazioneDB.closeConnection(conn);
}
// aggiorno la cache se e solo se il numero seriale e' esplicitato o il record e' valido
try {
if (param != null && (serialNumber != null || isValid(param)))
ConfigurazionePdDReader.getCache().put(getKey(idServizio, serialNumber), param);
} catch (UtilsException e) {
this.logger.warn("Errore nell'aggiunta di un valore in cache", e);
}
return param;
}
private Semaphore semaphore;
private SemaphoreLock lock;
private Semaphore getSemaphore() throws UtilsException {
if (this.semaphore == null) {
InfoStatistics semaphoreStatistics = new InfoStatistics();
SemaphoreConfiguration config = GestoreMessaggi.newSemaphoreConfiguration(1000, 1000);
this.semaphore = new Semaphore(semaphoreStatistics, SemaphoreMapping.newInstance(DB_LOCK_ID),
config,
TipiDatabase.toEnumConstant(this.driverConfigurazioneDB.getTipoDB()),
DriverConfigurazioneDB.getCheckLogger());
}
return this.semaphore;
}
/**
* Ottiene un lock (globale e locale) sulla tabella delle informazioni crittografiche
* @param idTransazione, id della transazione corrente
* @throws UtilsException
* @throws DriverConfigurazioneException
*/
public void acquireLock(String idTransazione) throws UtilsException, DriverConfigurazioneException {
Connection con = null;
try {
con = this.driverConfigurazioneDB.getConnection("acquireLockDigestService");
while(!this.getSemaphore().newLock(con, "acquireLockDigestService")) {
Utilities.sleep(1);
}
this.lock = THREAD_LOCK.acquire("acquireLock", idTransazione);
} catch (UtilsException | DriverConfigurazioneException e) {
this.releaseLock();
} finally {
this.driverConfigurazioneDB.releaseConnection(con);
}
}
/**
* Rilascia il lock (globale e locale) sulla tabella delle informazioni crittografiche
* @throws UtilsException
* @throws DriverConfigurazioneException
*/
public void releaseLock() throws UtilsException, DriverConfigurazioneException {
Connection con = null;
try {
THREAD_LOCK.release(this.lock, "acquireLock");
con = this.driverConfigurazioneDB.getConnection("releaseLockDigestService");
this.getSemaphore().releaseLock(con, "releaseLockDigestService");
} finally {
this.driverConfigurazioneDB.releaseConnection(con);
}
}
}