AuditAppender.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.web.lib.audit.appender;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;

import org.openspcoop2.utils.date.DateManager;
import org.openspcoop2.utils.regexp.RegularExpressionEngine;
import org.openspcoop2.utils.resources.ClassLoaderUtilities;
import org.openspcoop2.utils.serialization.FilteredObject;
import org.openspcoop2.utils.serialization.IDBuilder;
import org.openspcoop2.utils.serialization.SerializationConfig;
import org.openspcoop2.web.lib.audit.AuditException;
import org.openspcoop2.web.lib.audit.costanti.Costanti;
import org.openspcoop2.web.lib.audit.dao.Appender;
import org.openspcoop2.web.lib.audit.dao.Configurazione;
import org.openspcoop2.web.lib.audit.dao.Filtro;
import org.openspcoop2.web.lib.audit.log.Binary;
import org.openspcoop2.web.lib.audit.log.Operation;
import org.openspcoop2.web.lib.audit.log.constants.Stato;
import org.openspcoop2.web.lib.audit.log.constants.Tipologia;

/**
 * Appender per registrare operazione di audit
 * 
 * 
 * @author Andrea Poli (apoli@link.it)
 * @author Stefano Corallo (corallo@link.it)
 * @author Sandra Giangrandi (sandra@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 * 
 */
public class AuditAppender {

	/* Configurazione */
	private static Configurazione configurazioneAuditing;
	private static IDBuilder idBuilder;
	private static HashMap<String,IAuditAppender> appenders = new HashMap<String,IAuditAppender>();
	
	public void initializeAudit(Configurazione configurazioneAuditing,
			IDBuilder idBuilder) throws AuditException{
		_initializeAudit(configurazioneAuditing, idBuilder);
	}
	
	private static synchronized void _initializeAudit(Configurazione configurazioneAuditing,
			IDBuilder idBuilder) throws AuditException{
		
		try{
		
			AuditAppender.appenders.clear();	
			
			AuditAppender.configurazioneAuditing= configurazioneAuditing;
			AuditAppender.idBuilder = idBuilder;
			
			for(int i=0; i<AuditAppender.configurazioneAuditing.sizeAppender(); i++){
				
				// Istanzio appender
				Appender appenderConf = AuditAppender.configurazioneAuditing.getAppender(i);
				Class<?> c = Class.forName(appenderConf.getClassName());
				IAuditAppender appender = (IAuditAppender) ClassLoaderUtilities.newInstance(c);
				
				// Inizializzo appender
				Properties propertiesAppender = new Properties();
				for(int j=0; j<appenderConf.sizeProperties(); j++){
					propertiesAppender.put(appenderConf.getProperty(j).getName(), 
							appenderConf.getProperty(j).getValue());
				}
				appender.initAppender(appenderConf.getNome(),propertiesAppender);
				
				AuditAppender.appenders.put(appenderConf.getNome(),appender);
			}
			
		}catch(Exception e){
			throw new AuditException("InizializzazioneFallita: "+e.getMessage(),e);
		}
	}
	
	public void updateConfigurazioneAuditing(Configurazione configurazioneAuditing) throws AuditException{
		try{
			synchronized(AuditAppender.configurazioneAuditing){
			
				AuditAppender.configurazioneAuditing = configurazioneAuditing;
				
				AuditAppender.appenders.clear();
				
				for(int i=0; i<AuditAppender.configurazioneAuditing.sizeAppender(); i++){
					
					// Istanzio appender
					Appender appenderConf = AuditAppender.configurazioneAuditing.getAppender(i);
					Class<?> c = Class.forName(appenderConf.getClassName());
					IAuditAppender appender = (IAuditAppender) ClassLoaderUtilities.newInstance(c);
					
					// Inizializzo appender
					Properties propertiesAppender = new Properties();
					for(int j=0; j<appenderConf.sizeProperties(); j++){
						propertiesAppender.put(appenderConf.getProperty(j).getName(), 
								appenderConf.getProperty(j).getValue());
					}
					appender.initAppender(appenderConf.getNome(),propertiesAppender);
					
					AuditAppender.appenders.put(appenderConf.getNome(),appender);
				}
			}
		}catch(Exception e){
			throw new AuditException("AggiornamentoFallito: "+e.getMessage(),e);
		}
	}

	
	
	public IDOperazione registraOperazioneInFaseDiElaborazione(Tipologia tipoOperazione,Object object,String user,String interfaceMsg, boolean registrazioneBinari) throws AuditException,AuditDisabilitatoException{
		
		if(AuditAppender.configurazioneAuditing.isAuditEngineEnabled()==false){
			throw new AuditDisabilitatoException("Audit engine disabilitato");
		}
		
		try{
			
				
			Operation operation = new Operation();
			operation.setTipologia(tipoOperazione);
			operation.setUtente(user);
			operation.setStato(Stato.REQUESTING);
			operation.setTimeRequest(DateManager.getDate());
			operation.setTimeExecute(DateManager.getDate());
			operation.setInterfaceMsg(interfaceMsg);
			
			if(object!=null){
				
				operation.setTipoOggetto(AuditAppender.idBuilder.getSimpleName(object));
				operation.setObjectId(AuditAppender.idBuilder.toID(object));
				operation.setObjectOldId(AuditAppender.idBuilder.toOldID(object));
				operation.setObjectClass(object.getClass().getName());
				
			}else{
				throw new AuditException("Object riguardante l'operazione non definito");
			}

			
			// Filtro operazioni
			org.openspcoop2.utils.serialization.Filter listFilter = new org.openspcoop2.utils.serialization.Filter();
			String objectDetails = filtraOperazione(operation,object,listFilter, registrazioneBinari);
			
			if(objectDetails!=null){
				operation.setObjectDetails(objectDetails);
				
				// Aggiunto binaries filtrati
				for(int i=0; i<listFilter.sizeFilteredObjects(); i++){
					FilteredObject filteredObject = listFilter.getFilteredObject(i);
					
					Binary binary = new Binary();
					binary.setBinaryId(filteredObject.getId());
					binary.setChecksum(filteredObject.getChecksum());
					operation.addBinary(binary);
				}
			}
				
			
			// Appender
			IDOperazione idOperazione = new IDOperazione();
			if(AuditAppender.appenders!=null && AuditAppender.appenders.keySet()!=null){
				Iterator<String> iterator = AuditAppender.appenders.keySet().iterator();
				while(iterator.hasNext()){
					
					String appenderName = iterator.next();
					IAuditAppender appender =  AuditAppender.appenders.get(appenderName);
									
					Object idOperazioneAppender =  appender.registraOperazioneInFaseDiElaborazione(operation);
					idOperazione.addIdOperazione(appenderName, idOperazioneAppender);
				}
				return idOperazione;
			}
			else{
				throw new Exception("Appender non forniti");
			}
				
			
		}catch(AuditDisabilitatoException e){
			throw e;
		}catch(Exception e){
			throw new AuditException("registraOperazioneInFaseDiElaborazione error: "+e.getMessage(),e);
		}
		
	}
	
	
	
	public void registraOperazioneAccesso(Tipologia tipoOperazione,String user,String interfaceMsg, boolean registrazioneBinari) throws AuditException,AuditDisabilitatoException{
		
		if(AuditAppender.configurazioneAuditing.isAuditEngineEnabled()==false){
			throw new AuditDisabilitatoException("Audit engine disabilitato");
		}
		
		try{
			
				
			Operation operation = new Operation();
			operation.setTipologia(tipoOperazione);
			operation.setUtente(user);
			operation.setStato(Stato.COMPLETED);
			operation.setTimeRequest(DateManager.getDate());
			operation.setTimeExecute(DateManager.getDate());
			operation.setInterfaceMsg(interfaceMsg);
					
			
			// Filtro operazioni
			filtraOperazione(operation, registrazioneBinari);
			
			
			// Appender
			IDOperazione idOperazione = new IDOperazione();
			if(AuditAppender.appenders!=null && AuditAppender.appenders.keySet()!=null){
				Iterator<String> iterator = AuditAppender.appenders.keySet().iterator();
				while(iterator.hasNext()){
					
					String appenderName = iterator.next();
					IAuditAppender appender =  AuditAppender.appenders.get(appenderName);
									
					Object idOperazioneAppender =  appender.registraOperazioneInFaseDiElaborazione(operation);
					idOperazione.addIdOperazione(appenderName, idOperazioneAppender);
				}
			}
			else{
				throw new Exception("Appender non forniti");
			}
				
			
		}catch(AuditDisabilitatoException e){
			throw e;
		}catch(Exception e){
			throw new AuditException("registraOperazioneInFaseDiElaborazione error: "+e.getMessage(),e);
		}
		
	}
	
	
	
	public void registraOperazioneCompletataConSuccesso(IDOperazione idOperazione,String interfaceMsg) throws AuditException,AuditDisabilitatoException{
		
		if(AuditAppender.configurazioneAuditing.isAuditEngineEnabled()==false){
			throw new AuditDisabilitatoException("Audit engine disabilitato");
		}
		
		try{
			
			if(idOperazione==null){
				throw new AuditException("Identificativo dell'operazione per cui cambiare stato non fornito");
			}
			
			if(AuditAppender.appenders!=null && AuditAppender.appenders.keySet()!=null){
				Iterator<String> iterator = AuditAppender.appenders.keySet().iterator();
				while(iterator.hasNext()){
					
					String appenderName = iterator.next();
					IAuditAppender appender =  AuditAppender.appenders.get(appenderName);
					Object id = idOperazione.getIdOperazione(appenderName);
					if(id==null){
						throw new AuditException("Identificativo dell'operazione per l'appender["+appenderName+"] non fornito");
					}
					if(id instanceof Operation){
						Operation op = (Operation) id;
						op.setInterfaceMsg(interfaceMsg);
					}
					
					appender.registraOperazioneCompletataConSuccesso(id);
				}
			}
			else{
				throw new Exception("Appender non forniti");
			}
				
			
		}catch(AuditDisabilitatoException e){
			throw e;
		}catch(Exception e){
			throw new AuditException("registraOperazioneCompletataConSuccesso error: "+e.getMessage(),e);
		}
	}
	
	
	
	
	
	public void registraOperazioneTerminataConErrore(IDOperazione idOperazione,String motivoErrore,String interfaceMsg) throws AuditException,AuditDisabilitatoException{
		
		if(AuditAppender.configurazioneAuditing.isAuditEngineEnabled()==false){
			throw new AuditDisabilitatoException("Audit engine disabilitato");
		}
		
		try{
			
			if(idOperazione==null){
				throw new AuditException("Identificativo dell'operazione per cui cambiare stato non fornito");
			}
						
			if(AuditAppender.appenders!=null && AuditAppender.appenders.keySet()!=null){
				Iterator<String> iterator = AuditAppender.appenders.keySet().iterator();
				while(iterator.hasNext()){
					
					String appenderName = iterator.next();
					IAuditAppender appender =  AuditAppender.appenders.get(appenderName);
					Object id = idOperazione.getIdOperazione(appenderName);
					if(id==null){
						throw new AuditException("Identificativo dell'operazione per l'appender["+appenderName+"] non fornito");
					}		
					if(id instanceof Operation){
						Operation op = (Operation) id;
						op.setInterfaceMsg(interfaceMsg);
					}
									
					appender.registraOperazioneTerminataConErrore(id,motivoErrore);
				}
			}
			else{
				throw new Exception("Appender non forniti");
			}
				
			
		}catch(AuditDisabilitatoException e){
			throw e;
		}catch(Exception e){
			throw new AuditException("registraOperazioneCompletataConSuccesso error: "+e.getMessage(),e);
		}
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	// -------------- UTILITY -------------------
	
	private String serializeJsonObject(Object o,org.openspcoop2.utils.serialization.Filter listFilter, boolean registrazioneBinari) throws AuditException{
		try{
			if(registrazioneBinari==false) {
				listFilter.addFilterByValue(byte[].class);
			}
			SerializationConfig config = new SerializationConfig();
			config.setFilter(listFilter);
			config.setIdBuilder(AuditAppender.idBuilder);
			config.setPrettyPrint(true);
			// Deprecato
//			org.openspcoop2.utils.serialization.JSonSerializer serializer = 
//				new org.openspcoop2.utils.serialization.JSonSerializer(config);
			org.openspcoop2.utils.serialization.JsonJacksonSerializer serializer = 
				new org.openspcoop2.utils.serialization.JsonJacksonSerializer(config);
			return  serializer.getObject(o);		
		}catch(Exception e){
			throw new AuditException("serializeJsonObject error: "+e.getMessage(),e);
		}
	}
	
	private String serializeXMLObject(Object o,org.openspcoop2.utils.serialization.Filter listFilter, boolean registrazioneBinari) throws AuditException{
		try{
			if(registrazioneBinari==false) {
				listFilter.addFilterByValue(byte[].class);
			}
			SerializationConfig config = new SerializationConfig();
			config.setFilter(listFilter);
			config.setIdBuilder(AuditAppender.idBuilder);
			config.setPrettyPrint(true);
			org.openspcoop2.utils.serialization.XMLSerializer serializer = 
				new org.openspcoop2.utils.serialization.XMLSerializer(config);
			return  serializer.getObject(o);		
		}catch(Exception e){
			throw new AuditException("serializeXMLObject error: "+e.getMessage(),e);
		}
	}
	
	private void filtraOperazione(Operation operation, boolean registrazioneBinari) throws AuditException,AuditDisabilitatoException{
		this.filtraOperazione(operation, null, null, registrazioneBinari);
	}
	private String filtraOperazione(Operation operation,Object object,org.openspcoop2.utils.serialization.Filter listFilter, boolean registrazioneBinari) throws AuditException,AuditDisabilitatoException{
		
		try{
		
			ArrayList<Filtro> filtri = AuditAppender.configurazioneAuditing.getFiltri();
			boolean auditEnabledDefault = AuditAppender.configurazioneAuditing.isAuditEnabled();
			boolean dumpEnabledDefault = AuditAppender.configurazioneAuditing.isDumpEnabled();
			String objectDetails = null;
			
			for(int i=0; i<filtri.size(); i++){
				
				Filtro filtro = filtri.get(i);
				
				//System.out.println("ANALIZZO FILTRO["+i+"]: "+filtro.toString());
				
				if(filtro.getUsername()==null &&
						filtro.getTipoOperazione()==null &&
						filtro.getTipoOggettoInModifica()==null &&
						filtro.getStatoOperazione()==null &&
						filtro.getDump()==null){
					throw new AuditException("Filtro("+i+1+") non valido: nessun meccanismo di filtro definito");
				}
				
				if(filtro.getUsername()!=null){
					// Se e' definito un username nel filtro controllo che corrisponda
					if(filtro.getUsername().equals(operation.getUtente())==false){
						continue;
					}
				}
				
				if(filtro.getTipoOperazione()!=null){
					// Se e' definito un tipo di operazione nel filtro controllo che corrisponda
					if(filtro.getTipoOperazione().equals(operation.getTipologia())==false){
						continue;
					}
				}
				
				if(filtro.getTipoOggettoInModifica()!=null){
					// Se e' definito un TipoOggettoInModifica nel filtro controllo che corrisponda
					if(filtro.getTipoOggettoInModifica().equals(operation.getTipoOggetto())==false){
						continue;
					}
				}
				
				if(filtro.getStatoOperazione()!=null){
					// Se e' definito uno stato dell'operazione nel filtro controllo che corrisponda
					if(filtro.getStatoOperazione().equals(operation.getStato())==false){
						continue;
					}
				}
				
				if(filtro.getDump()!=null){
					
					if(object==null){
						continue; // Questo filtro per essere matchato deve essere controllato con un oggetto
					}
					
					if(objectDetails==null){
						// Serializzo l'oggetto
						objectDetails = this.serialize(object, listFilter, registrazioneBinari);
					}
					
					// Se e' definito un filtro sul contenuto dell'operazione nel filtro controllo che corrisponda
					if(filtro.isDumpExprRegular()){
						if(RegularExpressionEngine.getStringMatchPattern(objectDetails, filtro.getDump())==null){
							continue;
						}
					}else{
						if(objectDetails.contains(filtro.getDump())==false){
							continue;
						}
					}
				}
								
				// Se arriviamo a questo punto significa che l'operazione soddisfa il filtro.
				if(filtro.isAuditEnabled()==false){
					throw new AuditDisabilitatoException("Audit disabilitato, criterio adottato in base al filtro numero "+(i+1));
				}
				if(filtro.isDumpEnabled()){
					if(object!=null){
						// Se non avevo gia' serializzato prima per la ricerca tramite contenuto
						if(objectDetails==null){
							// Serializzo l'oggetto
							objectDetails = this.serialize(object, listFilter, registrazioneBinari);
						}
					}
				}else{
					if(objectDetails!=null){
						objectDetails=null; // era stato utilizzato per effettuare filtro sui contenuti ma non deve essere dumpato
					}
				}
								
				return objectDetails; // regola matcha
			}
			
			// Se nessun filtro sopra viene matchato, si applica il criterio di default
			if(auditEnabledDefault == false){
				throw new AuditDisabilitatoException("Audit disabilitato nella configurazione generale (Non vi sono filtri che creano eccezioni)");
			}
			if(dumpEnabledDefault){
				if(object!=null){
					// Se non avevo gia' serializzato prima per la ricerca tramite contenuto
					if(objectDetails==null){
						// Serializzo l'oggetto
						objectDetails = this.serialize(object, listFilter, registrazioneBinari);
					}
				}
			}else{
				if(objectDetails!=null){
					objectDetails=null; // era stato utilizzato per effettuare filtro sui contenuti ma non deve essere dumpato
				}
			}
			
			return objectDetails;
			
		}catch(AuditDisabilitatoException e){
			throw e;
		}catch(Exception e){
			throw new AuditException("filtraOperazione error: "+e.getMessage(),e);
		}
	}
	
	
	private String serialize(Object object,org.openspcoop2.utils.serialization.Filter listFilter, boolean registrazioneBinari)throws AuditException{
		if(Costanti.DUMP_JSON_FORMAT.equals(AuditAppender.configurazioneAuditing.getDumpFormat())){
			return this.serializeJsonObject(object, listFilter, registrazioneBinari);
		}
		else if(Costanti.DUMP_XML_FORMAT.equals(AuditAppender.configurazioneAuditing.getDumpFormat())){
			return this.serializeXMLObject(object, listFilter, registrazioneBinari);
		}else {
			throw new AuditException("Tipo di formattazione non conosciuta: "+AuditAppender.configurazioneAuditing.getDumpFormat());
		}
	}
}