ApiUtilities.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.rest.api;

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

  25. import org.openspcoop2.utils.beans.BaseBean;
  26. import org.openspcoop2.utils.rest.ProcessingException;
  27. import org.openspcoop2.utils.rest.ValidatorException;
  28. import org.openspcoop2.utils.transport.http.HttpRequestMethod;

  29. /**
  30.  * ApiOperation
  31.  *
  32.  *
  33.  * @author Poli Andrea (apoli@link.it)
  34.  * @author $Author$
  35.  * @version $Rev$, $Date$
  36.  */
  37. public class ApiUtilities extends BaseBean {

  38.     private static final long serialVersionUID = 1L;
  39.    
  40.     private static List<String> _static_list_characters = new ArrayList<>();
  41.     static {
  42.         for (int i = 'A'; i <= 'Z'; i++) {
  43.             _static_list_characters.add(((char)i)+"");
  44.         }
  45.         for (int i = 'a'; i <= 'z'; i++) {
  46.             _static_list_characters.add(((char)i)+"");
  47.         }
  48.         String chars = ".-_*;:";
  49.         char[] cs = chars.toCharArray();
  50.         for (int i = 0; i < cs.length; i++) {
  51.             _static_list_characters.add(cs[i]+"");
  52.         }
  53.     }
  54.    
  55.     public static void validatePath(String path) throws ValidatorException {
  56.         if(path.contains("{") || path.contains("}") ) {
  57.            
  58.             if(path.contains("{") && !path.contains("}") ) {
  59.                 throw new ValidatorException("Dynamic path '{' without closing '}' ") ;
  60.             }
  61.             if(!path.contains("{") && path.contains("}") ) {
  62.                 throw new ValidatorException("Dynamic path '}' without opening '{' ") ;
  63.             }
  64.             int countOpen = 0;
  65.             int countClose = 0;
  66.             boolean open = false;
  67.             for (int i = 0; i < path.length(); i++) {
  68.                 String charAt = path.charAt(i)+"";
  69.                 if(charAt.equals("{")) {
  70.                     countOpen++;
  71.                     open = true;
  72.                 }
  73.                 else if(charAt.equals("}")) {
  74.                     if(!open) {
  75.                         throw new ValidatorException("Dynamic path malformed; found '}' before '{'") ;
  76.                     }
  77.                     open = false;
  78.                     countClose++;
  79.                 }
  80.             }
  81.             if(countOpen != countClose) {
  82.                 throw new ValidatorException("Dynamic path malformed; found "+countOpen+" '{' and "+countClose+" closing '}' ") ;
  83.             }
  84.            
  85.             String specialCharStart = null;
  86.             String specialCharEnd = null;
  87.             for (String check : _static_list_characters) {
  88.                 if(path.contains(check)==false) {
  89.                     if(specialCharStart==null) {
  90.                         specialCharStart = check;
  91.                     }
  92.                     else if(specialCharEnd==null) {
  93.                         specialCharEnd = check;
  94.                         break;
  95.                     }
  96.                 }
  97.             }
  98.             String pathConverted = new String(path);
  99.             while(pathConverted.contains("{")){
  100.                 pathConverted = pathConverted.replace("{", specialCharStart);
  101.             }
  102.             while(pathConverted.contains("}")){
  103.                 pathConverted = pathConverted.replace("}", specialCharStart);
  104.             }
  105.            
  106. //          System.out.println("SPECIAL START: "+specialCharStart);
  107. //          System.out.println("SPECIAL END: "+specialCharEnd);
  108. //          System.out.println("SPECIAL pathConverted: "+pathConverted);
  109.            
  110.             try {
  111.                 URI.create(pathConverted);
  112.                 //System.out.println("VALIDATE PATH ("+path+")");
  113.             }catch(Exception e) {
  114.                 //System.out.println("ERRORE ORIGINALE: "+e.getMessage());
  115.                 String msg = e.getMessage();
  116.                 while(msg.contains(pathConverted)) {
  117.                     msg = msg.replace(pathConverted, path);
  118.                 }
  119.                 //System.out.println("ERRORE CONVERTITO: "+msg);
  120.                 throw new ValidatorException("Dynamic path malformed; "+msg,e);
  121.             }
  122.         }
  123.         else {
  124.             try {
  125.                 URI.create(path);
  126.             }catch(Exception e) {
  127.                 throw new ValidatorException("Path malformed; "+e.getMessage(),e);
  128.             }
  129.         }
  130.     }
  131.    
  132.    
  133.     public static ApiOperation findOperation(Api api, HttpRequestMethod httpMethod, String url, boolean exactlyLength) throws ProcessingException{

  134.         String[] urlList = extractUrlList(api.getBaseURL(), url);

  135.         return getOperation(urlList, api, httpMethod, exactlyLength);
  136.     }
  137.     public static String[] extractUrlList(URL baseURI, String url) throws ProcessingException{
  138.         if(url == null)
  139.             throw new ProcessingException("URL non fornita");

  140.         List<String> urlList = new ArrayList<>();

  141.         if(baseURI != null) {
  142.             if(url.startsWith(baseURI.toString())) {
  143.                 url = url.substring(baseURI.toString().length(), url.length());
  144.             }
  145.         }

  146.         for(String s : url.split("/")) {
  147.             if(s!=null && !s.equals("")) {
  148.                 urlList.add(s);
  149.             }
  150.         }

  151.         return urlList.toArray(new String[] {});

  152.     }
  153.    
  154.     private static ApiOperation getOperation(List<ApiOperation> list, String[] url, HttpRequestMethod httpMethod, boolean exactlyLength) throws ProcessingException{
  155.         int levelExactlyMatch = -1;
  156.         int levelDinamicMatch = -1;
  157.         ApiOperation apiOpExactlyMatch = null;
  158.         ApiOperation apiOpDynamicMatch = null;
  159.         for(int i = 0; i< list.size(); i++) {
  160.            
  161.             ApiOperation apiOp = list.get(i);
  162.            
  163.             if(apiOp.getHttpMethod()!=null) {
  164.                 if(!apiOp.getHttpMethod().equals(httpMethod)){
  165.                     continue;
  166.                 }
  167.             }
  168.             if(url.length<apiOp.sizePath()){
  169.                 continue;
  170.             }
  171.             int counterMatch = 0;
  172.             boolean exactlyMatch = true;
  173.             boolean dynamicMatch = true;
  174.             for (int j = 0; j < apiOp.sizePath(); j++) {
  175.                 String path = null;
  176.                 try{
  177.                     path = apiOp.getPath(j);
  178.                 }catch(ProcessingException pe){}
  179.                 if(path!=null && !path.equalsIgnoreCase(url[j])){
  180.                     exactlyMatch = false;
  181.                     if(!apiOp.isDynamicPath(j)){
  182.                         dynamicMatch = false;
  183.                         break;
  184.                     }
  185.                 }
  186.                 counterMatch++;
  187.             }
  188.             if(exactlyMatch){
  189.                 if(counterMatch>levelExactlyMatch){
  190.                     levelExactlyMatch=counterMatch;
  191.                     apiOpExactlyMatch = apiOp;
  192.                 }
  193.             }
  194.             else if(dynamicMatch){
  195.                 // dynamic
  196.                 if(counterMatch>levelDinamicMatch){
  197.                     levelDinamicMatch=counterMatch;
  198.                     apiOpDynamicMatch = apiOp;
  199.                 }
  200.             }
  201.            
  202.         }
  203.        
  204.         if(exactlyLength){
  205.             if(levelExactlyMatch==url.length){
  206.                 return apiOpExactlyMatch;
  207.             }
  208.             else if(levelDinamicMatch==url.length){
  209.                 return apiOpDynamicMatch;
  210.             }
  211.             else{
  212.                 return null;
  213.             }
  214.         }
  215.         else{
  216.             if(levelExactlyMatch>levelDinamicMatch){
  217.                 return apiOpExactlyMatch;
  218.             }
  219.             else{
  220.                 return apiOpDynamicMatch;
  221.             }
  222.         }
  223.     }
  224.    
  225.     private static ApiOperation getOperation(String[] url, Api api, HttpRequestMethod httpMethod, boolean exactlyLength) throws ProcessingException{

  226.         // Prima cerco operazioni con method specifico e path specifico
  227.         // prima della versione 3.3.0.p2: L'interfaccia non permette la creazione di risorse con un metodo preciso e qualsiasi path
  228.         // dopo la versione 3.3.0.p2: l'interfaccia consente la creazione di risorse con un metodo preciso e qualsiasi path ma controlla che non vi siano casi che possano essere inclusi in più path
  229.         List<ApiOperation> listMethodAndPath = new ArrayList<>();
  230.         List<ApiOperation> listQualsiasiMethodAndPath = new ArrayList<>();
  231.         List<ApiOperation> listMethodAndQualsiasiPath = new ArrayList<>();
  232.         List<ApiOperation> listQualsiasi = new ArrayList<>();
  233.         for (ApiOperation apiOperation : api.getOperations()) {
  234.             if(apiOperation.getHttpMethod()!=null && apiOperation.getPath()!=null) {
  235.                 listMethodAndPath.add(apiOperation);
  236.             }
  237.             else if(apiOperation.getPath()!=null) {
  238.                 listQualsiasiMethodAndPath.add(apiOperation);
  239.             }
  240.             else if(apiOperation.getHttpMethod()!=null) {
  241.                 if(apiOperation.getHttpMethod().equals(httpMethod)){
  242.                     listMethodAndQualsiasiPath.add(apiOperation);
  243.                 }
  244.             }
  245.             else {
  246.                 listQualsiasi.add(apiOperation);
  247.             }
  248.         }
  249.        
  250.         ApiOperation op = getOperation(listMethodAndPath, url, httpMethod, exactlyLength);
  251.         if(op==null) {
  252.             op = getOperation(listQualsiasiMethodAndPath, url, httpMethod, exactlyLength);
  253.         }
  254.        
  255.         if(op!=null) {
  256.             return op;
  257.         }
  258.        
  259.         if(listMethodAndQualsiasiPath.size()>0 && listQualsiasi.size()>0) {
  260.             throw new ProcessingException("Found more resource with path '*' (both httpMethod that '*')");
  261.         }
  262.        
  263.         if(listMethodAndQualsiasiPath.size()>0) {
  264.             if(listMethodAndQualsiasiPath.size()>1) {
  265.                 throw new ProcessingException("Found more resource with httpMethod '"+listMethodAndQualsiasiPath.get(0).getHttpMethod()+"' and path '*'");
  266.             }
  267.             else if(listMethodAndQualsiasiPath.size()==1) {
  268.                 return  listMethodAndQualsiasiPath.get(0);
  269.             }
  270.         }
  271.        
  272.         if(listQualsiasi.size()>0) {
  273.             if(listQualsiasi.size()>1) {
  274.                 throw new ProcessingException("Found more resource with httpMethod '*' and path '*'");
  275.             }
  276.             else if(listQualsiasi.size()==1) {
  277.                 return  listQualsiasi.get(0);
  278.             }
  279.         }
  280.        
  281.         return null;
  282.     }
  283.    
  284. }