HashGenerator.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.core.response_caching;
import java.io.ByteArrayOutputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.openspcoop2.core.config.ResponseCachingConfigurazione;
import org.openspcoop2.core.config.ResponseCachingConfigurazioneHashGenerator;
import org.openspcoop2.core.config.constants.StatoFunzionalita;
import org.openspcoop2.core.config.constants.StatoFunzionalitaCacheDigestQueryParameter;
import org.openspcoop2.message.OpenSPCoop2Message;
import org.openspcoop2.message.constants.ServiceBinding;
import org.openspcoop2.protocol.sdk.state.RequestInfo;
import org.openspcoop2.utils.io.Base64Utilities;
import org.openspcoop2.utils.transport.TransportUtils;
/**
* HashGenerator
*
* @author Poli Andrea (poli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class HashGenerator {
private String algoritmo;
public HashGenerator(String algoritmo) {
// MD5, SHA-1, SHA-256
this.algoritmo = algoritmo;
}
// NOTA:
// La generazione dell'hash deve essere fatta all'inizio prima che il messaggio venga modificato.
// L'hash deve essere calcolato, se il response caching รจ abilitato, subito dopo i vari controlli di auth e rateLimiting ma prima degli handler di out ed imbustamento
// In modo che si calcola sulla richiesta in ingresso effettiva.
// NOTA2: Mascherare con una doppia entry in cache per non ritornare hash
public String buildKeyCache(OpenSPCoop2Message message, RequestInfo requestInfo, ResponseCachingConfigurazione responseCachingConfig) throws Exception {
MessageDigest digest = MessageDigest.getInstance(this.algoritmo);
StringBuilder sb = new StringBuilder();
sb.append("interfaceName").append("=").append(requestInfo.getProtocolContext().getInterfaceName());
sb.append("\n").append("function").append("=").append(requestInfo.getProtocolContext().getFunction());
boolean printAzione = true;
sb.append("\n").append("idServizio").append("=").append(requestInfo.getIdServizio().toString(printAzione));
digest.update(sb.toString().getBytes());
//System.out.println("TESTa: "+sb.toString());
ResponseCachingConfigurazioneHashGenerator configHash = responseCachingConfig.getHashGenerator();
if(configHash==null) {
configHash = new ResponseCachingConfigurazioneHashGenerator(); // utilizzo i valori di default
}
if(configHash!=null) {
if(StatoFunzionalita.ABILITATO.equals(configHash.getRequestUri())) {
// I parametri vengono riordinati proprio per far si differenze nell'ordine non impattano nel digest
sb = new StringBuilder();
sb.append("requestType").append("=").append(requestInfo.getProtocolContext().getRequestType());
sb.append("\nrequestURI").append("=").append(requestInfo.getProtocolContext().getRequestURI());
digest.update(sb.toString().getBytes());
//System.out.println("TESTb: "+sb.toString());
}
if(StatoFunzionalitaCacheDigestQueryParameter.ABILITATO.equals(configHash.getQueryParameters()) ||
StatoFunzionalitaCacheDigestQueryParameter.SELEZIONE_PUNTUALE.equals(configHash.getQueryParameters())) {
// I parametri vengono riordinati proprio per far si differenze nell'ordine non impattano nel digest
sb = new StringBuilder();
sb.append("ParametriURL");
if(StatoFunzionalitaCacheDigestQueryParameter.ABILITATO.equals(configHash.getQueryParameters())) {
this.addList(requestInfo.getProtocolContext().getParameters(), false, sb);
}
else {
Map<String, List<String>> pUrlForDigest = new HashMap<>();
if(requestInfo.getProtocolContext().getParameters()!=null && configHash.sizeQueryParameterList()>0) {
for (String queryParameter : configHash.getQueryParameterList()) {
List<String> v = requestInfo.getProtocolContext().getParameterValues(queryParameter);
if(v!=null && !v.isEmpty()) {
pUrlForDigest.put(queryParameter, v);
}
}
}
if(!pUrlForDigest.isEmpty()) {
this.addList(pUrlForDigest, false, sb);
}
}
digest.update(sb.toString().getBytes());
//System.out.println("TESTb: "+sb.toString());
}
if(StatoFunzionalita.ABILITATO.equals(configHash.getHeaders())) {
// Gli header vengono riordinati e le chiavi vengono prese lowerCase proprio per far si che tali differenze non impattano nel digest
Map<String, List<String>> pTrasportoForDigest = new HashMap<>();
if(requestInfo.getProtocolContext().getHeaders()!=null && configHash.sizeHeaderList()>0) {
for (String header : configHash.getHeaderList()) {
List<String> v = requestInfo.getProtocolContext().getHeaderValues(header);
if(v!=null && !v.isEmpty()) {
pTrasportoForDigest.put(header, v);
}
}
}
if(!pTrasportoForDigest.isEmpty()) {
sb = new StringBuilder("HEADER");
this.addList(pTrasportoForDigest, true, sb);
digest.update(sb.toString().getBytes());
//System.out.println("TESTb: "+sb.toString());
}
}
if(StatoFunzionalita.ABILITATO.equals(configHash.getPayload())) {
boolean doDigest = true;
message.saveChanges();
if(ServiceBinding.REST.equals(message.getServiceBinding())) {
doDigest = message.castAsRest().hasContent();
}
if(doDigest) {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
message.writeTo(bout, false);
bout.flush();
bout.close();
digest.update(bout.toByteArray());
//System.out.println("TESTd: "+bout.toString());
}
}
}
return Base64Utilities.encodeAsString(digest.digest());
}
private void addList(Map<String, List<String>> p, boolean toLowerCase, StringBuilder sb) {
if(p!=null &&
!p.isEmpty()) {
List<String> sortKeys = new ArrayList<>();
Iterator<String> keys = p.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
sortKeys.add(key);
}
Collections.sort(sortKeys);
for (String sortKey : sortKeys) {
List<String> values = TransportUtils.getRawObject(p, sortKey);
List<String> ordinatedValues = new ArrayList<>();
ordinatedValues.addAll(values);
if(ordinatedValues.size()>1) {
Collections.sort(ordinatedValues);
}
String key = sortKey;
if(toLowerCase) {
key = key.toLowerCase();
}
if(ordinatedValues!=null && !ordinatedValues.isEmpty()) {
for (String value : ordinatedValues) {
sb.append("\n").append(key).append("=").append(value);
}
}
}
}
}
}