GestoreAutorizzazioneContenutiBuiltIn.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.autorizzazione;

import java.util.List;
import java.util.Map;

import org.openspcoop2.core.config.Proprieta;
import org.openspcoop2.core.config.utils.ConfigUtils;
import org.openspcoop2.message.OpenSPCoop2Message;
import org.openspcoop2.pdd.config.OpenSPCoop2Properties;
import org.openspcoop2.pdd.core.PdDContext;
import org.openspcoop2.pdd.core.dynamic.DynamicUtils;
import org.openspcoop2.pdd.logger.OpenSPCoop2Logger;
import org.openspcoop2.utils.SortedMap;
import org.openspcoop2.utils.regexp.RegularExpressionEngine;
import org.slf4j.Logger;

/**
 * GestoreAutorizzazioneContenutiBuiltIn
 *
 * @author Andrea Poli (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class GestoreAutorizzazioneContenutiBuiltIn {

	private boolean autorizzato = true;
	private String errorMessage = null;
	public boolean isAutorizzato() {
		return this.autorizzato;
	}

	public String getErrorMessage() {
		return this.errorMessage;
	}
	
	public void process(OpenSPCoop2Message msg, AbstractDatiInvocazione datiInvocazione, PdDContext pddContext,
			List<Proprieta> regole) throws Exception {
		
		Logger log = OpenSPCoop2Logger.getLoggerOpenSPCoopCore();
		
		/* Costruisco dynamic Map */
		boolean bufferMessage_readOnly =  OpenSPCoop2Properties.getInstance().isReadByPathBufferEnabled();
		Map<String, Object> dynamicMap = DynamicUtils.buildDynamicMap(msg, pddContext, datiInvocazione.getBusta(), log, bufferMessage_readOnly);
		
		
		/* Analisi regole di autorizzazione */
		
		if(regole==null || regole.size()<=0) {
			throw new Exception("Non sono state definite regole di autorizzazione contenuto");
		}
		
		SortedMap<List<String>> map = ConfigUtils.toSortedListMap(regole);
		if(map==null || map.isEmpty()) {
			throw new Exception("Non sono state definite regole di autorizzazione contenuto");
		}
		
		//System.out.println("\n\n@ ===== AUTH CONTENUTI =====");
		
		List<String> keys = map.keys();
		for (String risorsa : keys) {
			
			List<String> expectedValues = map.get(risorsa);
			
			if(expectedValues!=null && !expectedValues.isEmpty()) {
				
				for (String expectedValue : expectedValues) {
			
					//System.out.println("check '"+risorsa+"'='"+expectedValue+"'");
					
					if(risorsa==null) {
						throw new Exception("Trovata una regola di autorizzazione senza risorsa");
					}
					risorsa=risorsa.trim();
					
					if(risorsa.startsWith("#")) {
						// commento
						continue;
					}
					
					if(expectedValue==null) {
						throw new Exception("Trovata una regola di autorizzazione senza valore atteso");
					}	
					expectedValue = expectedValue.trim();
					
					// Risolvo valore della risorsa
					String valoreRisorsa = null;
					try {
						valoreRisorsa = DynamicUtils.convertDynamicPropertyValue(risorsa, risorsa, dynamicMap, pddContext);
					}catch(Exception e) {
						String msgError = "Conversione valore della risorsa '"+risorsa+"' non riuscita: "+e.getMessage();
						if(CostantiAutorizzazione.AUTHZ_UNDEFINED.equalsIgnoreCase(expectedValue)) {
							// NOP: se non l'ho risolto significa che non esiste.... sotto nel controllo undefined quindi autorizzo
							//if(!e.getMessage().contains("not exists as key in map")) {
							//	
							//}
						}
						else {
							log.error(msgError, e);
							this.autorizzato = false;
							this.errorMessage = "Resource '"+risorsa+"' not verifiable; unprocessable dynamic value '"+expectedValue+"': "+e.getMessage();
							break;
							//throw new Exception(msgError,e);
						}
					}
					
		
					
					if(CostantiAutorizzazione.AUTHZ_ANY_VALUE.equalsIgnoreCase(expectedValue)) {
						
						/** ANY VALUE */
						
						log.debug("Verifico valore della risorsa '"+risorsa+"' non sia null e non sia vuoto ...");
						
						// basta che abbia un valore not null
						if(valoreRisorsa==null || "".equals(valoreRisorsa)) {
							this.autorizzato = false;
							this.errorMessage = "Resource '"+risorsa+"' with unexpected empty value";
							break;
						}
					}
					else if(CostantiAutorizzazione.AUTHZ_UNDEFINED.equalsIgnoreCase(expectedValue)) {
						
						/** NOT PRESENT */
						
						log.debug("Verifico valore della risorsa '"+risorsa+"' sia null o sia vuoto ...");
						
						// basta che abbia un valore
						if(valoreRisorsa!=null && !"".equals(valoreRisorsa)) {
							this.autorizzato = false;
							this.errorMessage = "Unexpected resource '"+risorsa+"'";
							break;
						}
						
					}
					else if(
							(
									expectedValue.toLowerCase().startsWith(CostantiAutorizzazione.AUTHZ_REGEXP_MATCH_PREFIX.toLowerCase())
									||
									expectedValue.toLowerCase().startsWith(CostantiAutorizzazione.AUTHZ_REGEXP_FIND_PREFIX.toLowerCase())
									||
									expectedValue.toLowerCase().startsWith(CostantiAutorizzazione.AUTHZ_REGEXP_NOT_MATCH_PREFIX.toLowerCase())
									||
									expectedValue.toLowerCase().startsWith(CostantiAutorizzazione.AUTHZ_REGEXP_NOT_FIND_PREFIX.toLowerCase())
							) 
							&&
							expectedValue.toLowerCase().endsWith(CostantiAutorizzazione.AUTHZ_REGEXP_SUFFIX.toLowerCase())) {
						
						/** REGULAR EXPRESSION MATCH/FIND */
						
						boolean match = expectedValue.toLowerCase().startsWith(CostantiAutorizzazione.AUTHZ_REGEXP_MATCH_PREFIX.toLowerCase())
								||
								expectedValue.toLowerCase().startsWith(CostantiAutorizzazione.AUTHZ_REGEXP_NOT_MATCH_PREFIX.toLowerCase());
						boolean not = expectedValue.toLowerCase().startsWith(CostantiAutorizzazione.AUTHZ_REGEXP_NOT_MATCH_PREFIX.toLowerCase())
								||
								expectedValue.toLowerCase().startsWith(CostantiAutorizzazione.AUTHZ_REGEXP_NOT_FIND_PREFIX.toLowerCase());
						String regexpPattern = null;
						if(match) {
							int length = -1;
							if(expectedValue.toLowerCase().startsWith(CostantiAutorizzazione.AUTHZ_REGEXP_MATCH_PREFIX.toLowerCase())) {
								length = CostantiAutorizzazione.AUTHZ_REGEXP_MATCH_PREFIX.length();
							}
							else {
								length = CostantiAutorizzazione.AUTHZ_REGEXP_NOT_MATCH_PREFIX.length();
							}
							if(expectedValue.length()<= (length+CostantiAutorizzazione.AUTHZ_REGEXP_SUFFIX.length()) ) {
								throw new Exception("Resource '"+risorsa+"' configuration without expected regexp match");
							}
							regexpPattern = expectedValue.substring(length, (expectedValue.length()-CostantiAutorizzazione.AUTHZ_REGEXP_SUFFIX.length()));
						}
						else {
							int length = -1;
							if(expectedValue.toLowerCase().startsWith(CostantiAutorizzazione.AUTHZ_REGEXP_FIND_PREFIX.toLowerCase())) {
								length = CostantiAutorizzazione.AUTHZ_REGEXP_FIND_PREFIX.length();
							}
							else {
								length = CostantiAutorizzazione.AUTHZ_REGEXP_NOT_FIND_PREFIX.length();
							}
							if(expectedValue.length()<= (length+CostantiAutorizzazione.AUTHZ_REGEXP_SUFFIX.length()) ) {
								throw new Exception("Resource '"+risorsa+"' configuration without expected regexp find");
							}
							regexpPattern = expectedValue.substring(length, (expectedValue.length()-CostantiAutorizzazione.AUTHZ_REGEXP_SUFFIX.length()));
						}
						regexpPattern = regexpPattern.trim();
						log.debug("Verifico valore dela risorsa '"+risorsa+"' tramite espressione regolare (match:"+match+") '"+regexpPattern+"' ...");
						
						// basta che un valore abbia match
						boolean ok = match ? RegularExpressionEngine.isMatch(valoreRisorsa, regexpPattern) : RegularExpressionEngine.isFind(valoreRisorsa, regexpPattern);
						if(not) {
							if(ok) {
								String tipo = match ? "match" : "find";
								this.autorizzato = false;
								this.errorMessage = "Resource '"+risorsa+"' with unexpected value '"+valoreRisorsa+"' (regExpr not "+tipo+" failed)";
								break;
							}
						}
						else {
							if(!ok) {
								String tipo = match ? "match" : "find";
								this.autorizzato = false;
								this.errorMessage = "Resource '"+risorsa+"' with unexpected value '"+valoreRisorsa+"' (regExpr "+tipo+" failed)";
								break;
							}
						}
						
					}
					else {
					
						/** VALUE (con PLACEHOLDERS) */
						
						boolean not = false;
						if(
								expectedValue.toLowerCase().startsWith(CostantiAutorizzazione.AUTHZ_NOT_PREFIX.toLowerCase())
								&&
								expectedValue.toLowerCase().endsWith(CostantiAutorizzazione.AUTHZ_NOT_SUFFIX.toLowerCase())) {
							not = true;
							if(expectedValue.length()<= (CostantiAutorizzazione.AUTHZ_NOT_PREFIX.length()+CostantiAutorizzazione.AUTHZ_NOT_SUFFIX.length()) ) {
								throw new Exception("Resource '"+risorsa+"' configuration without value in not condition");
							}
							expectedValue = expectedValue.substring(CostantiAutorizzazione.AUTHZ_NOT_PREFIX.length(), (expectedValue.length()-CostantiAutorizzazione.AUTHZ_NOT_SUFFIX.length()));
						}
						
						boolean ignoreCase = false;
						if(
								expectedValue.toLowerCase().startsWith(CostantiAutorizzazione.AUTHZ_IGNORE_CASE_PREFIX.toLowerCase())
								&&
								expectedValue.toLowerCase().endsWith(CostantiAutorizzazione.AUTHZ_IGNORE_CASE_SUFFIX.toLowerCase())) {
							ignoreCase = true;
							if(expectedValue.length()<= (CostantiAutorizzazione.AUTHZ_IGNORE_CASE_PREFIX.length()+CostantiAutorizzazione.AUTHZ_IGNORE_CASE_SUFFIX.length()) ) {
								throw new Exception("Resource '"+risorsa+"' configuration without value in ignore case condition");
							}
							expectedValue = expectedValue.substring(CostantiAutorizzazione.AUTHZ_IGNORE_CASE_PREFIX.length(), (expectedValue.length()-CostantiAutorizzazione.AUTHZ_IGNORE_CASE_SUFFIX.length()));
						}
						
						try {
							expectedValue = DynamicUtils.convertDynamicPropertyValue(risorsa, expectedValue, dynamicMap, pddContext);
						}catch(Exception e) {
							String msgErrore = "Conversione valore per risorsa '"+risorsa+"' non riuscita (valore: "+expectedValue+"): "+e.getMessage();
							//throw new Exception(msgErrore,e);
							log.error(msgErrore, e);
							this.autorizzato = false;
							this.errorMessage = "Resource '"+risorsa+"' not verifiable; unprocessable dynamic value '"+expectedValue+"': "+e.getMessage();
							break;
						}
						
						if(expectedValue!=null) {
							boolean ok = false;
							if(expectedValue.contains(",")) {
								String [] values = expectedValue.split(",");
								for (int i = 0; i < values.length; i++) {
									String v = values[i].trim();
									if(v!=null) {
										if(ignoreCase) {
											if(v.equalsIgnoreCase(valoreRisorsa)) {
												ok = true;
												break;
											}
										}
										else {
											if(v.equals(valoreRisorsa)) {
												ok = true;
												break;
											}
										}
									}
								}
							}
							else {
								if(ignoreCase) {
									ok = expectedValue.equalsIgnoreCase(valoreRisorsa);
								}
								else {
									ok = expectedValue.equals(valoreRisorsa);
								}
							}
							
							if(not) {
								if(ok) {
									this.autorizzato = false;
									this.errorMessage = "Resource '"+risorsa+"' with unauthorized value '"+valoreRisorsa+"'";
									break;
								}
							}
							else {
								if(!ok) {
									this.autorizzato = false;
									this.errorMessage = "Resource '"+risorsa+"' with unexpected value '"+valoreRisorsa+"'";
									break;
								}
							}
						}
						else {
							if(valoreRisorsa!=null) {
								this.autorizzato = false;
								this.errorMessage = "Resource '"+risorsa+"' with unexpected value; expected null value";
								break;
							}
						}
					}
				}
				
			}
			
		}
		
	}

}