MessageSecurityAuthorizationSAMLPolicy.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.security.message.authorization;

  21. import java.io.File;
  22. import java.net.URI;
  23. import java.net.URL;
  24. import java.util.ArrayList;
  25. import java.util.HashMap;
  26. import java.util.List;
  27. import java.util.Map;

  28. import javax.xml.soap.SOAPElement;
  29. import javax.xml.soap.SOAPException;

  30. import org.apache.wss4j.common.saml.builder.SAML2Constants;
  31. import org.herasaf.xacml.core.context.impl.DecisionType;
  32. import org.herasaf.xacml.core.context.impl.ResultType;
  33. import org.openspcoop2.core.id.IDServizio;
  34. import org.openspcoop2.core.registry.AccordoServizioParteSpecifica;
  35. import org.openspcoop2.core.registry.Documento;
  36. import org.openspcoop2.core.registry.constants.TipiDocumentoSicurezza;
  37. import org.openspcoop2.core.registry.driver.IDServizioFactory;
  38. import org.openspcoop2.message.OpenSPCoop2Message;
  39. import org.openspcoop2.message.OpenSPCoop2SoapMessage;
  40. import org.openspcoop2.message.constants.MessageType;
  41. import org.openspcoop2.message.soap.WSSecurityUtils;
  42. import org.openspcoop2.message.xml.MessageDynamicNamespaceContextFactory;
  43. import org.openspcoop2.message.xml.XPathExpressionEngine;
  44. import org.openspcoop2.protocol.registry.RegistroServiziManager;
  45. import org.openspcoop2.protocol.sdk.Busta;
  46. import org.openspcoop2.security.message.constants.SecurityConstants;
  47. import org.openspcoop2.security.message.saml.SAMLConstants;
  48. import org.openspcoop2.utils.resources.FileSystemUtilities;
  49. import org.openspcoop2.utils.transport.http.HttpConstants;
  50. import org.openspcoop2.utils.transport.http.HttpRequest;
  51. import org.openspcoop2.utils.transport.http.HttpRequestMethod;
  52. import org.openspcoop2.utils.transport.http.HttpResponse;
  53. import org.openspcoop2.utils.transport.http.HttpUtilities;
  54. import org.openspcoop2.utils.xacml.CachedMapBasedSimplePolicyRepository;
  55. import org.openspcoop2.utils.xacml.MarshallUtilities;
  56. import org.openspcoop2.utils.xacml.PolicyDecisionPoint;
  57. import org.openspcoop2.utils.xacml.PolicyException;
  58. import org.openspcoop2.utils.xacml.ResultCombining;
  59. import org.openspcoop2.utils.xacml.ResultUtilities;
  60. import org.openspcoop2.utils.xacml.XacmlRequest;
  61. import org.openspcoop2.utils.xml.AbstractXPathExpressionEngine;
  62. import org.openspcoop2.utils.xml.DynamicNamespaceContext;
  63. import org.openspcoop2.utils.xml.XPathException;
  64. import org.openspcoop2.utils.xml.XPathNotFoundException;
  65. import org.openspcoop2.utils.xml.XPathNotValidException;
  66. import org.openspcoop2.utils.xml.XPathReturnType;
  67. import org.slf4j.Logger;
  68. import org.w3c.dom.Node;
  69. import org.w3c.dom.NodeList;
  70. import org.xml.sax.SAXException;


  71. /**
  72.  * Implementazione dell'interfaccia Authorization basata su token SAML
  73.  *
  74.  * @author Bussu Giovanni (bussu@link.it)
  75.  * @author $Author$
  76.  * @version $Rev$, $Date$
  77.  */

  78. public class MessageSecurityAuthorizationSAMLPolicy  implements IMessageSecurityAuthorization{

  79.     public static final String SAML_20_ISSUER = "urn:oasis:names:tc:SAML:2.0:assertion:Issuer";
  80.     public static final String SAML_20_SUBJECT_NAME = "urn:oasis:names:tc:SAML:2.0:assertion:Subject:NameID";
  81.     public static final String SAML_20_ATTRIBUTE_NAME = "urn:oasis:names:tc:SAML:2.0:assertion:AttributeStatement:Attribute:Name:";
  82.    
  83.     public static final String SAML_11_ISSUER = "urn:oasis:names:tc:SAML:2.0:assertion:Issuer";
  84.     public static final String SAML_11_AUTHENTICATION_SUBJECT_NAME = "urn:oasis:names:tc:SAML:1.0:assertion:AuthenticationStatement:Subject:NameIdentifier";
  85.     public static final String SAML_11_AUTHORIZATION_SUBJECT_NAME = "urn:oasis:names:tc:SAML:1.0:assertion:AttributeStatement:Subject:NameIdentifier";
  86.     public static final String SAML_11_ATTRIBUTE_NAME = "urn:oasis:names:tc:SAML:1.0:assertion:AttributeStatement:Attribute:Name:";
  87.    
  88.    

  89.     private static PolicyDecisionPoint pdp;
  90.     private static synchronized void initPdD(Logger log) throws PolicyException{
  91.         if(MessageSecurityAuthorizationSAMLPolicy.pdp == null) {
  92.             MessageSecurityAuthorizationSAMLPolicy.pdp = new PolicyDecisionPoint(log);
  93.         }
  94.     }
  95.    
  96.    
  97.    
  98.    
  99.     public MessageSecurityAuthorizationSAMLPolicy()  {
  100.         PolicyDecisionPoint.runInitializers(); //necessario per far inizializzare gli unmarshaller in caso di pdp remoto
  101.     }


  102.     @Override
  103.     public MessageSecurityAuthorizationResult authorize(MessageSecurityAuthorizationRequest request) throws SecurityException{

  104.         String principalWSS = request.getSecurityPrincipal();
  105.         Busta busta = request.getBusta();
  106.         org.openspcoop2.security.message.MessageSecurityContext messageSecurityContext = request.getMessageSecurityContext();
  107.         OpenSPCoop2Message msg = request.getMessage();
  108.        

  109.         IDServizio idServizio = null;
  110.         try {
  111.             idServizio = IDServizioFactory.getInstance().getIDServizioFromValues(busta.getTipoServizio(), busta.getServizio(),
  112.                     busta.getTipoDestinatario(), busta.getDestinatario(), busta.getVersioneServizio());
  113.        
  114.             OpenSPCoop2SoapMessage soapMessage = msg.castAsSoap();
  115.            
  116.             // ****** Proprieta' WSSecurity ********
  117.            
  118.             String actor = messageSecurityContext.getActor();
  119.             if("".equals(messageSecurityContext.getActor()))
  120.                 actor = null;
  121.            
  122.             String pdpLocalString = (String) messageSecurityContext.getIncomingProperties().get(SecurityConstants.AUTH_PDP_LOCAL);
  123.             boolean pdpLocal = true;
  124.             if(pdpLocalString==null || "".equals(pdpLocalString.trim())){
  125.                 pdpLocal = true;
  126.             }
  127.             else if("true".equalsIgnoreCase(pdpLocalString.trim())){
  128.                 pdpLocal = true;
  129.             }
  130.             else if("false".equalsIgnoreCase(pdpLocalString.trim())){
  131.                 pdpLocal = false;
  132.             }
  133.             else{
  134.                 throw new SecurityException("Property '"+SecurityConstants.AUTH_PDP_LOCAL+"' with wrong value ["+pdpLocalString.trim()+"]");
  135.             }
  136.            
  137.             String remotePdD_url = null;
  138.             Integer remotePdD_connectionTimeout = HttpUtilities.HTTP_CONNECTION_TIMEOUT;
  139.             Integer remotePdD_readConnectionTimeout = HttpUtilities.HTTP_READ_CONNECTION_TIMEOUT;
  140.             if(!pdpLocal){
  141.                 remotePdD_url = (String) messageSecurityContext.getIncomingProperties().get(SecurityConstants.AUTH_PDP_REMOTE_URL);
  142.                 if(pdpLocalString==null || "".equals(pdpLocalString.trim())){
  143.                     throw new SecurityException("Property '"+SecurityConstants.AUTH_PDP_REMOTE_URL+"' not found (required with "+SecurityConstants.AUTH_PDP_LOCAL+"=false)");
  144.                 }
  145.                 pdpLocalString = pdpLocalString.trim();
  146.                 try{
  147.                     (new URI(remotePdD_url)).toString();
  148.                 }catch(Exception e){
  149.                     throw new SecurityException("Property '"+SecurityConstants.AUTH_PDP_REMOTE_URL+"' with wrong value ["+remotePdD_url+"]: "+e.getMessage(),e);
  150.                 }
  151.                
  152.                 String tmp = (String) messageSecurityContext.getIncomingProperties().get(SecurityConstants.AUTH_PDP_REMOTE_CONNECTION_TIMEOUT);
  153.                 if(tmp!=null && !"".equals(tmp)){
  154.                     tmp = tmp.trim();
  155.                     try{
  156.                         remotePdD_connectionTimeout = Integer.parseInt(tmp);
  157.                     }catch(Exception e){
  158.                         throw new SecurityException("Property '"+SecurityConstants.AUTH_PDP_REMOTE_CONNECTION_TIMEOUT+"' with wrong value ["+tmp+"]: "+e.getMessage(),e);
  159.                     }
  160.                 }
  161.                
  162.                 tmp = (String) messageSecurityContext.getIncomingProperties().get(SecurityConstants.AUTH_PDP_REMOTE_READ_CONNECTION_TIMEOUT);
  163.                 if(tmp!=null && !"".equals(tmp)){
  164.                     tmp = tmp.trim();
  165.                     try{
  166.                         remotePdD_readConnectionTimeout = Integer.parseInt(tmp);
  167.                     }catch(Exception e){
  168.                         throw new SecurityException("Property '"+SecurityConstants.AUTH_PDP_REMOTE_READ_CONNECTION_TIMEOUT+"' with wrong value ["+tmp+"]: "+e.getMessage(),e);
  169.                     }
  170.                 }
  171.             }
  172.            
  173.            
  174.            
  175.            
  176.            
  177.             // ****** Raccolta Dati ********
  178.            
  179.             String tipoSoggettoErogatore = busta.getTipoDestinatario();
  180.             String nomeSoggettoErogatore = busta.getDestinatario();
  181.             String tipoServizio = busta.getTipoServizio();
  182.             String nomeServizio = busta.getServizio();
  183.             String azione = busta.getAzione() != null ? busta.getAzione() : "";
  184.    
  185.             String servizioKey = "http://"+tipoSoggettoErogatore+nomeSoggettoErogatore+".openspcoop2.org/servizi/"+tipoServizio+nomeServizio;
  186.             String azioneKey = servizioKey+"/" + azione;
  187.            
  188.             if(pdpLocal){
  189.                
  190.                 byte[] policy = null;
  191.                 try{
  192.                     AccordoServizioParteSpecifica asps = RegistroServiziManager.getInstance().getAccordoServizioParteSpecifica(idServizio, null, true, null); // non serve requestInfo tanto ho necessita degli allegati
  193.                     for (int i = 0; i < asps.sizeSpecificaSicurezzaList(); i++) {
  194.                         Documento d = asps.getSpecificaSicurezza(i);
  195.                         if(TipiDocumentoSicurezza.XACML_POLICY.getNome().equals(d.getTipo())){
  196.                             if(policy == null){
  197.                                 if(d.getByteContenuto()!=null){
  198.                                     policy = d.getByteContenuto();  
  199.                                 }
  200.                                 else if(d.getFile()!=null){
  201.                                     if(d.getFile().startsWith("http://") || d.getFile().startsWith("file://")){
  202.                                         URL url = new URL(d.getFile());
  203.                                         policy = HttpUtilities.requestHTTPFile(url.toString());
  204.                                     }
  205.                                     else{
  206.                                         File f = new File(d.getFile());
  207.                                         policy = FileSystemUtilities.readBytesFromFile(f);
  208.                                     }
  209.                                 }
  210.                             }else
  211.                                 throw new SecurityException("Piu di una xacml policy trovata trovata per il servizio "+idServizio.toString());
  212.                         }
  213.                     }
  214.                 }catch(Exception e){
  215.                     throw new SecurityException("Errore durante la ricerca delle policies xacml per il servizio "+idServizio.toString()+": "+e.getMessage(),e);
  216.                 }
  217.                 if(policy== null){
  218.                     throw new SecurityException("Nessuna xacml policy trovata per il servizio "+idServizio.toString());
  219.                 }
  220.                
  221.                 if(MessageSecurityAuthorizationSAMLPolicy.pdp == null) {
  222.                     MessageSecurityAuthorizationSAMLPolicy.initPdD(messageSecurityContext.getLog());
  223.                 }
  224.                
  225.                 // Caricamento in PdP vedendo che la policy non sia gia stata caricata ....
  226.                 MessageSecurityAuthorizationSAMLPolicy.pdp.addPolicy(MarshallUtilities.unmarshallPolicy(policy), servizioKey);
  227.             }
  228.            
  229.            
  230.            
  231.            
  232.             // ****** Header WSSecurity con SAML ********
  233.            
  234.             SOAPElement security = null;
  235.             try{
  236.                 security = (SOAPElement) WSSecurityUtils.getSecurityHeader(soapMessage.getSOAPPart(), soapMessage.getMessageType(), actor, true);
  237.             }catch(Exception e){
  238.                 throw new SecurityException("Errore durante la ricerca dell'header WSSecurity (actor:"+actor+") "+e.getMessage(),e);
  239.             }
  240.             if(security==null){
  241.                 throw new SecurityException("Header WSSecurity (actor:"+actor+") contenente una SAML non trovato");
  242.             }
  243.            
  244.             DynamicNamespaceContext dnc = null;
  245.             if(MessageType.SOAP_11.equals(soapMessage.getMessageType())) {
  246.                 dnc = MessageDynamicNamespaceContextFactory.getInstance(soapMessage.getFactory()).getNamespaceContextFromSoapEnvelope11(soapMessage.getSOAPPart().getEnvelope());
  247.             } else {
  248.                 dnc = MessageDynamicNamespaceContextFactory.getInstance(soapMessage.getFactory()).getNamespaceContextFromSoapEnvelope12(soapMessage.getSOAPPart().getEnvelope());
  249.             }
  250.            
  251.             AbstractXPathExpressionEngine xpathExpressionEngine = new XPathExpressionEngine(soapMessage.getFactory());
  252.                        
  253.             boolean SAML_2_0 = false;
  254.             try {
  255.                 Object o = xpathExpressionEngine.getMatchPattern(security, dnc, SAMLConstants.XPATH_SAML_20_ASSERTION, XPathReturnType.NODE);
  256.                 SAML_2_0 = (o!=null);
  257.             } catch(XPathNotFoundException e) {
  258.                 SAML_2_0 = false;
  259.             }

  260.            
  261.            
  262.            
  263.            
  264.             // ****** Produzione XACMLRequest a partire dalla SAML ********
  265.        
  266.             XacmlRequest xacmlRequest = new XacmlRequest();
  267.            
  268.        
  269.             // ** action **
  270.             xacmlRequest.addAction(azioneKey);
  271.    
  272.            
  273.             // ** subject **
  274.             // Creare un Subject che contiene nel subject id:
  275.             // come urn:oasis:names:tc:xacml:1.0:subject:subject-id il valore del principal presente nella richiesta (principalWSS)
  276.             if(principalWSS != null) {
  277.                 xacmlRequest.addSubjectAttribute("urn:oasis:names:tc:xacml:1.0:subject:subject-id", principalWSS);
  278.             }
  279.            
  280.             // inoltre creare altri attribute del subject che contengano:
  281.             SAMLAttributes saml = new SAMLAttributes(security, dnc, SAML_2_0, xpathExpressionEngine);

  282.             // il valore del NameID all'interno dell'elemento Subject
  283.             if(SAML_2_0){
  284.                 if(saml.nameIDAuthorization!=null){
  285.                     xacmlRequest.addSubjectAttribute(SAML_20_SUBJECT_NAME, saml.nameIDAuthorization);
  286.                 }
  287.             }
  288.             else{
  289.                 if(saml.nameIDAuthorization!=null){
  290.                     xacmlRequest.addSubjectAttribute(SAML_11_AUTHORIZATION_SUBJECT_NAME, saml.nameIDAuthorization);
  291.                 }
  292.                 if(saml.nameIDAuthentication!=null){
  293.                     xacmlRequest.addSubjectAttribute(SAML_11_AUTHENTICATION_SUBJECT_NAME, saml.nameIDAuthentication);
  294.                 }
  295.             }
  296.    
  297.             // il valore dell'elemento Issuer
  298.             if(SAML_2_0){
  299.                 xacmlRequest.addSubjectAttribute(SAML_20_ISSUER, saml.issuer);
  300.             }
  301.             else{
  302.                 xacmlRequest.addSubjectAttribute(SAML_11_ISSUER, saml.issuer);
  303.             }
  304.            
  305.             // - AttributeId="urn:oasis:names:tc:SAML:XX:assertion/AttributeStatement/Attribute/Name/<Nome> se il NameFormat non è una uri altrimenti direttamente il nome come attribute Id
  306.             for(String keyAttribute: saml.customAttributes.keySet()) {
  307.                 xacmlRequest.addSubjectAttribute(keyAttribute, saml.customAttributes.get(keyAttribute));
  308.             }
  309.    
  310.            
  311.             // ** environment **
  312.            
  313.             xacmlRequest.createEnvironment();
  314.    
  315.            
  316.            
  317.            
  318.             // ****** Valutazione XACMLRequest con PdD ********
  319.            
  320.             List<ResultType> results = null;
  321.             if(pdpLocal){
  322.                 try {
  323.                    
  324.                     //solo in caso di pdp locale inizializzo la resource __resource_id___ con il nome del servizio in modo da consentire l'identificazione della policy
  325.                    
  326.                     xacmlRequest.addResourceAttribute(CachedMapBasedSimplePolicyRepository.RESOURCE_ATTRIBUTE_ID_TO_MATCH, servizioKey);
  327.                            
  328.                     messageSecurityContext.getLog().debug("----XACML Request locale begin ---");
  329.                     messageSecurityContext.getLog().debug(new String(MarshallUtilities.marshallRequest(xacmlRequest)));
  330.                     messageSecurityContext.getLog().debug("----XACML Request locale end ---");
  331.                    
  332.                     results = MessageSecurityAuthorizationSAMLPolicy.pdp.evaluate(xacmlRequest);
  333.                     messageSecurityContext.getLog().debug("----XACML Results begin ---");
  334.                     for(ResultType result: results) {
  335.                         messageSecurityContext.getLog().debug("Decision: "+result.getDecision().toString());
  336.                     }
  337.                     messageSecurityContext.getLog().debug("----XACML Results end ---");

  338.                 } catch(Exception e) {
  339.                     throw new SecurityException("Errore avvenuto durante l'interrogazione del PdP locale per l'autorizzazione del servizio "+idServizio.toString()+": "+e.getMessage(),e);
  340.                 }
  341.             }
  342.             else{
  343.                 try{
  344.                     xacmlRequest.createResource();
  345.                    
  346.                     byte[] xacmlBytes = MarshallUtilities.marshallRequest(xacmlRequest);
  347.                     messageSecurityContext.getLog().debug("----XACML Request remota begin ---");
  348.                     messageSecurityContext.getLog().debug(new String(xacmlBytes));
  349.                     messageSecurityContext.getLog().debug("----XACML Request remota end ---");

  350.                     HttpRequest httpRequest = new HttpRequest();
  351.                     httpRequest.setUrl(remotePdD_url);
  352.                     httpRequest.setContent(xacmlBytes);
  353.                     httpRequest.setMethod(HttpRequestMethod.POST);
  354.                     httpRequest.setContentType(HttpConstants.CONTENT_TYPE_TEXT_XML);
  355.                     httpRequest.setReadTimeout(remotePdD_readConnectionTimeout);
  356.                     httpRequest.setConnectTimeout(remotePdD_connectionTimeout);
  357.                     HttpResponse httpResponse = HttpUtilities.httpInvoke(httpRequest);
  358.                     if(httpResponse.getResultHTTPOperation()==200){
  359.                         byte[] res = httpResponse.getContent();
  360.                         results = MarshallUtilities.unmarshallResult(res);
  361.                     }
  362.                     else{
  363.                         throw new Exception("invocazione fallita (http-return-code: "+httpResponse.getResultHTTPOperation()+")");
  364.                     }
  365.                 }catch(Exception e){
  366.                     throw new SecurityException("Errore avvenuto durante l'interrogazione del PdP remoto ["+remotePdD_url+"] per l'autorizzazione del servizio "+idServizio.toString()+": "+e.getMessage(),e);
  367.                 }
  368.             }
  369.            
  370.             DecisionType decision = ResultCombining.combineDenyOverrides(results);
  371.    
  372.             MessageSecurityAuthorizationResult result = new MessageSecurityAuthorizationResult();
  373.    
  374.             if(DecisionType.PERMIT.equals(decision)) {
  375.                 result.setAuthorized(true);
  376.                 result.setErrorMessage(null);
  377.             } else {
  378.                
  379.                 String url = "";
  380.                 String tipo = "XACML-Policy";
  381.                 if(!pdpLocal){
  382.                     url = " url["+remotePdD_url+"]";
  383.                     tipo = "XACML-Policy-RemotePdp";
  384.                 }
  385.                 try{
  386.                     String resultAsString = ResultUtilities.toRawString(results);
  387.                     messageSecurityContext.getLog().error("Autorizzazione con XACMLPolicy fallita pddLocal["+pdpLocal+"]"+url+"; results (size:"+results.size()+"): \n"+resultAsString);
  388.                 }catch(Throwable e){
  389.                     messageSecurityContext.getLog().error("Autorizzazione con XACMLPolicy fallita pddLocal["+pdpLocal+"]"+url+". Serializzazione risposta non riuscita",e);
  390.                 }
  391.                
  392.                 result.setAuthorized(false);
  393.                    
  394.                 String resultAsString = ResultUtilities.toString(results, decision);
  395.                 if(resultAsString!=null && resultAsString.length()>0){
  396.                     result.setErrorMessage(tipo+" "+resultAsString);
  397.                 }
  398.                 else{
  399.                     result.setErrorMessage(tipo);
  400.                 }
  401.             }
  402.             return result;
  403.            
  404.         } catch(SecurityException e) {
  405.             messageSecurityContext.getLog().error("Errore di sicurezza durante la gestione della richiesta per il servizio: " + idServizio, e);
  406.             throw e;
  407.         } catch(Exception e) {
  408.             messageSecurityContext.getLog().error("Errore generico durante la gestione della richiesta per il servizio: " + idServizio, e);
  409.             throw new SecurityException(e);
  410.         }
  411.     }

  412.  
  413.    
  414.     private class SAMLAttributes {
  415.         public String nameIDAuthentication;
  416.         public String nameIDAuthorization;
  417.         public String issuer;
  418.         public Map<String, List<String>> customAttributes;
  419.        
  420.         private DynamicNamespaceContext dnc;
  421.         private boolean saml20;
  422.         private AbstractXPathExpressionEngine xpathExpressionEngine;
  423.        
  424.         public SAMLAttributes(SOAPElement security, DynamicNamespaceContext dnc, boolean saml20, AbstractXPathExpressionEngine xpathExpressionEngine) throws Exception {
  425.             this.dnc = dnc;
  426.             this.saml20 = saml20;
  427.             this.xpathExpressionEngine = xpathExpressionEngine;
  428.             if(this.saml20){
  429.                 this.nameIDAuthorization = find(security, SAMLConstants.XPATH_SAML_20_ASSERTION_SUBJECT_NAMEID,false);
  430.                 this.issuer = find(security,  SAMLConstants.XPATH_SAML_20_ASSERTION_ISSUER,true);
  431.             }
  432.             else{
  433.                 this.nameIDAuthorization = find(security, SAMLConstants.XPATH_SAML_11_ASSERTION_ATTRIBUTESTATEMENT_SUBJECT_NAMEID,false);
  434.                 this.nameIDAuthentication = find(security, SAMLConstants.XPATH_SAML_11_ASSERTION_AUTHENTICATIONSTATEMENT_SUBJECT_NAMEID,false);
  435.                 this.issuer = find(security,  SAMLConstants.XPATH_SAML_11_ASSERTION_ISSUER,true);
  436.             }
  437.             this.customAttributes = findNameAttributes(security);
  438.         }

  439.         private String find(SOAPElement security,String xpath, boolean required) throws SAXException, SOAPException,
  440.                 XPathException, XPathNotValidException, Exception {
  441.            
  442.             String match = null;
  443.             try {
  444.                 match = (String) this.xpathExpressionEngine.getMatchPattern(security, this.dnc, xpath, XPathReturnType.STRING);
  445.             } catch(XPathNotFoundException e) {
  446.                 if(required)
  447.                     throw new Exception(e.getMessage(),e);
  448.                 else
  449.                     return null;
  450.             }
  451.             return match;
  452.         }

  453.         private Map<String, List<String>> findNameAttributes(SOAPElement security) throws SAXException, SOAPException,
  454.                 XPathException, XPathNotValidException, Exception {
  455.            
  456.             Map<String, List<String>> nameAttributes = new HashMap<>();

  457.             String xpath = null;
  458.             if(this.saml20){
  459.                 xpath = SAMLConstants.XPATH_SAML_20_ASSERTION_ATTRIBUTESTATEMENT_ATTRIBUTE;
  460.             }
  461.             else{
  462.                 xpath = SAMLConstants.XPATH_SAML_11_ASSERTION_ATTRIBUTESTATEMENT_ATTRIBUTE;
  463.             }
  464.             try {
  465.                 NodeList attributes = (NodeList) this.xpathExpressionEngine.getMatchPattern(security, this.dnc, xpath, XPathReturnType.NODESET);
  466.                 if(attributes != null) {
  467.                     for(int i = 0; i < attributes.getLength(); i++) {
  468.                         org.w3c.dom.Node attribute = attributes.item(i);
  469.                         String name = null;
  470.                         String friendlyName = null;
  471.                         boolean uriNameFormat = false;
  472.                         if(this.saml20){
  473.                             Node nameAttribute = attribute.getAttributes().getNamedItem("Name");
  474.                             name = nameAttribute.getNodeValue();
  475.                             Node friendlyNameAttribute = attribute.getAttributes().getNamedItem("FriendlyName");
  476.                             if(friendlyNameAttribute!=null){
  477.                                 friendlyName = friendlyNameAttribute.getNodeValue();
  478.                             }
  479.                             Node nameFormatAttribute = attribute.getAttributes().getNamedItem("NameFormat");
  480.                             if(nameFormatAttribute!=null){
  481.                                 uriNameFormat = SAML2Constants.ATTRNAME_FORMAT_URI.equals(nameFormatAttribute.getNodeValue());
  482.                             }  
  483.                         }
  484.                         else{
  485.                             Node nameAttribute = attribute.getAttributes().getNamedItem("AttributeNamespace");
  486.                             name = nameAttribute.getNodeValue();
  487.                             Node friendlyNameAttribute = attribute.getAttributes().getNamedItem("AttributeName");
  488.                             friendlyName = friendlyNameAttribute.getNodeValue(); // obbligatorio in saml11
  489.                         }
  490.                         if(uriNameFormat==false){
  491.                             if(friendlyName==null){
  492.                                 if(name.startsWith("urn:") || name.startsWith("url:") || name.startsWith("http:") || name.startsWith("htts:")){
  493.                                     uriNameFormat = true;
  494.                                 }
  495.                             }
  496.                         }

  497.                         String key = null;
  498.                         if(friendlyName != null) {
  499.                             if(this.saml20){
  500.                                 key = SAML_20_ATTRIBUTE_NAME + friendlyName;
  501.                             }
  502.                             else{
  503.                                 key = SAML_11_ATTRIBUTE_NAME + friendlyName;
  504.                             }
  505.                         } else {
  506.                             if(uriNameFormat) {
  507.                                 key = name;
  508.                             } else {
  509.                                 if(this.saml20){
  510.                                     key = SAML_20_ATTRIBUTE_NAME + name;
  511.                                 }
  512.                                 else{
  513.                                     key = SAML_11_ATTRIBUTE_NAME + name;
  514.                                 }
  515.                             }
  516.                         }

  517.                         String samlNamespace = null;
  518.                         if(this.saml20){
  519.                             samlNamespace = SAMLConstants.SAML_20_NAMESPACE;
  520.                         }else{
  521.                             samlNamespace = SAMLConstants.SAML_11_NAMESPACE;
  522.                         }
  523.                        
  524.                         List<String> values = null;
  525.                         if(nameAttributes.containsKey(key)) {
  526.                             values = nameAttributes.remove(key);
  527.                         } else {
  528.                             values = new ArrayList<>();
  529.                         }
  530.                         NodeList attributeValues = attribute.getChildNodes();
  531.                         if(attributeValues != null) {
  532.                             for(int j = 0; j < attributeValues.getLength(); j++) {
  533.                                 org.w3c.dom.Node attributeValue = attributeValues.item(j);
  534.                                 if(attributeValue != null && "AttributeValue".equals(attributeValue.getLocalName()) &&
  535.                                         samlNamespace.equals(attributeValue.getNamespaceURI())) {
  536.                                     values.add(attributeValue.getTextContent());
  537.                                 }
  538.                             }
  539.                         }
  540.                         nameAttributes.put(key, values);
  541.                     }
  542.                 }
  543.             } catch(XPathNotFoundException e) {
  544.                 throw new Exception("Impossibile trovare l'xPath ["+xpath+"]");
  545.             }
  546.             return nameAttributes;
  547.         }
  548.     }

  549.    
  550. }