ApiUtilities.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.utils.rest.api;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.openspcoop2.utils.beans.BaseBean;
import org.openspcoop2.utils.rest.ProcessingException;
import org.openspcoop2.utils.rest.ValidatorException;
import org.openspcoop2.utils.transport.http.HttpRequestMethod;
/**
* ApiOperation
*
*
* @author Poli Andrea (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class ApiUtilities extends BaseBean {
private static final long serialVersionUID = 1L;
private static List<String> _static_list_characters = new ArrayList<>();
static {
for (int i = 'A'; i <= 'Z'; i++) {
_static_list_characters.add(((char)i)+"");
}
for (int i = 'a'; i <= 'z'; i++) {
_static_list_characters.add(((char)i)+"");
}
String chars = ".-_*;:";
char[] cs = chars.toCharArray();
for (int i = 0; i < cs.length; i++) {
_static_list_characters.add(cs[i]+"");
}
}
public static void validatePath(String path) throws ValidatorException {
if(path.contains("{") || path.contains("}") ) {
if(path.contains("{") && !path.contains("}") ) {
throw new ValidatorException("Dynamic path '{' without closing '}' ") ;
}
if(!path.contains("{") && path.contains("}") ) {
throw new ValidatorException("Dynamic path '}' without opening '{' ") ;
}
int countOpen = 0;
int countClose = 0;
boolean open = false;
for (int i = 0; i < path.length(); i++) {
String charAt = path.charAt(i)+"";
if(charAt.equals("{")) {
countOpen++;
open = true;
}
else if(charAt.equals("}")) {
if(!open) {
throw new ValidatorException("Dynamic path malformed; found '}' before '{'") ;
}
open = false;
countClose++;
}
}
if(countOpen != countClose) {
throw new ValidatorException("Dynamic path malformed; found "+countOpen+" '{' and "+countClose+" closing '}' ") ;
}
String specialCharStart = null;
String specialCharEnd = null;
for (String check : _static_list_characters) {
if(path.contains(check)==false) {
if(specialCharStart==null) {
specialCharStart = check;
}
else if(specialCharEnd==null) {
specialCharEnd = check;
break;
}
}
}
String pathConverted = new String(path);
while(pathConverted.contains("{")){
pathConverted = pathConverted.replace("{", specialCharStart);
}
while(pathConverted.contains("}")){
pathConverted = pathConverted.replace("}", specialCharStart);
}
// System.out.println("SPECIAL START: "+specialCharStart);
// System.out.println("SPECIAL END: "+specialCharEnd);
// System.out.println("SPECIAL pathConverted: "+pathConverted);
try {
URI.create(pathConverted);
//System.out.println("VALIDATE PATH ("+path+")");
}catch(Exception e) {
//System.out.println("ERRORE ORIGINALE: "+e.getMessage());
String msg = e.getMessage();
while(msg.contains(pathConverted)) {
msg = msg.replace(pathConverted, path);
}
//System.out.println("ERRORE CONVERTITO: "+msg);
throw new ValidatorException("Dynamic path malformed; "+msg,e);
}
}
else {
try {
URI.create(path);
}catch(Exception e) {
throw new ValidatorException("Path malformed; "+e.getMessage(),e);
}
}
}
public static ApiOperation findOperation(Api api, HttpRequestMethod httpMethod, String url, boolean exactlyLength) throws ProcessingException{
String[] urlList = extractUrlList(api.getBaseURL(), url);
return getOperation(urlList, api, httpMethod, exactlyLength);
}
public static String[] extractUrlList(URL baseURI, String url) throws ProcessingException{
if(url == null)
throw new ProcessingException("URL non fornita");
List<String> urlList = new ArrayList<>();
if(baseURI != null) {
if(url.startsWith(baseURI.toString())) {
url = url.substring(baseURI.toString().length(), url.length());
}
}
for(String s : url.split("/")) {
if(s!=null && !s.equals("")) {
urlList.add(s);
}
}
return urlList.toArray(new String[] {});
}
private static ApiOperation getOperation(List<ApiOperation> list, String[] url, HttpRequestMethod httpMethod, boolean exactlyLength) throws ProcessingException{
int levelExactlyMatch = -1;
int levelDinamicMatch = -1;
ApiOperation apiOpExactlyMatch = null;
ApiOperation apiOpDynamicMatch = null;
for(int i = 0; i< list.size(); i++) {
ApiOperation apiOp = list.get(i);
if(apiOp.getHttpMethod()!=null) {
if(!apiOp.getHttpMethod().equals(httpMethod)){
continue;
}
}
if(url.length<apiOp.sizePath()){
continue;
}
int counterMatch = 0;
boolean exactlyMatch = true;
boolean dynamicMatch = true;
for (int j = 0; j < apiOp.sizePath(); j++) {
String path = null;
try{
path = apiOp.getPath(j);
}catch(ProcessingException pe){}
if(path!=null && !path.equalsIgnoreCase(url[j])){
exactlyMatch = false;
if(!apiOp.isDynamicPath(j)){
dynamicMatch = false;
break;
}
}
counterMatch++;
}
if(exactlyMatch){
if(counterMatch>levelExactlyMatch){
levelExactlyMatch=counterMatch;
apiOpExactlyMatch = apiOp;
}
}
else if(dynamicMatch){
// dynamic
if(counterMatch>levelDinamicMatch){
levelDinamicMatch=counterMatch;
apiOpDynamicMatch = apiOp;
}
}
}
if(exactlyLength){
if(levelExactlyMatch==url.length){
return apiOpExactlyMatch;
}
else if(levelDinamicMatch==url.length){
return apiOpDynamicMatch;
}
else{
return null;
}
}
else{
if(levelExactlyMatch>levelDinamicMatch){
return apiOpExactlyMatch;
}
else{
return apiOpDynamicMatch;
}
}
}
private static ApiOperation getOperation(String[] url, Api api, HttpRequestMethod httpMethod, boolean exactlyLength) throws ProcessingException{
// Prima cerco operazioni con method specifico e path specifico
// prima della versione 3.3.0.p2: L'interfaccia non permette la creazione di risorse con un metodo preciso e qualsiasi path
// 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
List<ApiOperation> listMethodAndPath = new ArrayList<>();
List<ApiOperation> listQualsiasiMethodAndPath = new ArrayList<>();
List<ApiOperation> listMethodAndQualsiasiPath = new ArrayList<>();
List<ApiOperation> listQualsiasi = new ArrayList<>();
for (ApiOperation apiOperation : api.getOperations()) {
if(apiOperation.getHttpMethod()!=null && apiOperation.getPath()!=null) {
listMethodAndPath.add(apiOperation);
}
else if(apiOperation.getPath()!=null) {
listQualsiasiMethodAndPath.add(apiOperation);
}
else if(apiOperation.getHttpMethod()!=null) {
if(apiOperation.getHttpMethod().equals(httpMethod)){
listMethodAndQualsiasiPath.add(apiOperation);
}
}
else {
listQualsiasi.add(apiOperation);
}
}
ApiOperation op = getOperation(listMethodAndPath, url, httpMethod, exactlyLength);
if(op==null) {
op = getOperation(listQualsiasiMethodAndPath, url, httpMethod, exactlyLength);
}
if(op!=null) {
return op;
}
if(listMethodAndQualsiasiPath.size()>0 && listQualsiasi.size()>0) {
throw new ProcessingException("Found more resource with path '*' (both httpMethod that '*')");
}
if(listMethodAndQualsiasiPath.size()>0) {
if(listMethodAndQualsiasiPath.size()>1) {
throw new ProcessingException("Found more resource with httpMethod '"+listMethodAndQualsiasiPath.get(0).getHttpMethod()+"' and path '*'");
}
else if(listMethodAndQualsiasiPath.size()==1) {
return listMethodAndQualsiasiPath.get(0);
}
}
if(listQualsiasi.size()>0) {
if(listQualsiasi.size()>1) {
throw new ProcessingException("Found more resource with httpMethod '*' and path '*'");
}
else if(listQualsiasi.size()==1) {
return listQualsiasi.get(0);
}
}
return null;
}
}