Validator.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.utils.wadl.validator;

  21. import java.io.InputStream;
  22. import java.net.URI;
  23. import java.util.ArrayList;
  24. import java.util.List;

  25. import javax.ws.rs.core.MultivaluedMap;
  26. import javax.xml.validation.Schema;

  27. import org.jvnet.ws.wadl.ast.AbstractNode;
  28. import org.jvnet.ws.wadl.ast.FaultNode;
  29. import org.jvnet.ws.wadl.ast.MethodNode;
  30. import org.jvnet.ws.wadl.ast.RepresentationNode;
  31. import org.jvnet.ws.wadl.ast.ResourceNode;
  32. import org.openspcoop2.utils.rest.AbstractApiValidator;
  33. import org.openspcoop2.utils.rest.ApiParameterType;
  34. import org.openspcoop2.utils.rest.ApiValidatorConfig;
  35. import org.openspcoop2.utils.rest.IApiValidator;
  36. import org.openspcoop2.utils.rest.ProcessingException;
  37. import org.openspcoop2.utils.rest.ValidatorException;
  38. import org.openspcoop2.utils.rest.api.Api;
  39. import org.openspcoop2.utils.rest.api.ApiOperation;
  40. import org.openspcoop2.utils.rest.api.ApiSchemaTypeRestriction;
  41. import org.openspcoop2.utils.rest.entity.HttpBaseEntity;
  42. import org.openspcoop2.utils.rest.entity.HttpBaseRequestEntity;
  43. import org.openspcoop2.utils.rest.entity.HttpBaseResponseEntity;
  44. import org.openspcoop2.utils.wadl.ApplicationWrapper;
  45. import org.openspcoop2.utils.wadl.WADLApi;
  46. import org.openspcoop2.utils.wadl.WADLException;
  47. import org.openspcoop2.utils.wadl.WADLUtilities;
  48. import org.openspcoop2.utils.xml.AbstractValidatoreXSD;
  49. import org.openspcoop2.utils.xml.AbstractXMLUtils;
  50. import org.openspcoop2.utils.xml.ValidatoreXSD;
  51. import org.slf4j.Logger;
  52. import org.w3c.dom.Document;
  53. import org.w3c.dom.Element;
  54. import org.w3c.dom.Node;

  55. /**
  56.  * Validator
  57.  *
  58.  *
  59.  * @author Poli Andrea (apoli@link.it)
  60.  * @author $Author$
  61.  * @version $Rev$, $Date$
  62.  */
  63. public class Validator extends AbstractApiValidator implements IApiValidator {

  64.     private WADLApi wadlApi;
  65.     private ApplicationWrapper application;
  66.     private Logger log;
  67.     private AbstractXMLUtils xmlUtils;
  68.     private Schema schema;
  69.     private boolean initialize = false;
  70.        
  71.    
  72.     @Override
  73.     public void init(Logger log, Api api, ApiValidatorConfig config) throws WADLException {
  74.         try{
  75.             this.wadlApi = (WADLApi) api;
  76.             this.log = log;
  77.             this.application = this.wadlApi.getApplicationWadlWrapper();
  78.             this.xmlUtils = config.getXmlUtils();
  79.            
  80.             // Costruisco l'eventuale schema XSD necessario per una validazione rispetto a schemi XSD
  81.             if(this.application.getResources().size()>0){
  82.                 this.schema = this.application.buildSchemaCollection(this.log).buildSchema(this.log);
  83.             }
  84.            
  85.             this.initialize = true;
  86.         }catch(Exception e){
  87.             throw new WADLException(e.getMessage(),e);
  88.         }
  89.     }
  90.    
  91.     public Validator(){}
  92.    
  93.     @Override
  94.     public void close(Logger log, Api api, ApiValidatorConfig config) throws ProcessingException{
  95.        
  96.     }
  97.    
  98.     @Override
  99.     public void validate(HttpBaseEntity<?> httpEntity) throws ProcessingException, ValidatorException{
  100.        
  101.         if(!this.initialize){
  102.             throw new WADLException("Validatore non inizializzato");
  103.         }

  104.         List<Object> context = new ArrayList<>();
  105.        
  106.         this.validate(this.wadlApi, httpEntity, context);

  107.     }
  108.    
  109.     @SuppressWarnings("unchecked")
  110.     @Override
  111.     public void validatePreConformanceCheck(HttpBaseEntity<?> httpEntity,ApiOperation operation,Object ... args) throws ProcessingException,ValidatorException{
  112.        
  113.         // Effettuo la validazione del contenuto
  114.        
  115.         // Recupera il resource node corrispondente alla url
  116.         ResourceNode resourceNode = WADLUtilities.findResourceNode(this.application.getApplicationNode(),httpEntity.getUrl());
  117.        
  118.         // Recupera il metodo corrispondente al method invocato
  119.         MethodNode methodNode = WADLUtilities.findMethodNode(resourceNode,httpEntity.getMethod());
  120.        
  121.         // Aggiungo in context
  122.         ((List<Object>)args[0]).add(resourceNode);
  123.         ((List<Object>)args[0]).add(methodNode);
  124.        
  125.         this.validateAgainstXSDSchema(httpEntity, resourceNode, methodNode);
  126.     }
  127.    
  128.     @SuppressWarnings("unchecked")
  129.     @Override
  130.     public void validatePostConformanceCheck(HttpBaseEntity<?> httpEntity,ApiOperation operation,Object ... args) throws ProcessingException,ValidatorException{
  131.        
  132.         // Verifico che un eventuale contenuto che rispetta lo schema, sia esattamente anche quello atteso per l'operazione
  133.        
  134.         ResourceNode resourceNode = (ResourceNode) ((List<Object>)args[0]).get(0);
  135.         MethodNode methodNode = (MethodNode) ((List<Object>)args[0]).get(1);
  136.        
  137.         this.wadlConformanceCheck(httpEntity, resourceNode, methodNode);
  138.     }
  139.    
  140.     @Override
  141.     public void validateValueAsType(ApiParameterType parameterType, String value,String type, ApiSchemaTypeRestriction typeRestriction) throws ProcessingException,ValidatorException{
  142.        
  143.         // Tipi XSD : {http://www.w3.org/2001/XMLSchema}string
  144.         if(type.startsWith("{http://www.w3.org/2001/XMLSchema}")){
  145.             String tipoEffettivo = type.substring("{http://www.w3.org/2001/XMLSchema}".length());
  146.             if(tipoEffettivo!=null){
  147.                 tipoEffettivo = tipoEffettivo.trim();
  148.                
  149.                 if("byte".equalsIgnoreCase(tipoEffettivo) || "unsignedByte".equalsIgnoreCase(tipoEffettivo)){
  150.                     try{
  151.                         Byte.parseByte(value);
  152.                     }catch(Exception e){
  153.                         throw new ValidatorException(e.getMessage(),e);
  154.                     }
  155.                 }
  156.                 else if("char".equalsIgnoreCase(tipoEffettivo)){
  157.                     if(value.length()>1){
  158.                         throw new ValidatorException("More than one character");
  159.                     }
  160.                 }
  161.                 else if("double".equalsIgnoreCase(tipoEffettivo) || "decimal".equalsIgnoreCase(tipoEffettivo)){
  162.                     try{
  163.                         Double.parseDouble(value);
  164.                     }catch(Exception e){
  165.                         throw new ValidatorException(e.getMessage(),e);
  166.                     }
  167.                 }
  168.                 else if("float".equalsIgnoreCase(tipoEffettivo)){
  169.                     try{
  170.                         Float.parseFloat(value);
  171.                     }catch(Exception e){
  172.                         throw new ValidatorException(e.getMessage(),e);
  173.                     }
  174.                 }
  175.                 else if("int".equalsIgnoreCase(tipoEffettivo) || "integer".equalsIgnoreCase(tipoEffettivo) ||
  176.                         "positiveInteger".equalsIgnoreCase(tipoEffettivo) || "negativeInteger".equalsIgnoreCase(tipoEffettivo) ||
  177.                         "nonPositiveInteger".equalsIgnoreCase(tipoEffettivo) || "nonNegativeInteger".equalsIgnoreCase(tipoEffettivo) ||
  178.                         "unsignedInt".equalsIgnoreCase(tipoEffettivo)){
  179.                     try{
  180.                         int i = Integer.parseInt(value);
  181.                         if("positiveInteger".equalsIgnoreCase(tipoEffettivo)){
  182.                             if(i<=0){
  183.                                 throw new ValidatorException("Expected a positive value");
  184.                             }
  185.                         }
  186.                         else if("nonNegativeInteger".equalsIgnoreCase(tipoEffettivo)){
  187.                             if(i<0){
  188.                                 throw new ValidatorException("Expected a non negative value");
  189.                             }
  190.                         }
  191.                         else if("negativeInteger".equalsIgnoreCase(tipoEffettivo)){
  192.                             if(i>=0){
  193.                                 throw new ValidatorException("Expected a negative value");
  194.                             }
  195.                         }
  196.                         else if("nonPositiveInteger".equalsIgnoreCase(tipoEffettivo)){
  197.                             if(i>0){
  198.                                 throw new ValidatorException("Expected a non positive value");
  199.                             }
  200.                         }
  201.                         else if("unsignedInt".equalsIgnoreCase(tipoEffettivo)){
  202.                             if(i<0){
  203.                                 throw new ValidatorException("Expected a unsigned value");
  204.                             }
  205.                         }
  206.                     }catch(Exception e){
  207.                         throw new ValidatorException(e.getMessage(),e);
  208.                     }
  209.                 }
  210.                 else if("long".equalsIgnoreCase(tipoEffettivo) || "unsignedLong".equalsIgnoreCase(tipoEffettivo)){
  211.                     try{
  212.                         long l = Long.parseLong(value);
  213.                         if("unsignedLong".equalsIgnoreCase(tipoEffettivo)){
  214.                             if(l<0){
  215.                                 throw new ValidatorException("Expected a unsigned value");
  216.                             }
  217.                         }
  218.                     }catch(Exception e){
  219.                         throw new ValidatorException(e.getMessage(),e);
  220.                     }
  221.                 }
  222.                 else if("short".equalsIgnoreCase(tipoEffettivo) || "unsignedShort".equalsIgnoreCase(tipoEffettivo)){
  223.                     try{
  224.                         short s = Short.parseShort(value);
  225.                         if("unsignedShort".equalsIgnoreCase(tipoEffettivo)){
  226.                             if(s<0){
  227.                                 throw new ValidatorException("Expected a unsigned value");
  228.                             }
  229.                         }
  230.                     }catch(Exception e){
  231.                         throw new ValidatorException(e.getMessage(),e);
  232.                     }
  233.                 }
  234.                 else if("boolean".equalsIgnoreCase(tipoEffettivo)){
  235.                     try{
  236.                         Boolean.parseBoolean(value);
  237.                     }catch(Exception e){
  238.                         throw new ValidatorException(e.getMessage(),e);
  239.                     }
  240.                 }
  241.                 else if("anyURI".equalsIgnoreCase(tipoEffettivo)){
  242.                     try{
  243.                         new URI(value);
  244.                     }catch(Exception e){
  245.                         throw new ValidatorException(e.getMessage(),e);
  246.                     }
  247.                 }
  248.             }
  249.            
  250.         }
  251.        
  252.         // altri tipi non li valido per ora
  253.        
  254.     }
  255.    
  256.    
  257.    
  258.     private void validateAgainstXSDSchema(HttpBaseEntity<?> httpEntity,ResourceNode resourceNode,MethodNode methodNode) throws WADLException,WADLValidatorException{

  259.         if(this.schema!=null){
  260.        
  261.             try{
  262.            
  263.                 AbstractValidatoreXSD validatore = null;
  264.                 try{
  265.                     validatore = new ValidatoreXSD(this.schema);
  266.    
  267.                 }catch(Exception e){
  268.                     throw new WADLException("Riscontrato errore durante la costruzione del validatore XSD per il contenuto applicativo: "+e.getMessage(),e);
  269.                 }
  270.                
  271.                 javax.xml.namespace.QName elementAtteso = null;
  272.                
  273.                 AbstractNode nodeXML = getNode(httpEntity, methodNode);
  274.                
  275.                 if(nodeXML != null) {
  276.                     if(nodeXML instanceof RepresentationNode) {
  277.                         elementAtteso = ((RepresentationNode)nodeXML).getElement();
  278.                     } else if(nodeXML instanceof FaultNode) {
  279.                         elementAtteso = ((FaultNode)nodeXML).getElement();
  280.                     }  
  281.                 }
  282.                
  283.                 if(elementAtteso!=null){
  284.                     // Devo effettivamente effettuare la validazione XSD
  285.                    
  286.                     Node node = this.readNode(elementAtteso, httpEntity);
  287.                    
  288.                     String nomeElemento = null;
  289.                     try{
  290.                         nomeElemento = node.getLocalName();
  291.                         validatore.valida(node,true);
  292.                     }catch(Exception e){
  293.                         StringBuilder errorMsgValidazioneXSD = new StringBuilder();
  294.                         errorMsgValidazioneXSD.append("validation failed");
  295.                         errorMsgValidazioneXSD.append(" (element "+nomeElemento+"): "+e.getMessage());
  296.                         String elementNonValidato = null;
  297.                         try{
  298.                             elementNonValidato = this.xmlUtils.toString(node);
  299.                         }catch(Exception eString){
  300.                             this.log.error("Errore durante la conversione del Node in String: "+eString.getMessage(),eString);
  301.                         }
  302.                         this.log.error("Validazione fallita (elemento "+nomeElemento+") ["+elementNonValidato+"]: "+e.getMessage(),e);
  303.                         throw new WADLValidatorException(errorMsgValidazioneXSD.toString(),e);
  304.                     }
  305.    
  306.                 }
  307.             }catch(WADLException e){
  308.                 throw e;
  309.             }catch(WADLValidatorException e){
  310.                 throw e;
  311.             }catch(Exception e){
  312.                 throw new WADLException(e.getMessage(),e);
  313.             }
  314.         }
  315.        
  316.     }
  317.    
  318.     private AbstractNode getNode(HttpBaseEntity<?> httpEntity, MethodNode methodNode)throws WADLException,WADLValidatorException{
  319.         // Navigare il methodNode secondo la seguente specifica:
  320.         // Se siamo nella richiesta (httpEntity instanceof HttpRequestEntity)
  321.         //    verificare se esiste un input con mediaType 'application/xml (o text/xml)'
  322.         //    se esiste recuperare l'elementNameAtteso
  323.         // else se siamo nella risposta (httpEntity instanceof HttpResponseEntity)
  324.         //    verificare se esiste un output o un fault con mediaType 'application/xml (o text/xml)' e status uguale a quello presente nella risposta http
  325.         //    se esiste recuperare l'elementNameAtteso

  326.         if(httpEntity instanceof HttpBaseRequestEntity<?>) {
  327.             if(methodNode.getSupportedInputs() != null) {
  328.                 for(RepresentationNode input: methodNode.getSupportedInputs()) {
  329.                     if(input.getMediaType().equals("application/xml") || input.getMediaType().equals("text/xml")) {
  330.                         return input;
  331.                     }
  332.                 }
  333.             }
  334.         } else if(httpEntity instanceof HttpBaseResponseEntity<?>) {
  335.             int status = ((HttpBaseResponseEntity<?>)httpEntity).getStatus();
  336.            
  337.             List<RepresentationNode> lstOutputs = getSupportedOutputs(methodNode.getSupportedOutputs(), status);

  338.             if(lstOutputs != null) {
  339.                 for(RepresentationNode output: lstOutputs) {
  340.                     if(output.getMediaType().equals("application/xml") || output.getMediaType().equals("text/xml")) {
  341.                         return output;
  342.                     }
  343.                 }
  344.             }

  345.             List<FaultNode> lstFaults = getSupportedFaults(methodNode.getFaults(), status);
  346.             if(lstFaults != null) {
  347.                 for(FaultNode fault: lstFaults) {
  348.                     if(fault.getMediaType().equals("application/xml") || fault.getMediaType().equals("text/xml")) {
  349.                         return fault;
  350.                     }
  351.                 }
  352.             }
  353.            
  354.             lstOutputs = methodNode.getSupportedOutputs().get(new ArrayList<Long>());

  355.             if(lstOutputs != null) {
  356.                 for(RepresentationNode output: lstOutputs) {
  357.                     if(output.getMediaType().equals("application/xml") || output.getMediaType().equals("text/xml")) {
  358.                         return output;
  359.                     }
  360.                 }
  361.             }

  362.            
  363.         }
  364.        
  365.         return null;

  366.     }
  367.    
  368.     private List<RepresentationNode> getSupportedOutputs(MultivaluedMap<List<Long>, RepresentationNode> outputs,
  369.             int status) {
  370.         for(List<Long> lst: outputs.keySet()) {
  371.             for(Long longValue: lst) {
  372.                 if(longValue.intValue() == status)
  373.                     return outputs.get(lst);
  374.             }
  375.         }
  376.         return null;
  377.     }
  378.    
  379.     private List<FaultNode> getSupportedFaults(MultivaluedMap<List<Long>, FaultNode> faults,
  380.             int status) {
  381.         for(List<Long> lst: faults.keySet()) {
  382.             for(Long longValue: lst) {
  383.                 if(longValue.intValue() == status)
  384.                     return faults.get(lst);
  385.             }
  386.         }
  387.         return null;
  388.     }
  389.    
  390.     private void wadlConformanceCheck(HttpBaseEntity<?> httpEntity,ResourceNode resourceNode,MethodNode methodNode) throws WADLException,WADLValidatorException{

  391.         try{
  392.            
  393.             javax.xml.namespace.QName elementAtteso = null;

  394.             // ... altri vedere un po ....

  395.             AbstractNode nodeXML = getNode(httpEntity, methodNode);

  396.             if(nodeXML != null) {
  397.                 if(nodeXML instanceof RepresentationNode) {
  398.                     RepresentationNode representationNode = (RepresentationNode)nodeXML;
  399.                     elementAtteso = representationNode.getElement();
  400.                 } else if(nodeXML instanceof FaultNode) {
  401.                     FaultNode faultNode = (FaultNode)nodeXML;
  402.                     elementAtteso = faultNode.getElement();
  403.                 }  
  404.             }
  405.            
  406.            
  407.             if(elementAtteso!=null){
  408.                
  409.                 // Verifica rootNode
  410.                 Node node = this.readNode(elementAtteso, httpEntity);
  411.                 if(node.getLocalName()==null){
  412.                     throw new WADLValidatorException("Verifica presenza element ["+elementAtteso+"] fallita. Element presente nel payload http non contiene un local-name?");
  413.                 }
  414.                 if(node.getNamespaceURI()==null){
  415.                     throw new WADLValidatorException("Verifica presenza element ["+elementAtteso+"] fallita. Element presente nel payload http non contiene un namespace?");
  416.                 }
  417.                 if(node.getLocalName().equals(elementAtteso.getLocalPart())==false){
  418.                     throw new WADLValidatorException("Verifica presenza element ["+elementAtteso+"] fallita. Element presente nel payload http contiene un local-name ["+node.getLocalName()+"] differente da quello atteso ["+elementAtteso.getLocalPart()+"]");
  419.                 }
  420.                 if(node.getNamespaceURI().equals(elementAtteso.getNamespaceURI())==false){
  421.                     throw new WADLValidatorException("Verifica presenza element ["+elementAtteso+"] fallita. Element presente nel payload http contiene un namespace ["+node.getNamespaceURI()+"] differente da quello atteso ["+elementAtteso.getNamespaceURI()+"]");
  422.                 }
  423.             }

  424.         }catch(WADLException e){
  425.             throw e;
  426.         }catch(WADLValidatorException e){
  427.             throw e;
  428.         }catch(Exception e){
  429.             throw new WADLException(e.getMessage(),e);
  430.         }
  431.        
  432.     }

  433.     private Node readNode(javax.xml.namespace.QName elementAtteso, HttpBaseEntity<?> httpEntity) throws WADLValidatorException, WADLException{
  434.         Node node = null;
  435.         try{
  436.             if(httpEntity.getContent()==null){
  437.                 throw new WADLValidatorException("Verifica presenza element ["+elementAtteso+"] fallita. Non è stato riscontrato alcun dato nel payload http");
  438.             }
  439.             Object content = httpEntity.getContent();
  440.            
  441.             if(content instanceof Element){
  442.                 node = ((Element) content);
  443.             }
  444.             else if(content instanceof Document){
  445.                 node = ((Document) content).getDocumentElement();
  446.             }
  447.             else if(content instanceof byte[]){
  448.                 byte [] bytes = (byte[]) content;
  449.                 node = this.xmlUtils.newDocument(bytes);
  450.             }
  451.             else if(content instanceof String){
  452.                 byte [] bytes = ((String)content).getBytes();
  453.                 node = this.xmlUtils.newDocument(bytes);
  454.             }
  455.             else if(content instanceof InputStream){
  456.                 InputStream is = (InputStream) content;
  457.                 node = this.xmlUtils.newDocument(is);
  458.             }
  459.             else{
  460.                 throw new WADLException("HttpBaseEntity ["+httpEntity.getClass().getName()+"] non gestita");
  461.             }
  462.            
  463.             return node;
  464.            
  465.            
  466.         }catch(WADLException e){
  467.             throw e;
  468.         }catch(WADLValidatorException e){
  469.             throw e;
  470.         }catch(Exception e){
  471.             throw new WADLValidatorException("Verifica presenza element ["+elementAtteso+"] fallita. Non è stato riscontrato nel payload http dei dati che contengano una struttura xml valida, "+e.getMessage(),e);
  472.         }
  473.     }

  474. }