MessageSecurityReceiver_impl.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.security.message.engine;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPHeaderElement;
import org.openspcoop2.message.OpenSPCoop2Message;
import org.openspcoop2.message.constants.ServiceBinding;
import org.openspcoop2.message.soap.reference.Reference;
import org.openspcoop2.protocol.sdk.Busta;
import org.openspcoop2.protocol.sdk.constants.CodiceErroreCooperazione;
import org.openspcoop2.security.SecurityException;
import org.openspcoop2.security.message.IMessageSecurityReceiver;
import org.openspcoop2.security.message.MessageSecurityContext;
import org.openspcoop2.security.message.MessageSecurityUtilities;
import org.openspcoop2.security.message.SubErrorCodeSecurity;
import org.openspcoop2.security.message.authorization.IMessageSecurityAuthorization;
import org.openspcoop2.security.message.authorization.MessageSecurityAuthorizationRequest;
import org.openspcoop2.security.message.authorization.MessageSecurityAuthorizationResult;
import org.openspcoop2.security.message.constants.SecurityConstants;
import org.openspcoop2.security.message.saml.SAMLBuilderConfigConstants;
/**
* Classe per la gestione della Sicurezza (role:Receiver)
*
* @author Lorenzo Nardi (nardi@link.it)
* @author $Author$
* @version $Rev$, $Date$
*
*/
public class MessageSecurityReceiver_impl extends MessageSecurityReceiver{
protected MessageSecurityReceiver_impl(MessageSecurityContext messageSecurityContext) {
super(messageSecurityContext);
}
@Override
protected boolean process(OpenSPCoop2Message message,Busta busta, org.openspcoop2.utils.Map<Object> ctx) {
try{
IMessageSecurityReceiver receiverInterface = this.messageSecurityContext.getMessageSecurityReceiver();
// ** Fix per SOAPFault (quando ci sono le encryptionParts o le signatureParts, la Sicurezza fallisce se c'e' un SOAPFault) **
if(ServiceBinding.SOAP.equals(message.getServiceBinding())){
if(message.isFault() || message.castAsSoap().getSOAPBody().hasFault()){
if(MessageSecurityUtilities.processSOAPFault(this.messageSecurityContext.getIncomingProperties()) == false){
return true; // non devo applicare la sicurezza.
}
}
}
else if(ServiceBinding.REST.equals(message.getServiceBinding())){
if(message.isFault() || message.castAsRest().isProblemDetailsForHttpApis_RFC7807()){
if(MessageSecurityUtilities.processProblemDetails(this.messageSecurityContext.getIncomingProperties()) == false){
return true; // non devo applicare la sicurezza.
}
}
}
String action = (String) this.messageSecurityContext.getIncomingProperties().get(SecurityConstants.ACTION);
if(action==null || "".equals(action.trim())){
return true; // nessuna action: non devo applicare la sicurezza.
}
// ** Inizializzazione parametri **
// Authentication class from properties
String authClass = (String) this.messageSecurityContext.getIncomingProperties().get(SecurityConstants.AUTHORIZATION_CLASS);
// MustUnderstand from properties
// boolean mustUnderstandValue = false;
// Object mustUnderstand = this.messageSecurityContext.getIncomingProperties().get(SecurityConstants.MUST_UNDERSTAND);
// if(mustUnderstand!=null){
// mustUnderstandValue = Boolean.parseBoolean((String)mustUnderstand);
// }
String actor = this.messageSecurityContext.getActor();
if("".equals(this.messageSecurityContext.getActor()))
actor = null;
// ** Verifico presenza header sicurezza ** /
if(receiverInterface.checkExistsWSSecurityHeader()) {
if(this.messageSecurityContext.existsSecurityHeader(message, actor)==false){
this.msgErrore = "Header Message Security, richiesto dalla configurazione (action:"+action+"), non riscontrato nella SOAPEnvelope ricevuta";
if(action.contains(SecurityConstants.ACTION_SIGNATURE)){
this.codiceErrore = CodiceErroreCooperazione.SICUREZZA_FIRMA_NON_PRESENTE;
}
else if(SecurityConstants.containsActionEncryption(action) ||
SecurityConstants.containsActionDecryption(action)){
this.codiceErrore = CodiceErroreCooperazione.SICUREZZA_CIFRATURA_NON_PRESENTE;
}
else{
this.codiceErrore = CodiceErroreCooperazione.SICUREZZA_NON_PRESENTE;
}
return false;
}
// se e' richiesta la verifica SAML la faccio.
if(action.contains(SecurityConstants.ACTION_SAML_TOKEN_SIGNED) || action.contains(SecurityConstants.ACTION_SAML_TOKEN_UNSIGNED)) {
String samlVersion = (String) this.messageSecurityContext.getIncomingProperties().get(SecurityConstants.SAML_VERSION_XMLCONFIG_ID);
if(samlVersion!=null && !"".equals(samlVersion)) {
SOAPHeaderElement securityHeader = this.messageSecurityContext.getSecurityHeader(message, actor); // verificato prima
SOAPElement samlToken = this.messageSecurityContext.getSAMLTokenInSecurityHeader(message.getFactory(), securityHeader, samlVersion);
if(samlToken==null) {
this.msgErrore = "SAMLToken (versione:"+samlVersion+"), richiesto dalla configurazione (action:"+action+"), non riscontrato nell'header Message Security presente all'interno della SOAPEnvelope ricevuta";
this.codiceErrore = CodiceErroreCooperazione.SICUREZZA_TOKEN_NON_PRESENTE;
return false;
}
String samlConfirmationType = (String) this.messageSecurityContext.getIncomingProperties().get(SecurityConstants.SAML_SUBJECT_CONFIRMATION_VALIDATION_METHOD_XMLCONFIG_ID);
if(samlConfirmationType!=null && !"".equals(samlConfirmationType)) {
String confirmationMethod = this.messageSecurityContext.getSAMLTokenSubjectConfirmationMethodInSecurityHeader(message.getFactory(), samlToken, samlVersion);
if(confirmationMethod==null) {
this.msgErrore = "SAMLToken (versione:"+samlVersion+"), richiesto dalla configurazione (action:"+action+"), non possiede un metodo di subject confirmation";
this.codiceErrore = CodiceErroreCooperazione.SICUREZZA_TOKEN_NON_VALIDO;
return false;
}
String atteso = null;
if(SecurityConstants.SAML_SUBJECT_CONFIRMATION_VALIDATION_METHOD_XMLCONFIG_ID_HOLDER_OF_KEY.equals(samlConfirmationType)) {
atteso = SecurityConstants.SAML_VERSION_XMLCONFIG_ID_VALUE_20.equals(samlVersion) ?
SAMLBuilderConfigConstants.SAML_CONFIG_BUILDER_SUBJECT_CONFIRMATION_METHOD_HOLDER_OF_KEY_SAML_20 :
SAMLBuilderConfigConstants.SAML_CONFIG_BUILDER_SUBJECT_CONFIRMATION_METHOD_HOLDER_OF_KEY_SAML_10;
}
else if(SecurityConstants.SAML_SUBJECT_CONFIRMATION_VALIDATION_METHOD_XMLCONFIG_ID_SENDER_VOUCHES.equals(samlConfirmationType)) {
atteso = SecurityConstants.SAML_VERSION_XMLCONFIG_ID_VALUE_20.equals(samlVersion) ?
SAMLBuilderConfigConstants.SAML_CONFIG_BUILDER_SUBJECT_CONFIRMATION_METHOD_SENDER_VOUCHES_SAML_20 :
SAMLBuilderConfigConstants.SAML_CONFIG_BUILDER_SUBJECT_CONFIRMATION_METHOD_SENDER_VOUCHES_SAML_10;
}
if(atteso!=null) {
if(atteso.equals(confirmationMethod)==false) {
this.msgErrore = "SAMLToken (versione:"+samlVersion+"), richiesto dalla configurazione (action:"+action+"), possiede un metodo di subject confirmation ("+confirmationMethod+") diverso da quello atteso ("+atteso+")";
this.codiceErrore = CodiceErroreCooperazione.SICUREZZA_TOKEN_NON_VALIDO;
return false;
}
}
}
}
}
}
// ** Raccolgo elementi "toccati" da Security per pulirli ** /
List<Reference> elementsToClean = null;
if(ServiceBinding.SOAP.equals(message.getServiceBinding())){
elementsToClean = receiverInterface.getDirtyElements(this.messageSecurityContext, message.castAsSoap());
}
// ** Verifica cifratura/firma elementi richiesti dalla configurazione ** /
List<SubErrorCodeSecurity> listaErroriRiscontrati = new ArrayList<SubErrorCodeSecurity>();
Map<QName, QName> notResolved = null;
if(ServiceBinding.SOAP.equals(message.getServiceBinding())){
notResolved = receiverInterface.checkEncryptSignatureParts(this.messageSecurityContext,elementsToClean, message.castAsSoap(),listaErroriRiscontrati);
}
if(listaErroriRiscontrati.size()>0){
StringBuilder bf = new StringBuilder();
for (Iterator<?> iterator = listaErroriRiscontrati.iterator(); iterator.hasNext();) {
SubErrorCodeSecurity subCodiceErroreSicurezza = (SubErrorCodeSecurity) iterator.next();
if(bf.length()>0){
bf.append(" ; ");
}
bf.append(subCodiceErroreSicurezza.getMsgErrore());
}
this.messageSecurityContext.getLog().error(bf.toString());
this.msgErrore = bf.toString();
this.codiceErrore = CodiceErroreCooperazione.SICUREZZA_NON_PRESENTE;
this.listaSubCodiceErrore.addAll(listaErroriRiscontrati);
return false;
}
// ** Applico sicurezza tramite engine **/
receiverInterface.process(this.messageSecurityContext, message, busta, ctx);
// ** Verifica elementi cifrati come element, possibile solo dopo la decifratura ** //
List<SubErrorCodeSecurity> listaErroriInElementi = new ArrayList<SubErrorCodeSecurity>();
if(ServiceBinding.SOAP.equals(message.getServiceBinding())){
receiverInterface.checkEncryptionPartElements(notResolved, message.castAsSoap(), listaErroriInElementi);
}
if(listaErroriInElementi.size()>0){
StringBuilder bf = new StringBuilder();
for (Iterator<?> iterator = listaErroriInElementi.iterator(); iterator.hasNext();) {
SubErrorCodeSecurity subCodiceErroreSicurezza = (SubErrorCodeSecurity) iterator.next();
if(bf.length()>0){
bf.append(" ; ");
}
bf.append(subCodiceErroreSicurezza.getMsgErrore());
}
this.messageSecurityContext.getLog().error(bf.toString());
this.msgErrore = bf.toString();
this.codiceErrore = CodiceErroreCooperazione.SICUREZZA_NON_PRESENTE;
this.listaSubCodiceErrore.addAll(listaErroriInElementi);
return false;
}
// ** Lettura Subject Certificato **/
try{
this.subject=receiverInterface.getCertificate();
}catch(Exception e){
this.messageSecurityContext.getLog().error("GetPrincipal Message-Security(Receiver) error: "+e.getMessage(),e);
}
// ** Autorizzazione Message Security **/
if (authClass != null) {
try {
IMessageSecurityAuthorization auth = (IMessageSecurityAuthorization)org.openspcoop2.utils.resources.Loader.getInstance().newInstance(authClass);
MessageSecurityAuthorizationRequest req = new MessageSecurityAuthorizationRequest();
req.setBusta(busta);
req.setSecurityPrincipal(this.subject);
req.setMessageSecurityContext(this.messageSecurityContext);
req.setMessage(message);
MessageSecurityAuthorizationResult result = auth.authorize(req);
if(!result.isAuthorized()){
this.msgErrore = "Mittente della busta ["+busta.getTipoMittente()+busta.getMittente()+
"] (subject:"+this.subject+") non autorizzato ad invocare il servizio ["+busta.getServizio()+"] erogato dal soggetto ["+busta.getTipoDestinatario()+busta.getDestinatario()+"]";
if(result.getErrorMessage()!=null){
this.msgErrore = this.msgErrore + "(" +result.getErrorMessage()+")";
}
if(result.getException()!=null){
this.messageSecurityContext.getLog().error(this.msgErrore,result.getException());
}
this.codiceErrore = CodiceErroreCooperazione.SICUREZZA_AUTORIZZAZIONE_FALLITA;
return false;
}
}
catch (Exception e) {
throw new Exception("Errore di Processamento durante l'autorizzazione: " + e.getMessage());
}
}
// ** Pulizia elementi "toccati" da MessageSecurity per pulirli ** /
// NOTA: Clean dipendente dall'implementazione
try{
boolean detachValue = true; // per default l'header WSS viene eliminato
Object detach = this.messageSecurityContext.getIncomingProperties().get(SecurityConstants.DETACH_HEADER_WSS); // per backward compatibility
if(detach!=null){
detachValue = Boolean.parseBoolean((String)detach);
}
if(detach==null) {
detach = this.messageSecurityContext.getIncomingProperties().get(SecurityConstants.DETACH_SECURITY_INFO);
if(detach!=null){
detachValue = Boolean.parseBoolean((String)detach);
}
}
boolean removeAllIdRefValue = this.messageSecurityContext.isRemoveAllWsuIdRef();
Object removeAllIdRef = this.messageSecurityContext.getIncomingProperties().get(SecurityConstants.REMOVE_ALL_WSU_ID_REF);
if(removeAllIdRef!=null){
removeAllIdRefValue = Boolean.parseBoolean((String)removeAllIdRef);
}
if(ServiceBinding.SOAP.equals(message.getServiceBinding())){
receiverInterface.cleanDirtyElements(this.messageSecurityContext, message.castAsSoap(), elementsToClean, detachValue, removeAllIdRefValue);
}
else {
if(detachValue) {
receiverInterface.detachSecurity(this.messageSecurityContext, message.castAsRest());
}
}
} catch (SecurityException e) {
this.messageSecurityContext.getLog().error("Errore durante il clean del messaggio: " + e.getMessage(), e);
throw new Exception("Errore durante la cleanMessage: " + e.getMessage());
}
} catch (Exception e) {
String prefix = "Generatosi errore durante il processamento Message-Security(Receiver): ";
this.messageSecurityContext.getLog().error(prefix+e.getMessage(),e);
this.msgErrore = prefix+"Generatosi errore durante il processamento Message-Security(Receiver): "+e.getMessage();
this.codiceErrore = CodiceErroreCooperazione.SICUREZZA;
if(e instanceof SecurityException){
SecurityException securityException = (SecurityException) e;
if(securityException.getMsgErrore()!=null){
this.msgErrore = prefix+securityException.getMsgErrore();
}
if(securityException.getCodiceErrore()!=null){
this.codiceErrore = securityException.getCodiceErrore();
}
}
return false;
}
return true;
}
}