XACMLPolicyUtilities.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.io.File;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.openspcoop2.core.config.ServizioApplicativo;
import org.openspcoop2.core.config.constants.TipoAutenticazione;
import org.openspcoop2.core.config.driver.DriverConfigurazioneNotFound;
import org.openspcoop2.core.id.IDRuolo;
import org.openspcoop2.core.id.IDServizio;
import org.openspcoop2.core.id.IDServizioApplicativo;
import org.openspcoop2.core.id.IDSoggetto;
import org.openspcoop2.core.registry.AccordoServizioParteSpecifica;
import org.openspcoop2.core.registry.Documento;
import org.openspcoop2.core.registry.Ruolo;
import org.openspcoop2.core.registry.constants.RuoloContesto;
import org.openspcoop2.core.registry.constants.RuoloTipologia;
import org.openspcoop2.core.registry.constants.TipiDocumentoSicurezza;
import org.openspcoop2.core.registry.driver.DriverRegistroServiziNotFound;
import org.openspcoop2.core.registry.driver.FiltroRicercaRuoli;
import org.openspcoop2.pdd.config.ConfigurazionePdDManager;
import org.openspcoop2.pdd.core.PdDContext;
import org.openspcoop2.pdd.core.autorizzazione.container.AutorizzazioneHttpServletRequest;
import org.openspcoop2.pdd.core.autorizzazione.pa.DatiInvocazionePortaApplicativa;
import org.openspcoop2.pdd.core.autorizzazione.pd.DatiInvocazionePortaDelegata;
import org.openspcoop2.pdd.core.token.EsitoGestioneToken;
import org.openspcoop2.pdd.core.token.InformazioniToken;
import org.openspcoop2.pdd.core.token.InformazioniTokenUserInfo;
import org.openspcoop2.pdd.core.token.TokenUtilities;
import org.openspcoop2.pdd.core.token.attribute_authority.InformazioniAttributi;
import org.openspcoop2.pdd.logger.OpenSPCoop2Logger;
import org.openspcoop2.protocol.registry.RegistroServiziManager;
import org.openspcoop2.protocol.sdk.IProtocolFactory;
import org.openspcoop2.protocol.sdk.state.URLProtocolContext;
import org.openspcoop2.utils.MapKey;
import org.openspcoop2.utils.json.JSONUtils;
import org.openspcoop2.utils.resources.FileSystemUtilities;
import org.openspcoop2.utils.transport.http.HttpUtilities;
import org.openspcoop2.utils.xacml.CachedMapBasedSimplePolicyRepository;
import org.openspcoop2.utils.xacml.MarshallUtilities;
import org.openspcoop2.utils.xacml.PolicyDecisionPoint;
import org.openspcoop2.utils.xacml.PolicyException;
import org.openspcoop2.utils.xacml.XacmlRequest;
import org.slf4j.Logger;


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

	private static PolicyDecisionPoint pdp;
	private static synchronized void initPdD(Logger log) throws PolicyException{
		if(XACMLPolicyUtilities.pdp == null) {
			XACMLPolicyUtilities.pdp = new PolicyDecisionPoint(log);
		}
	}	
	public static PolicyDecisionPoint getPolicyDecisionPoint(Logger log) throws PolicyException{
		if(XACMLPolicyUtilities.pdp == null) {
			XACMLPolicyUtilities.initPdD(log);
		}
		return XACMLPolicyUtilities.pdp;
	}

	// Workaround: Serve nel caso di Porta Delegata per poter utilizzare una policy differente da quella utilizzata nell'erogazione
	private static final String  NOME_POLICY_FRUIZIONE = "fruizioneXacmlPolicy.xml";

	public static void loadPolicy(String xacmlPolicyPorta, IDServizio idServizio, String key, 
			boolean portaDelegata,IDSoggetto fruitore, // fruitore is null se non e' portaDelegata
			Logger log) throws PolicyException{
		byte[] policy = null; 
		if(xacmlPolicyPorta!=null) {
			policy = xacmlPolicyPorta.getBytes();
		}
		else {
			@SuppressWarnings("unused")
			String nomePolicy = null;
			int numeroPolicy = 0;
			@SuppressWarnings("unused")
			boolean numeroPolicyFruizione = false;
			try{
				AccordoServizioParteSpecifica asps = RegistroServiziManager.getInstance().getAccordoServizioParteSpecifica(idServizio, null, true, null); // requestInfo non serve poichè devono essere caricati gli allegati
				for (int i = 0; i < asps.sizeSpecificaSicurezzaList(); i++) {
					Documento d = asps.getSpecificaSicurezza(i);
					if(TipiDocumentoSicurezza.XACML_POLICY.getNome().equals(d.getTipo())){
	
						if(policy == null || (portaDelegata && (fruitore.getNome()+"_"+NOME_POLICY_FRUIZIONE).equals(d.getFile()))){
							if(NOME_POLICY_FRUIZIONE.equals(d.getFile())){
								numeroPolicyFruizione = true;
							}
							else{
								numeroPolicy++;
							}
							if(d.getByteContenuto()!=null){
								policy = d.getByteContenuto();
								nomePolicy = d.getFile();
							}
							else if(d.getFile()!=null){
								if(d.getFile().startsWith("http://") || d.getFile().startsWith("file://")){
									URL url = new URL(d.getFile());
									policy = HttpUtilities.requestHTTPFile(url.toString());
								}
								else{
									File f = new File(d.getFile());
									policy = FileSystemUtilities.readBytesFromFile(f);
								}
							}
						}
					}
				}
				if(numeroPolicy>1){
					throw new PolicyException("Piu di una xacml policy trovata per il servizio "+idServizio.toString());
				}
			}catch(Exception e){
				throw new PolicyException("Errore durante la ricerca delle policies xacml per il servizio "+idServizio.toString()+": "+e.getMessage(),e);
			}
			if(policy== null){
				throw new PolicyException("Nessuna xacml policy trovata trovata per il servizio "+idServizio.toString());
			}
		}

		try{
			// Caricamento in PdP vedendo che la policy non sia gia stata caricata ....
			XACMLPolicyUtilities.getPolicyDecisionPoint(log).addPolicy(MarshallUtilities.unmarshallPolicy(policy), key);
		}catch(Exception e){
			throw new PolicyException("Errore durante il caricamento della xacml policy sul PdD (servizio "+idServizio.toString()+"): "+e.getMessage(),e);
		}
	}

	public static XacmlRequest newXacmlRequest(IProtocolFactory<?> protocolFactory, AbstractDatiInvocazione datiInvocazione, 
			boolean checkRuoloRegistro, boolean checkRuoloEsterno,
			String policyKey) throws AutorizzazioneException{

		XacmlRequest xacmlRequest = new XacmlRequest();
		URLProtocolContext urlProtocolContext = null;
		if(datiInvocazione.getInfoConnettoreIngresso()==null ||
				datiInvocazione.getInfoConnettoreIngresso().getUrlProtocolContext()==null){
			throw new AutorizzazioneException("UrlProtocolContext non disponibile; risorsa richiesta dall'autorizzazione");
		}
		urlProtocolContext = datiInvocazione.getInfoConnettoreIngresso().getUrlProtocolContext();

		if(datiInvocazione.getIdServizio()==null || 
				datiInvocazione.getIdServizio().getSoggettoErogatore()==null ||
				datiInvocazione.getIdServizio().getSoggettoErogatore().getTipo()==null ||
				datiInvocazione.getIdServizio().getSoggettoErogatore().getNome()==null ||
				datiInvocazione.getIdServizio().getTipo()==null ||
				datiInvocazione.getIdServizio().getNome() == null){
			throw new AutorizzazioneException("DatiServizio non disponibile; risorsa richiesta dall'autorizzazione");
		}
		String tipoSoggettoErogatore = datiInvocazione.getIdServizio().getSoggettoErogatore().getTipo();
		String nomeSoggettoErogatore = datiInvocazione.getIdServizio().getSoggettoErogatore().getNome();
		String tipoServizio = datiInvocazione.getIdServizio().getTipo();
		String nomeServizio = datiInvocazione.getIdServizio().getNome();
		String azione = datiInvocazione.getIdServizio().getAzione() != null ? datiInvocazione.getIdServizio().getAzione() : "";

		DatiInvocazionePortaDelegata datiPD = null;
		if(datiInvocazione instanceof DatiInvocazionePortaDelegata){
			datiPD = (DatiInvocazionePortaDelegata) datiInvocazione;
		}

		DatiInvocazionePortaApplicativa datiPA = null;
		if(datiInvocazione instanceof DatiInvocazionePortaApplicativa){
			datiPA = (DatiInvocazionePortaApplicativa) datiInvocazione;
		}
	
		PdDContext pddContext = datiInvocazione.getPddContext();
		
		InformazioniToken informazioniTokenNormalizzate = null;
		InformazioniTokenUserInfo informazioniTokenUserInfoNormalizzate = null;
		Map<String, Serializable> jwtClaims = null;
		Map<String, Serializable> introspectionClaims = null;
		Map<String, Serializable> userInfoClaims = null;
		Object oInformazioniTokenNormalizzate = pddContext.getObject(org.openspcoop2.pdd.core.token.Costanti.PDD_CONTEXT_TOKEN_INFORMAZIONI_NORMALIZZATE);
		if(oInformazioniTokenNormalizzate!=null) {
			informazioniTokenNormalizzate = (InformazioniToken) oInformazioniTokenNormalizzate;
			informazioniTokenUserInfoNormalizzate = informazioniTokenNormalizzate.getUserInfo();
		}
		Object oTmp = pddContext.getObject(org.openspcoop2.pdd.core.token.Costanti.PDD_CONTEXT_TOKEN_ESITO_VALIDAZIONE);
		if(oTmp!=null) {
			EsitoGestioneToken esito = (EsitoGestioneToken) oTmp;
			if(esito.getInformazioniToken()!=null &&
					esito.getInformazioniToken().getClaims()!=null &&
					esito.getInformazioniToken().getClaims().size()>0) {
				jwtClaims = esito.getInformazioniToken().getClaims();
			}
		}
		oTmp = pddContext.getObject(org.openspcoop2.pdd.core.token.Costanti.PDD_CONTEXT_TOKEN_ESITO_INTROSPECTION);
		if(oTmp!=null) {
			EsitoGestioneToken esito = (EsitoGestioneToken) oTmp;
			if(esito.getInformazioniToken()!=null &&
					esito.getInformazioniToken().getClaims()!=null &&
					esito.getInformazioniToken().getClaims().size()>0) {
				introspectionClaims = esito.getInformazioniToken().getClaims();
			}
		}
		oTmp = pddContext.getObject(org.openspcoop2.pdd.core.token.Costanti.PDD_CONTEXT_TOKEN_ESITO_USER_INFO);
		if(oTmp!=null) {
			EsitoGestioneToken esito = (EsitoGestioneToken) oTmp;
			if(esito.getInformazioniToken()!=null &&
					esito.getInformazioniToken().getClaims()!=null &&
					esito.getInformazioniToken().getClaims().size()>0) {
				userInfoClaims = esito.getInformazioniToken().getClaims();
			}
		}
		List<String> tokenRoles = null;
		if(informazioniTokenNormalizzate!=null) {
			tokenRoles = informazioniTokenNormalizzate.getRoles();
		}
		
		List<String> attributeNames = null;
		Map<String, Serializable> attributes = null;
		boolean multipleAA = false;
		InformazioniAttributi informazioniAttributiNormalizzati = null;
		Object oInformazioniAttributiNormalizzati = pddContext.getObject(org.openspcoop2.pdd.core.token.Costanti.PDD_CONTEXT_ATTRIBUTI_INFORMAZIONI_NORMALIZZATE);
		if(oInformazioniAttributiNormalizzati!=null) {
			informazioniAttributiNormalizzati = (InformazioniAttributi) oInformazioniAttributiNormalizzati;
		}
		if(informazioniAttributiNormalizzati!=null) {
			attributeNames = informazioniAttributiNormalizzati.getAttributesNames();
			attributes = informazioniAttributiNormalizzati.getAttributes();
			multipleAA = informazioniAttributiNormalizzati.isMultipleAttributeAuthorities()!=null && 
					informazioniAttributiNormalizzati.isMultipleAttributeAuthorities().getValue()!=null && 
					informazioniAttributiNormalizzati.isMultipleAttributeAuthorities().getValue();
		}

		Map<String, String> apiImplConfig = readConfig(pddContext, org.openspcoop2.core.constants.Costanti.PROPRIETA_CONFIGURAZIONE);
		Map<String, String> applicativoConfig = readConfig(pddContext, org.openspcoop2.core.constants.Costanti.PROPRIETA_APPLICATIVO);
		Map<String, String> soggettoFruitoreConfig = readConfig(pddContext, org.openspcoop2.core.constants.Costanti.PROPRIETA_SOGGETTO_FRUITORE);
		Map<String, String> soggettoErogatoreConfig = readConfig(pddContext, org.openspcoop2.core.constants.Costanti.PROPRIETA_SOGGETTO_EROGATORE);
						
		HttpServletRequest httpServletRequest = null;
		if(checkRuoloEsterno){
			if(datiInvocazione.getInfoConnettoreIngresso().getUrlProtocolContext().getHttpServletRequest()==null){
				if(tokenRoles==null) {
					throw new AutorizzazioneException("HttpServletRequest non disponibile; risorsa richiesta dall'autorizzazione");
				}
			}
			httpServletRequest = datiInvocazione.getInfoConnettoreIngresso().getUrlProtocolContext().getHttpServletRequest();
		}

		


		// Action

		String azioneId = urlProtocolContext.getRequestURI();
		xacmlRequest.addAction(azioneId); // namespace standard xacml

		xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_PROVIDER_ATTRIBUTE_ID, tipoSoggettoErogatore+"/"+nomeSoggettoErogatore); 
		if(soggettoErogatoreConfig!=null && !soggettoErogatoreConfig.isEmpty()) {
			Iterator<String> keys = soggettoErogatoreConfig.keySet().iterator();
			while (keys.hasNext()) {
				String key = (String) keys.next();
				String value = soggettoErogatoreConfig.get(key);
				xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_PROVIDER_CONFIG_ATTRIBUTE_PREFIX+key, value);
			}
		}

		xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_SERVICE_ATTRIBUTE_ID, tipoServizio+"/"+nomeServizio);  
		if(apiImplConfig!=null && !apiImplConfig.isEmpty()) {
			Iterator<String> keys = apiImplConfig.keySet().iterator();
			while (keys.hasNext()) {
				String key = (String) keys.next();
				String value = apiImplConfig.get(key);
				xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_SERVICE_CONFIG_ATTRIBUTE_PREFIX+key, value);
			}
		}

		if(azione!=null){
			xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_ACTION_ATTRIBUTE_ID, azione);
		}

		xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_URL_ATTRIBUTE_ID, urlProtocolContext.getRequestURI());    	

		if(urlProtocolContext.getParameters()!=null && urlProtocolContext.getParameters().size()>0){
			Iterator<String> keys = urlProtocolContext.getParameters().keySet().iterator();
			while (keys.hasNext()) {
				String key = (String) keys.next();
				List<String> values = urlProtocolContext.getParameterValues(key);
				if(key!=null && !key.contains(" ") && values!=null && !values.isEmpty()){
					if(values.size()==1) {
						xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_URL_PARAMETER_ATTRIBUTE_ID+key, values.get(0));
					}
					else {
						xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_URL_PARAMETER_ATTRIBUTE_ID+key, values);
					}
				}
			}
		}

		if(urlProtocolContext.getHeaders()!=null && urlProtocolContext.getHeaders().size()>0){
			Iterator<String> keys = urlProtocolContext.getHeaders().keySet().iterator();
			while (keys.hasNext()) {
				String key = (String) keys.next();
				List<String> values = urlProtocolContext.getHeaderValues(key);
				if(key!=null && !key.contains(" ") && values!=null && !values.isEmpty()){
					if(values.size()==1) {
						xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_TRANSPORT_HEADER_ATTRIBUTE_ID+key, values.get(0));
					}
					else {
						xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_TRANSPORT_HEADER_ATTRIBUTE_ID+key, values);
					}
				}
			}
		}

		if(urlProtocolContext.getFunction()!=null){
			xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_PDD_SERVICE_ATTRIBUTE_ID, urlProtocolContext.getFunction());    	
		}

		if(datiInvocazione.getInfoConnettoreIngresso().getSoapAction()!=null){
			xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_SOAP_ACTION_ATTRIBUTE_ID, datiInvocazione.getInfoConnettoreIngresso().getSoapAction());    	
		}

		if(protocolFactory!=null){
			xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_PROTOCOL_ATTRIBUTE_ID, protocolFactory.getProtocol());    	
		}
		
		if(informazioniTokenNormalizzate!=null) {
			if(informazioniTokenNormalizzate.getAud()!=null) {
				xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_TOKEN_AUDIENCE_ATTRIBUTE_ID, informazioniTokenNormalizzate.getAud());
			}
			if(informazioniTokenNormalizzate.getScopes()!=null && informazioniTokenNormalizzate.getScopes().size()>0) {
				xacmlRequest.addActionAttribute(XACMLCostanti.XACML_REQUEST_ACTION_TOKEN_SCOPE_ATTRIBUTE_ID, informazioniTokenNormalizzate.getScopes());
			}
		}
		
		if(jwtClaims!=null && jwtClaims.size()>0) {
			Iterator<?> it = jwtClaims.keySet().iterator();
			while (it.hasNext()) {
				String key = (String) it.next();
				Object value = jwtClaims.get(key);
				key = normalizeKeyClaim(key);
				setActionAttribute(xacmlRequest, value, 
						XACMLCostanti.XACML_REQUEST_ACTION_TOKEN_JWT_CLAIMS_PREFIX+key);
			}
		}
		
		if(introspectionClaims!=null && introspectionClaims.size()>0) {
			Iterator<?> it = introspectionClaims.keySet().iterator();
			while (it.hasNext()) {
				String key = (String) it.next();
				Object value = introspectionClaims.get(key);
				key = normalizeKeyClaim(key);
				setActionAttribute(xacmlRequest, value, 
						XACMLCostanti.XACML_REQUEST_ACTION_TOKEN_INTROSPECTION_CLAIMS_PREFIX+key);
			}
		}

		

		// Subject

		String nomeServizioApplicativo = null;
		IDSoggetto soggettoFruitore = null;
		if(datiPD!=null){
			if(datiPD.getIdServizioApplicativo()!=null){
				nomeServizioApplicativo = datiPD.getIdServizioApplicativo().getNome();
				soggettoFruitore = datiPD.getIdServizioApplicativo().getIdSoggettoProprietario();
			}
			else if(datiPD.getIdPD()!=null && datiPD.getIdPD().getIdentificativiFruizione()!=null &&
					datiPD.getIdPD().getIdentificativiFruizione().getSoggettoFruitore()!=null){
				soggettoFruitore = datiPD.getIdPD().getIdentificativiFruizione().getSoggettoFruitore();
			}
		}
		else if(datiPA!=null){
			if(datiPA.getIdSoggettoFruitore()!=null){
				soggettoFruitore = datiPA.getIdSoggettoFruitore();
			}
			if(datiPA.getIdentitaServizioApplicativoFruitore()!=null){
				nomeServizioApplicativo = datiPA.getIdentitaServizioApplicativoFruitore().getNome();
				if(soggettoFruitore==null){
					soggettoFruitore = datiPA.getIdentitaServizioApplicativoFruitore().getIdSoggettoProprietario();
				}
			}
		}
		
		String credential = null;
		TipoAutenticazione autenticazione = null;
		if(datiPD!=null){
			if(datiPD.getPd()!=null){
				autenticazione = TipoAutenticazione.toEnumConstant(datiPD.getPd().getAutenticazione());
			}
		}
		else if(datiPA!=null){
			if(datiPA.getPa()!=null){
				autenticazione = TipoAutenticazione.toEnumConstant(datiPA.getPa().getAutenticazione());
			}
		}
		if(datiInvocazione.getInfoConnettoreIngresso().getCredenziali()!=null){
			
			if(autenticazione!=null){
				if(TipoAutenticazione.BASIC.equals(autenticazione)){
					if(datiInvocazione.getInfoConnettoreIngresso().getCredenziali().getUsername()!=null){
						credential = datiInvocazione.getInfoConnettoreIngresso().getCredenziali().getUsername();
					}
				}
				else if(TipoAutenticazione.SSL.equals(autenticazione)){
					if(datiInvocazione.getInfoConnettoreIngresso().getCredenziali().getSubject()!=null){
						credential = datiInvocazione.getInfoConnettoreIngresso().getCredenziali().getSubject();
					}
				}
				else if(TipoAutenticazione.PRINCIPAL.equals(autenticazione)){
					if(datiInvocazione.getInfoConnettoreIngresso().getCredenziali().getPrincipal()!=null){
						credential = datiInvocazione.getInfoConnettoreIngresso().getCredenziali().getPrincipal();
					}
				}
			}
						
			if(credential == null){
				if(datiInvocazione.getInfoConnettoreIngresso().getCredenziali().getPrincipal()!=null){
					credential = datiInvocazione.getInfoConnettoreIngresso().getCredenziali().getPrincipal();
				}
				else if(datiInvocazione.getInfoConnettoreIngresso().getCredenziali().getSubject()!=null){
					credential = datiInvocazione.getInfoConnettoreIngresso().getCredenziali().getSubject();
				}
				else if(datiInvocazione.getInfoConnettoreIngresso().getCredenziali().getUsername()!=null){
					credential = datiInvocazione.getInfoConnettoreIngresso().getCredenziali().getUsername();
				}
			}
		}
		
		List<String> roles = new ArrayList<>();
		if(checkRuoloRegistro){
			if(datiPD!=null){
				if(datiPD.getServizioApplicativo()==null && !checkRuoloEsterno &&
						informazioniTokenNormalizzate==null){
					throw new AutorizzazioneException("Identità servizio applicativo non disponibile; tale informazione è richiesta dall'autorizzazione");
				}
				if(datiPD.getServizioApplicativo()!=null) {
					if(datiPD.getServizioApplicativo().getInvocazionePorta()!=null && 
							datiPD.getServizioApplicativo().getInvocazionePorta().getRuoli()!=null &&
							datiPD.getServizioApplicativo().getInvocazionePorta().getRuoli().sizeRuoloList()>0){
						for (int i = 0; i < datiPD.getServizioApplicativo().getInvocazionePorta().getRuoli().sizeRuoloList(); i++) {
							roles.add(datiPD.getServizioApplicativo().getInvocazionePorta().getRuoli().getRuolo(i).getNome());
						}
					}
				}
			}
			else if(datiPA!=null){
				if(datiPA.getSoggettoFruitore()==null && !checkRuoloEsterno &&
						informazioniTokenNormalizzate==null){
					throw new AutorizzazioneException("Identità soggetto fruitore non disponibile; tale informazione è richiesta dall'autorizzazione");
				}
				if(datiPA.getSoggettoFruitore()!=null) {
					if(datiPA.getSoggettoFruitore().getRuoli()!=null &&
							datiPA.getSoggettoFruitore().getRuoli().sizeRuoloList()>0){
						for (int i = 0; i < datiPA.getSoggettoFruitore().getRuoli().sizeRuoloList(); i++) {
							roles.add(datiPA.getSoggettoFruitore().getRuoli().getRuolo(i).getNome());
						}
					}
				}
			}
		}
		if(checkRuoloEsterno){
			try{
				FiltroRicercaRuoli filtroRicerca = new FiltroRicercaRuoli();
				if(datiPD!=null){
					filtroRicerca.setContesto(RuoloContesto.PORTA_DELEGATA);
				}
				else if(datiPA!=null){
					filtroRicerca.setContesto(RuoloContesto.PORTA_APPLICATIVA);
				}
				filtroRicerca.setTipologia(RuoloTipologia.ESTERNO);
				RegistroServiziManager registroServiziManager = RegistroServiziManager.getInstance(datiInvocazione.getState());
				List<IDRuolo> list = registroServiziManager.getAllIdRuoli(filtroRicerca, null);
				if(list==null || list.size()<=0){
					throw new DriverRegistroServiziNotFound();
				}
				for (IDRuolo idRuolo : list) {
					String nomeRuoloDaVerificare = idRuolo.getNome();
					try {
						Ruolo ruoloRegistro = registroServiziManager.getRuolo(idRuolo.getNome(), null, datiInvocazione.getRequestInfo());
						if(ruoloRegistro.getNomeEsterno()!=null && !"".equals(ruoloRegistro.getNomeEsterno())) {
							nomeRuoloDaVerificare = ruoloRegistro.getNomeEsterno();
						}
					}catch(Exception e) {
						throw new Exception("Recupero del ruolo '"+idRuolo.getNome()+"' fallito: "+e.getMessage(),e);
					}
					if(httpServletRequest.isUserInRole(nomeRuoloDaVerificare)){
						roles.add(idRuolo.getNome()); // nella xacml policy comunque inserisco l'identificativo del ruolo nel registro della PdD
					}
				}
				if(tokenRoles!=null && tokenRoles.size()>0) {
					roles.addAll(tokenRoles);
				}
			}
			catch(DriverRegistroServiziNotFound notFound){
				if(!checkRuoloRegistro) {
					throw new AutorizzazioneException("Non sono stati registrati ruoli utilizzabili con un'autorizzazione esterna");
				}
			}
			catch(Exception e){
				throw new AutorizzazioneException("E' avvenuto un errore durante la ricerca dei ruoli: "+e.getMessage(),e);
			}
		}
		
		oTmp = pddContext.getObject(org.openspcoop2.core.constants.Costanti.ID_APPLICATIVO_TOKEN);
		IDServizioApplicativo idApplicativoToken = null;
		Map<String, String> applicativoTokenConfig = null;
		Map<String, String> soggettoProprietarioApplicativoTokenConfig = null;
		List<String> applicativoTokenRoles = new ArrayList<>();
		if(oTmp!=null && oTmp instanceof IDServizioApplicativo) {
			idApplicativoToken = (IDServizioApplicativo) oTmp;
			applicativoTokenConfig = readConfig(pddContext, org.openspcoop2.core.constants.Costanti.PROPRIETA_APPLICATIVO_TOKEN);
			soggettoProprietarioApplicativoTokenConfig = readConfig(pddContext, org.openspcoop2.core.constants.Costanti.PROPRIETA_SOGGETTO_PROPRIETARIO_APPLICATIVO_TOKEN);
			
			ServizioApplicativo saToken = null;
			if(checkRuoloRegistro || checkRuoloEsterno) {
				try {
					saToken = ConfigurazionePdDManager.getInstance().getServizioApplicativo(idApplicativoToken, datiInvocazione.getRequestInfo());
				}catch(DriverConfigurazioneNotFound notFound) {}
				catch(Exception e){
					throw new AutorizzazioneException("E' avvenuto un errore durante la ricerca dell'applicativo token '"+idApplicativoToken+"': "+e.getMessage(),e);
				}
			}
			if(checkRuoloRegistro){
				if(saToken!=null) {
					if(saToken.getInvocazionePorta()!=null && 
							saToken.getInvocazionePorta().getRuoli()!=null &&
									saToken.getInvocazionePorta().getRuoli().sizeRuoloList()>0){
						for (int i = 0; i < saToken.getInvocazionePorta().getRuoli().sizeRuoloList(); i++) {
							applicativoTokenRoles.add(saToken.getInvocazionePorta().getRuoli().getRuolo(i).getNome());
						}
					}
				}
			}
		}
		if(checkRuoloEsterno && informazioniTokenNormalizzate!=null && tokenRoles!=null &&
				!tokenRoles.isEmpty()){
			try{
				FiltroRicercaRuoli filtroRicerca = new FiltroRicercaRuoli();
				if(datiPD!=null){
					filtroRicerca.setContesto(RuoloContesto.PORTA_DELEGATA);
				}
				else if(datiPA!=null){
					filtroRicerca.setContesto(RuoloContesto.PORTA_APPLICATIVA);
				}
				filtroRicerca.setTipologia(RuoloTipologia.ESTERNO);
				RegistroServiziManager registroServiziManager = RegistroServiziManager.getInstance(datiInvocazione.getState());
				List<IDRuolo> list = registroServiziManager.getAllIdRuoli(filtroRicerca, null);
				if(list==null || list.size()<=0){
					throw new DriverRegistroServiziNotFound();
				}
				for (IDRuolo idRuolo : list) {
					String nomeRuoloDaVerificare = idRuolo.getNome();
					try {
						Ruolo ruoloRegistro = registroServiziManager.getRuolo(idRuolo.getNome(), null, datiInvocazione.getRequestInfo());
						if(ruoloRegistro.getNomeEsterno()!=null && !"".equals(ruoloRegistro.getNomeEsterno())) {
							nomeRuoloDaVerificare = ruoloRegistro.getNomeEsterno();
						}
					}catch(Exception e) {
						throw new Exception("Recupero del ruolo '"+idRuolo.getNome()+"' fallito: "+e.getMessage(),e);
					}
					if(informazioniTokenNormalizzate.getRoles().contains(nomeRuoloDaVerificare)) {
						applicativoTokenRoles.add(idRuolo.getNome()); // nella xacml policy comunque inserisco l'identificativo del ruolo nel registro della PdD
					}
				}
				for (String role : tokenRoles) {
					if(!applicativoTokenRoles.contains(role)) {
						applicativoTokenRoles.addAll(tokenRoles); // inserisco anche i nomi originali
					}
				}
			}
			catch(DriverRegistroServiziNotFound notFound){
				if(!checkRuoloRegistro) {
					throw new AutorizzazioneException("Non sono stati registrati ruoli utilizzabili con un'autorizzazione esterna");
				}
			}
			catch(Exception e){
				throw new AutorizzazioneException("E' avvenuto un errore durante la ricerca dei ruoli: "+e.getMessage(),e);
			}
		}

		if(soggettoFruitore!=null){
			String subjectId = soggettoFruitore.toString();
			if(nomeServizioApplicativo!=null){
				subjectId = nomeServizioApplicativo + "." +subjectId;
			}
			xacmlRequest.addSubject(subjectId); // namespace standard xacml
		}
		else if(credential!=null){
			String subjectId = credential;
			xacmlRequest.addSubject(subjectId); // namespace standard xacml
		}

		if(soggettoFruitore!=null){
			xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_ORGANIZATION_ATTRIBUTE_ID, soggettoFruitore.toString());
		}
		if(soggettoFruitoreConfig!=null && !soggettoFruitoreConfig.isEmpty()) {
			Iterator<String> keys = soggettoFruitoreConfig.keySet().iterator();
			while (keys.hasNext()) {
				String key = (String) keys.next();
				String value = soggettoFruitoreConfig.get(key);
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_ORGANIZATION_CONFIG_ATTRIBUTE_PREFIX+key, value);
			}
		}
		
		if(nomeServizioApplicativo!=null){
			xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_CLIENT_ATTRIBUTE_ID, nomeServizioApplicativo);
		}
		if(applicativoConfig!=null && !applicativoConfig.isEmpty()) {
			Iterator<String> keys = applicativoConfig.keySet().iterator();
			while (keys.hasNext()) {
				String key = (String) keys.next();
				String value = applicativoConfig.get(key);
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_CLIENT_CONFIG_ATTRIBUTE_PREFIX+key, value);
			}
		}
		
		if(credential!=null){
			xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_CREDENTIAL_ATTRIBUTE_ID, credential);
		}
		
		if(roles!=null && roles.size()>0){
			xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_ROLE_ATTRIBUTE_ID, roles);
		}
				
		if(informazioniTokenUserInfoNormalizzate!=null) {
			if(informazioniTokenUserInfoNormalizzate.getFullName()!=null) {
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_USER_INFO_FULL_NAME_ATTRIBUTE_ID, informazioniTokenUserInfoNormalizzate.getFullName());
			}
			if(informazioniTokenUserInfoNormalizzate.getFirstName()!=null) {
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_USER_INFO_FIRST_NAME_ATTRIBUTE_ID, informazioniTokenUserInfoNormalizzate.getFirstName());
			}
			if(informazioniTokenUserInfoNormalizzate.getMiddleName()!=null) {
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_USER_INFO_MIDDLE_NAME_ATTRIBUTE_ID, informazioniTokenUserInfoNormalizzate.getMiddleName());
			}
			if(informazioniTokenUserInfoNormalizzate.getFamilyName()!=null) {
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_USER_INFO_FAMILY_NAME_ATTRIBUTE_ID, informazioniTokenUserInfoNormalizzate.getFamilyName());
			}
			if(informazioniTokenUserInfoNormalizzate.getEMail()!=null) {
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_USER_INFO_EMAIL_NAME_ATTRIBUTE_ID, informazioniTokenUserInfoNormalizzate.getEMail());
			}
		}
		if(informazioniTokenNormalizzate!=null) {
			if(informazioniTokenNormalizzate.getIss()!=null) {
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_TOKEN_ISSUER_ATTRIBUTE_ID, informazioniTokenNormalizzate.getIss());
			}
			if(informazioniTokenNormalizzate.getSub()!=null) {
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_TOKEN_SUBJECT_ATTRIBUTE_ID, informazioniTokenNormalizzate.getSub());
			}
			if(informazioniTokenNormalizzate.getUsername()!=null) {
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_TOKEN_USERNAME_ATTRIBUTE_ID, informazioniTokenNormalizzate.getUsername());
			}
			if(informazioniTokenNormalizzate.getClientId()!=null) {
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_TOKEN_CLIENT_ID_ATTRIBUTE_ID, informazioniTokenNormalizzate.getClientId());
			}
		}
		
		if(userInfoClaims!=null && userInfoClaims.size()>0) {
			Iterator<?> it = userInfoClaims.keySet().iterator();
			while (it.hasNext()) {
				String key = (String) it.next();
				Object value = userInfoClaims.get(key);
				key = normalizeKeyClaim(key);
				setSubjectAttribute(xacmlRequest, value, 
						XACMLCostanti.XACML_REQUEST_SUBJECT_TOKEN_USERINFO_CLAIMS_PREFIX+key);
			}
		}
		
		if(attributeNames!=null && !attributeNames.isEmpty()) {
			setSubjectAttribute(xacmlRequest, attributeNames, 
					XACMLCostanti.XACML_REQUEST_SUBJECT_ATTRIBUTE_ATTRIBUTE_NAMES_ID);
		}
		if(attributes!=null && attributes.size()>0){
			addAttributes(xacmlRequest, attributes, multipleAA);
		}
		
		if(idApplicativoToken!=null) {
			if(idApplicativoToken.getNome()!=null) {
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_TOKEN_CLIENT_APPLICATION_ATTRIBUTE_ID, idApplicativoToken.getNome());
			}
			if(applicativoTokenConfig!=null && !applicativoTokenConfig.isEmpty()) {
				Iterator<String> keys = applicativoTokenConfig.keySet().iterator();
				while (keys.hasNext()) {
					String key = (String) keys.next();
					String value = applicativoTokenConfig.get(key);
					xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_TOKEN_CLIENT_CONFIG_ATTRIBUTE_PREFIX+key, value);
				}
			}
			
			if(idApplicativoToken.getIdSoggettoProprietario()!=null) {
				xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_TOKEN_CLIENT_ORGANIZATION_ATTRIBUTE_ID, idApplicativoToken.getIdSoggettoProprietario().toString());
			}
			if(soggettoProprietarioApplicativoTokenConfig!=null && !soggettoProprietarioApplicativoTokenConfig.isEmpty()) {
				Iterator<String> keys = soggettoProprietarioApplicativoTokenConfig.keySet().iterator();
				while (keys.hasNext()) {
					String key = (String) keys.next();
					String value = soggettoProprietarioApplicativoTokenConfig.get(key);
					xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_TOKEN_CLIENT_ORGANIZATION_CONFIG_ATTRIBUTE_PREFIX+key, value);
				}
			}
			
		}
		
		if(applicativoTokenRoles!=null && applicativoTokenRoles.size()>0){
			xacmlRequest.addSubjectAttribute(XACMLCostanti.XACML_REQUEST_SUBJECT_TOKEN_CLIENT_ROLE_ATTRIBUTE_ID, applicativoTokenRoles);
		}

		
		
		// Environment
		
		xacmlRequest.createEnvironment();
		
		
		
		// Resource
		
		//solo in caso di pdp locale inizializzo la resource __resource_id___ con il nome del servizio in modo da consentire l'identificazione della policy
		
		xacmlRequest.addResourceAttribute(CachedMapBasedSimplePolicyRepository.RESOURCE_ATTRIBUTE_ID_TO_MATCH, policyKey);
		
		
		// Fill XAML
		if(httpServletRequest!=null && httpServletRequest instanceof AutorizzazioneHttpServletRequest) {
			AutorizzazioneHttpServletRequest authHttpServletRequest = (AutorizzazioneHttpServletRequest) httpServletRequest;
			authHttpServletRequest.fillXacmlRequest(xacmlRequest);
		}
		
		return xacmlRequest;


	}
	
	@SuppressWarnings("unchecked")
	private static Map<String,String> readConfig(PdDContext pddContext, MapKey<String> pName){
		Object o = pddContext.getObject(pName);
		if(o!=null && o instanceof Map<?, ?>) {
			return (Map<String, String>) o;
		}
		return null;
	}
	
	private static void addAttributes(XacmlRequest xacmlRequest, Map<String, Serializable> attributesParam, boolean multipleAA) {
		if(attributesParam!=null && !attributesParam.isEmpty()) {
			String logAA = "";
			if(multipleAA) {
				// informazioni normalizzate, devo scendere di un livello, senno al primo degli attributi ci sono le attribute authority
				for (String attrAuthName : attributesParam.keySet()) {
					Object o = attributesParam.get(attrAuthName);
					if(o instanceof Map) {
						try {
							List<String> attributesNames = new ArrayList<>();
							@SuppressWarnings("unchecked")
							Map<String, Serializable> attributes = (Map<String, Serializable>) o;
							if(attributes!=null && !attributes.isEmpty()) {
								for (String attrName : attributes.keySet()) {
									if(!attributesNames.contains(attrName)) {
										attributesNames.add(attrName);
									}
								}
							}
							Collections.sort(attributesNames);
							String xacmlIdPrefix = XACMLCostanti.XACML_REQUEST_SUBJECT_ATTRIBUTE_AUTHORITY_ATTRIBUTE_PREFIX+
									attrAuthName+
									XACMLCostanti._XACML_REQUEST_SUBJECT_ATTRIBUTE_ATTRIBUTE_PREFIX;
							JSONUtils jsonUtils = JSONUtils.getInstance();
							logAA = " (A.A. "+attrAuthName+")";
							for (String attributeName : attributesNames) {
								addAttribute(xacmlRequest, xacmlIdPrefix, attributes, attributeName,
										jsonUtils, logAA);
							}
						}catch(Throwable t) {
							OpenSPCoop2Logger.getLoggerOpenSPCoopCore().error("addAttributes failed (A.A. "+attrAuthName+"): "+t.getMessage(),t);
						}
					}
				}
				
			}
			else {
				List<String> attributesNames = new ArrayList<>();
				for (String attrName : attributesParam.keySet()) {
					attributesNames.add(attrName);
				}
				Collections.sort(attributesNames);
				String xacmlIdPrefix = XACMLCostanti.XACML_REQUEST_SUBJECT_ATTRIBUTE_ATTRIBUTE_PREFIX;
				JSONUtils jsonUtils = JSONUtils.getInstance();
				for (String attributeName : attributesNames) {
					addAttribute(xacmlRequest, xacmlIdPrefix, attributesParam, attributeName,
							jsonUtils, logAA);
				}
			}
		}
	}
	private static void addAttribute(XacmlRequest xacmlRequest, String xacmlIdPrefix, Map<String, Serializable> attributes, String attributeName,
			JSONUtils jsonUtils, String logAA) {
		Object oValue = attributes.get(attributeName);
		if(oValue!=null) {
			try {
				String key = normalizeKeyClaim(attributeName);
				setSubjectAttribute(xacmlRequest, oValue, 
						xacmlIdPrefix+key);
				
//				//System.out.println("TIPO: "+oValue.getClass().getName());
//				if(oValue instanceof String) {
//					String value = (String) oValue;
//					xacmlRequest.addSubjectAttribute(xacmlIdPrefix+attributeName, value);
//				}
//				else if(oValue instanceof List<?>) {
//					List<?> list = (List<?>) oValue;
//					if(list!=null && !list.isEmpty()) {
//						List<String> lValues = new ArrayList<>();
//						for (Object object : list) {
//							if(object!=null && object instanceof String) {
//								lValues.add((String)object);
//							}
//						}
//						if(!lValues.isEmpty()) {
//							xacmlRequest.addSubjectAttribute(xacmlIdPrefix+attributeName, lValues);
//						}
//					}
//				}
//				else {
//					throw new Exception("type '"+oValue.getClass().getName()+"' unmanaged");
//				}
			}catch(Throwable t) {
				OpenSPCoop2Logger.getLoggerOpenSPCoopCore().error("addAttribute '"+attributeName+"' failed"+logAA+": "+t.getMessage(),t);
			}
		}
	}
	
	private static String normalizeKeyClaim(String keyParam) {
		String key = keyParam;
		while(key.contains(".")) {
			key = key.replace(".", ":");
		}
		return key;
	}
	
	private static void setActionAttribute(XacmlRequest xacmlRequest, Object value, String claim) {
		setAttribute(xacmlRequest, value, claim, true);
	}
	private static void setSubjectAttribute(XacmlRequest xacmlRequest, Object value, String claim) {
		setAttribute(xacmlRequest, value, claim, false);
	}
	private static void setAttribute(XacmlRequest xacmlRequest, Object value, String claim, boolean action) {
		if(value!=null) {
			List<String> l = TokenUtilities.getClaimValues(value);
			if(l!=null && !l.isEmpty()) {
				if(l.size()>1) {
					if(action) {
						xacmlRequest.addActionAttribute(claim, l);
					}
					else {
						xacmlRequest.addSubjectAttribute(claim, l);
					}
				}
				else {
					if(action) {
						xacmlRequest.addActionAttribute(claim, l.get(0));
					}
					else {
						xacmlRequest.addSubjectAttribute(claim, l.get(0));
					}
				}
			}
		}
	}

}