Validatore.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.web.lib.mvc.security;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Entities;
import org.jsoup.safety.Safelist;
import org.openspcoop2.web.lib.mvc.security.exception.ValidationException;
import org.slf4j.Logger;
import jakarta.servlet.http.HttpServletRequest;
/**
* Validatore
*
* @author Giuliano Pintori (pintori@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class Validatore {
private static Logger log;
private SecurityProperties sc;
private InputSanitizerProperties isp;
private static Validatore instance;
private Safelist safelist;
public static synchronized void init(SecurityProperties sc, InputSanitizerProperties isp, Logger log) {
if(Validatore.log == null) {
Validatore.log = log;
}
if(Validatore.instance == null) {
Validatore.instance = new Validatore(sc, isp);
}
}
public static synchronized Validatore getInstance() {
return instance;
}
private Validatore(SecurityProperties sc, InputSanitizerProperties isp) {
this.sc = sc;
this.isp = isp;
this.safelist = this.isp.getSafelist();
}
public String validate(String oggetto, String valore, boolean nullable, String... pattern) throws ValidationException {
return validate(oggetto, valore, nullable, true, pattern);
}
public String validate(String oggetto, String valore, boolean nullable, boolean checkSqlInjection, String... pattern) throws ValidationException {
return validate(oggetto, valore, Integer.valueOf(0), null, nullable, checkSqlInjection, pattern);
}
public String validate(String oggetto, String valore, Integer maxLength, boolean nullable, String... pattern) throws ValidationException {
return validate(oggetto, valore, maxLength, nullable, true, pattern);
}
public String validate(String oggetto, String valore, Integer maxLength, boolean nullable, boolean checkSqlInjection, String... pattern) throws ValidationException {
return validate(oggetto, valore, Integer.valueOf(0), maxLength, nullable, checkSqlInjection, pattern);
}
public String validate(String oggetto, String valore, Integer minLength, Integer maxLength, boolean nullable, boolean checkSqlInjection, String... pattern) throws ValidationException {
List<Pattern> patternsToCheck = new ArrayList<>();
for (int i = 0; i < pattern.length; i++) {
String pt = pattern[i];
Pattern p = this.sc.getValidationPattern(pt);
if ( p != null ) {
patternsToCheck.add(p);
} else {
throw new ValidationException("Non e' stata trovato un pattern di validazione per il tipo [" + StringUtils.join(pattern,",") + "].");
}
}
// check stringa vuota/null
checkEmpty(oggetto, valore, nullable);
// check lunghezza min/max
checkLength(oggetto, valore, minLength, maxLength);
// check regexpr
checkPatterns(oggetto, valore, patternsToCheck);
// check sqlInjection
if(checkSqlInjection) {
checkSqlInjection(oggetto, valore, this.sc.getValidationPattern(Costanti.PATTERN_SQL_INJECTION));
}
return valore;
}
public String validateTabId(String idTab) {
return validateTabId("IdTab", idTab);
}
public String validatePrevTabId(String idTab) {
return validateTabId("PrevIdTab", idTab);
}
private String validateTabId(String oggetto, String idTab) {
if(idTab != null) {
try {
return this.validate(oggetto, idTab, Integer.valueOf(36), false, true, org.openspcoop2.web.lib.mvc.security.Costanti.PATTERN_ID_TAB);
} catch(ValidationException e) {
log.warn("Valore ["+idTab+"] ricevuto per il parametro ["+oggetto+"] non valido: " + e.getMessage(),e);
}
}
return null;
}
/***
* Controllo se l'input e' vuoto
*
* @param oggetto
* @param valore
* @param nullable
* @return
* @throws ValidationException
*/
private String checkEmpty(String oggetto, String valore, boolean nullable) throws ValidationException
{
// se e' nullable il controllo non si deve fare
if(nullable) return valore;
if(!StringUtils.isEmpty(valore))
return valore;
throw new ValidationException(oggetto + " non puo' essere vuoto o null.");
}
/***
* Controllo sulla lunghezza dell'input
*
* @param oggetto
* @param valore
* @param minLength
* @param maxLength
* @return
* @throws ValidationException
*/
private String checkLength(String oggetto, String valore, Integer minLength, Integer maxLength) throws ValidationException
{
if(minLength != null &&
valore.length() < minLength) {
throw new ValidationException( oggetto + " non rispetta la lunghezza minima prevista di " + minLength + " caratteri.");
}
if(maxLength != null &&
valore.length() > maxLength) {
throw new ValidationException( oggetto + " non rispetta la lunghezza massima prevista di " + maxLength + " caratteri.");
}
return valore;
}
/***
* Validazione dell'input secondo i pattern previsti.
*
* @param oggetto
* @param valore
* @param patternsToCheck
* @return
* @throws ValidationException
*/
private String checkPatterns(String oggetto, String valore, List<Pattern> patternsToCheck) throws ValidationException
{
for (Pattern p : patternsToCheck) {
if ( !p.matcher(valore).matches() ) {
throw new ValidationException( oggetto + " non rispetta il pattern di validazione previsto [" + p.pattern() + "].");
}
}
return valore;
}
/***
* Validazione dell'input secondo i pattern previsti.
*
* @param oggetto
* @param valore
* @param pattern
* @return
* @throws ValidationException
*/
private String checkSqlInjection(String oggetto, String valore, Pattern pattern) throws ValidationException
{
if (pattern.matcher(valore).find()) {
throw new ValidationException( oggetto + " non rispetta il pattern di validazione previsto [" + pattern.pattern() + "].");
}
return valore;
}
public boolean verificaEsistenzaParametroOriginale(HttpServletRequest request, String parametro) {
return this.getParametroOriginale(request, parametro) != null;
}
public String getParametroOriginale(HttpServletRequest request, String parametro) {
if(request instanceof SecurityWrappedHttpServletRequest secReq) {
return secReq.getOriginalParameter(parametro);
}
return request.getParameter(parametro);
}
public String getParametroSanificato(String originalValue, boolean usaValidazioneTextArea) {
if(originalValue == null) {
return null;
}
Document.OutputSettings os = new Document.OutputSettings().prettyPrint(false);
if(usaValidazioneTextArea) {
// pulizia del contenuto della textarea
String cleaned = Jsoup.clean(originalValue, "", this.safelist, os);
// inserisco una root fittizia, così non viene toccato il body radice del Document
Document tmp = Jsoup.parse("<x-root>" + cleaned + "</x-root>");
tmp.outputSettings(os);
// rimuovo il body di primo livello, se presente
Element firstTopBody = tmp.selectFirst("#__root > body");
if (firstTopBody != null) firstTopBody.unwrap();
String result = tmp.selectFirst("x-root").html();
// unascape delle entità HTML
return Entities.unescape(result);
}
return Entities.unescape(Jsoup.parse(Jsoup.clean(originalValue, "", this.safelist, os)).body().html());
}
}