ch.simuonline.idh.attribute.resolver.dc.aq.AttributeQueryDataConnector.java Source code

Java tutorial

Introduction

Here is the source code for ch.simuonline.idh.attribute.resolver.dc.aq.AttributeQueryDataConnector.java

Source

/*
 * 
 * Copyright 2016 Simon Gfeller
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package ch.simuonline.idh.attribute.resolver.dc.aq;

import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nonnull;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;

import org.joda.time.DateTime;
import org.joda.time.chrono.ISOChronology;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.XMLObjectBuilderFactory;
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.core.xml.io.MarshallingException;
import org.opensaml.core.xml.schema.XSString;
import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.messaging.context.InOutOperationContext;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.saml.common.SAMLVersion;
import org.opensaml.saml.common.assertion.ValidationContext;
import org.opensaml.saml.common.assertion.ValidationResult;
import org.opensaml.saml.saml2.assertion.SAML20AssertionValidator;
import org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.Attribute;
import org.opensaml.saml.saml2.core.AttributeQuery;
import org.opensaml.saml.saml2.core.AttributeStatement;
import org.opensaml.saml.saml2.core.Conditions;
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.Response;
import org.opensaml.saml.saml2.core.Subject;
import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;
import org.opensaml.security.x509.X509Credential;
import org.opensaml.security.x509.X509Support;
import org.opensaml.security.x509.impl.X509KeyManagerX509CredentialAdapter;
import org.opensaml.soap.client.http.HttpSOAPClient;
import org.opensaml.soap.messaging.context.SOAP11Context;
import org.opensaml.soap.soap11.Body;
import org.opensaml.soap.soap11.Envelope;
import org.opensaml.xmlsec.impl.StaticSignatureValidationParametersResolver;
import org.opensaml.xmlsec.signature.Signature;
import org.opensaml.xmlsec.signature.support.SignatureException;
import org.opensaml.xmlsec.signature.support.SignatureValidator;
import org.opensaml.xmlsec.signature.support.impl.BaseSignatureTrustEngine;
import org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;

import ch.simuonline.idh.webflow.AttributeQueryContext;
import net.shibboleth.idp.attribute.IdPAttribute;
import net.shibboleth.idp.attribute.IdPAttributeValue;
import net.shibboleth.idp.attribute.StringAttributeValue;
import net.shibboleth.idp.attribute.resolver.AbstractDataConnector;
import net.shibboleth.idp.attribute.resolver.ResolutionException;
import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
import net.shibboleth.idp.relyingparty.impl.SigningCredentialsResolver;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.httpclient.HttpClientBuilder;
import net.shibboleth.utilities.java.support.httpclient.TLSSocketFactoryBuilder;
import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.primitive.StringSupport;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.xml.BasicParserPool;
import net.shibboleth.utilities.java.support.xml.SerializeSupport;

/**
 * Data Connector which resolves attributes with an SAML2 attribute query to an attribute authority
 * Visit {@link https://github.com/gfels4/identity_hub} for more informations.
 * 
 * @author Simon Gfeller
 *
 */
public class AttributeQueryDataConnector extends AbstractDataConnector {

    /** Class logger. */
    @Nonnull
    private final Logger log = LoggerFactory.getLogger(AttributeQueryDataConnector.class);

    /** Strategy to resolve target informations for the query*/
    @NonnullAfterInit
    private TargetDeterminationStrategy targetResolvingStrategy;

    /** Builder for the attribute Query element */
    @NonnullAfterInit
    private AttributeQueryBuilder attributeQueryBuilder;

    /** Builder for the attribute Query element */
    @NonnullAfterInit
    private AttributeQueryKeyManager keyManager;

    /** Determines if a signature is required or not */
    private boolean signatureRequired;

    /** Determines if an AttributeConsumerService with requestedAttributes from the SP Metadatas is required  */
    private boolean requireMetadataAttributes;

    public void setRequireMetadataAttributes(boolean requireMetadataAttributes) {
        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);

        this.requireMetadataAttributes = requireMetadataAttributes;
    }

    public void setSignatureRequired(boolean signatureRequired) {
        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);

        this.signatureRequired = signatureRequired;
    }

    /**
     * Sets the {@link AttributeQueryBuilder} used for building the attribute query element
     * 
     * @param newAttributeQueryBuilder the new {@link AttributeQueryBuilder}
     */
    public void setAttributeQueryBuilder(@Nonnull final AttributeQueryBuilder newAttributeQueryBuilder) {
        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);

        this.attributeQueryBuilder = Constraint.isNotNull(newAttributeQueryBuilder,
                "AttributeQueryBuilder can not be null");
    }

    /**
     * Sets the {@link AttributeQueryKeyManager} which holds the private key and the certificate for signing the 
     * attribute query and also for the TLS connection
     * 
     * @param newKeyManager The {@link AttributeQueryKeyManager} holding the certificate and the private key
     */
    public void setAttributeQueryKeyManager(@Nonnull final AttributeQueryKeyManager newKeyManager) {
        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);

        this.keyManager = Constraint.isNotNull(newKeyManager, "AttributeQueryKeyManager can not be null");
    }

    /**
     * Sets the {@link TargetResolvingStrategy} used for the attribute query, its used
     * to get target informations for the AttributeQuery.
     * 
     * @param newTargetResolvingStrategy The {@link TargetResolvingStrategy} used for the AttributeQuery
     */
    public void setTargetResolvingStrategy(@Nonnull final TargetDeterminationStrategy newTargetResolvingStrategy) {
        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);

        this.targetResolvingStrategy = Constraint.isNotNull(newTargetResolvingStrategy,
                "TargetResolvingStrategy can not be null");
    }

    /** {@inheritDoc} */
    @Override
    @Nonnull
    protected Map<String, IdPAttribute> doDataConnectorResolve(
            @Nonnull final AttributeResolutionContext resolutionContext,
            @Nonnull final AttributeResolverWorkContext workContext) throws ResolutionException {
        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
        final DateTime start = DateTime.now();
        log.debug("{} Begin with resolving Attributes, the principal is: {}, Start time of AQ Extension: {}!",
                getLogPrefix(), resolutionContext.getPrincipal(), start);

        // get informations about the query target
        AQTarget queryTarget = targetResolvingStrategy.resolveTargetInformations(resolutionContext);
        if (queryTarget == null || StringSupport.trimOrNull(queryTarget.getNameID()) == null
                || (signatureRequired && queryTarget.getCertificate() == null)
                || StringSupport.trimOrNull(queryTarget.getEntityID()) == null
                || StringSupport.trimOrNull(queryTarget.getAttributeQueryURL()) == null) {
            log.warn("{} No working target informations for the attribute query found for principal: {}",
                    getLogPrefix(), resolutionContext.getPrincipal());
            return null;
        }

        // get the aq context and check if there are attributes to resolve from the metadata of this service provider
        AttributeQueryContext aqcontext = resolutionContext.getSubcontext(AttributeQueryContext.class, false);
        List<AQAttribute> attributesToResolve = null;
        if (aqcontext != null) {
            attributesToResolve = aqcontext.getAttributesToResolve();
        }

        // if there are no attributes to resolve from the metadatas, no attribtue query is needed
        if ((attributesToResolve == null || attributesToResolve.isEmpty()) && requireMetadataAttributes) {
            log.warn("{} Attributes from the SP Metadata required, but no attributes found", getLogPrefix());
            return null;
        }

        // Building the Attribute Query
        AttributeQuery query = attributeQueryBuilder.buildAttributeQuery(queryTarget, keyManager,
                attributesToResolve);
        if (query == null) {
            log.warn("{} Attribute query build failed", getLogPrefix());
            return null;
        }

        // Method for creating the SOAP Envelope
        Envelope envelope = createSOAPEnvelope(query);

        try {
            Element dom = XMLObjectSupport.marshall(envelope);
            log.debug("{} Print attribute query with envelope {}", getLogPrefix(),
                    SerializeSupport.prettyPrintXML(dom));
        } catch (MarshallingException e) {
            log.debug("{} Unable to print out created attribute query", getLogPrefix());
        }

        // Create InOutOperationContext to send the soap envelope
        InOutOperationContext<Envelope, Envelope> ioCtx = new InOutOperationContext<Envelope, Envelope>(null, null);
        ioCtx.setOutboundMessageContext(new MessageContext<Envelope>());
        ioCtx.getOutboundMessageContext().getSubcontext(SOAP11Context.class, true).setEnvelope(envelope);

        // Get a SOAP CLient
        HttpSOAPClient soapClient = getSOAPClient();

        // Send the AttributeQuery
        try {
            soapClient.send(queryTarget.getAttributeQueryURL(), ioCtx);
        } catch (Exception e) {
            log.warn("{} Sending SOAP Message failed {}", getLogPrefix(), e);
            return null;
        }

        // Get the Response Envelope
        Envelope SOAPResponse = ioCtx.getInboundMessageContext().getSubcontext(SOAP11Context.class, false)
                .getEnvelope();
        if (SOAPResponse == null || SOAPResponse.getBody() == null) {
            log.warn("{} No SOAP Response Recieved", getLogPrefix());
            return null;
        }

        Response samlResponse = null;
        final List<XMLObject> bodyElements = SOAPResponse.getBody().getUnknownXMLObjects();
        for (XMLObject element : bodyElements) {
            if (element instanceof Response) {
                samlResponse = (Response) element;
            }
        }

        if (samlResponse == null) {
            log.warn("{} SOAP Response Body containts no SAML Response!", getLogPrefix());
            return null;
        }

        // Log the Response
        try {
            Element dom = XMLObjectSupport.marshall(samlResponse);
            log.debug("{} Print SAML Response: {}", getLogPrefix(), SerializeSupport.prettyPrintXML(dom));
        } catch (MarshallingException e) {
            log.warn("{} Unable to print SAML Response. {}", getLogPrefix(), e);
        }

        // Validate the saml response
        if (!validateResponse(samlResponse, query.getID(), query.getIssueInstant(), queryTarget)) {
            log.warn("{} SAML Response from the AttributeQuery is not valid!", getLogPrefix());
            return null;
        }

        // Get the attribute statement
        final List<AttributeStatement> attributeStatements = samlResponse.getAssertions().get(0)
                .getAttributeStatements();

        // if there is no attribue statement, the method returns null (no attributes)
        if (attributeStatements == null || attributeStatements.size() == 0)
            return null;

        // Create the Attributes to release
        final List<Attribute> responseAttributes = attributeStatements.get(0).getAttributes();
        final Map<String, IdPAttribute> returnMap = new HashMap<>(responseAttributes.size());
        for (Attribute responseAttribute : responseAttributes) {
            final IdPAttribute idpAttribute = new IdPAttribute(responseAttribute.getFriendlyName());

            final List<XMLObject> responseAttributeValues = responseAttribute.getAttributeValues();
            final ArrayList<IdPAttributeValue<?>> idpAttributeValues = new ArrayList<>(
                    responseAttributeValues.size());
            for (XMLObject responseAttributeValue : responseAttributeValues) {
                if (responseAttributeValue instanceof XSString) {
                    idpAttributeValues
                            .add(StringAttributeValue.valueOf(((XSString) responseAttributeValue).getValue()));
                }
            }
            log.debug("{} New Attribute produced from AttributeStatement, friendly name: {}, name: {}",
                    getLogPrefix(), responseAttribute.getFriendlyName(), responseAttribute.getName());

            idpAttribute.setValues(idpAttributeValues);
            returnMap.put(idpAttribute.getId(), idpAttribute);
        }

        log.debug("{} Duration of AQ Data Connector: {}ms", getLogPrefix(),
                (DateTime.now().getMillis() - start.getMillis()));
        return returnMap;
    }

    /**
     * 
     * Validator of the SAML Response
     * 
     * @param response the response object to validate
     * @param randomID the randomID from the attribute query
     * @param queryTime the timestamp from the attribute query
     * @param nameID the nameID from the query subject
     * @param entityID the entityID from the attribute authority
     * @return true if validation of the response was positive, false if something with the response is wrong
     * @throws ResolutionException throws if some element of the response is not readable
     */
    protected boolean validateResponse(@Nonnull Response response, String randomID, DateTime queryTime,
            AQTarget queryTarget) throws ResolutionException {
        try {

            // Check SAML version         
            if (response.getVersion() != SAMLVersion.VERSION_20) {
                log.warn("{} Validation of SALM2 response failed! Unsupported SAML version of response: {} ",
                        getLogPrefix(), response.getVersion());
                return false;
            }

            // It should have exactly one assertion in this response
            if (response.getAssertions().size() != 1) {
                log.warn(
                        "{} Validation of SALM2 response failed! There are {} assertions, but it must have exactly 1 assertion!",
                        getLogPrefix(), response.getAssertions().size());
                return false;
            }

            // Validate if response status is success
            if (!response.getStatus().getStatusCode().getValue()
                    .equals("urn:oasis:names:tc:SAML:2.0:status:Success")) {
                log.warn(
                        "{} Validation of SALM2 response failed! Wrong Status Code: {}, it has to be urn:oasis:names:tc:SAML:2.0:status:Success",
                        getLogPrefix(), response.getStatus().getStatusCode().getValue());
                return false;
            }

            Assertion assertion = response.getAssertions().get(0);
            if (assertion.getVersion() != SAMLVersion.VERSION_20) {
                log.warn("{} Validation of SALM2 response failed! Assertion is not a SAML 2.0 version Assertion",
                        getLogPrefix());
                return false;
            }

            // Check if issuer has the right entityID
            Issuer assertionIssuer = assertion.getIssuer();
            String entityID = queryTarget.getEntityID();
            if (!assertionIssuer.getValue().equals(entityID)) {
                log.warn("{} Validation of SALM2 response failed! Wrong Issuer {}, it should be {}", getLogPrefix(),
                        assertion.getIssuer().getValue(), entityID);
                return false;
            }

            // verify signature of assertion
            if (assertion.isSigned() && signatureRequired) {
                log.debug("{} Begin with checking signature", getLogPrefix());
                Signature signature = assertion.getSignature();

                // checks if its a valid signature profile
                SAMLSignatureProfileValidator spv = new SAMLSignatureProfileValidator();
                try {
                    spv.validate(signature);
                    log.debug("{} Signature hat correct profile", getLogPrefix());
                } catch (SignatureException e) {
                    log.warn("{} Validation of SALM2 response failed! Exception while validating the exception: {}",
                            getLogPrefix(), e);
                    return false;
                }

                //            String certificate = "MIIDLDCCAhSgAwIBAgIVAPhMcSU5PCjjIosHSOpreF2ztyCZMA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNVBAMMEHNoaWJhYS50aS5iZmguY2gwHhcNMTUwNDIxMDYzNTM2WhcNMzUwNDIxMDYzNTM2WjAbMRkwFwYDVQQDDBBzaGliYWEudGkuYmZoLmNoMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkmZJpYDL6CbQtzUBrwvfPOAdihQyI+BZiio6060GC4H8ObyaBIbRNFVkaG6FVlfBR8XVvB6SiRIMjd4pmWii0/lfhy67/61dHs35+AwSKzYcxThsov8YtTnyhj3bAij0Ved2cmdx3ZCQyfCmPH2mI6bFssDxMczRT4oTtCCmmOQH3XoebT2HJ8CMXqTxBQzQm0f5voc8BfeGVBjduDGS5D14kS7couuTmgTIa91EnypeXH67ZHk8QlRcPFRyGHn2s3ivPjbwRrvWtqpHCry14MvQVtOcu8TJ0OsN4+h3ds2OX+ZigIcxVOfBx+VM9O8vlHwzrCm0ACoi9uy0mVbDZQIDAQABo2cwZTAdBgNVHQ4EFgQUdWPQ51FiJv3dS+MEmDlF8XcSWzkwRAYDVR0RBD0wO4IQc2hpYmFhLnRpLmJmaC5jaIYnaHR0cHM6Ly9zaGliYWEudGkuYmZoLmNoL2lkcC9zaGliYm9sZXRoMA0GCSqGSIb3DQEBBQUAA4IBAQAxXuCR2Xd7cM+LTRkateGSu3SblomsOFnWJT3hizLsRa9y2kbQz14NAn3KF5pQ1srEX4AS18AM6YVjkR2r8if96m8PmrGxwGUNwK6AYXoBQ/oRq3ZC1DZJFS8qmgmX9wr96Gb0yJbFmJeHOfvqgzPSdB+oZUX3RTPXF6QOnt7+LFvSf1EfDwadp8lq8MQAqtHszHYFMkRGPJC+KBEC6PNkFa36K3pD+E2yh9Q51Yg5eic7GyG5qyZeYIuo3hERS9w3ZlLGjQ+mkvxN5gM3U7fJAYkcdc+crABVy/XAuLoLUMiIUD0gaKjs2enRB+LQVX+rjyiyukETAvdftadGFxv8";
                //            X509Certificate cert = X509Support.decodeCertificate(certificate);
                X509Certificate cert = queryTarget.getCertificate();
                AttributeQueryKeyManager keyManager = new AttributeQueryKeyManager(cert);
                X509Credential validationCredential = new X509KeyManagerX509CredentialAdapter(keyManager,
                        "verification");

                try {
                    SignatureValidator.validate(signature, validationCredential);
                    log.debug("{} Signature validated, no problem", getLogPrefix());
                } catch (SignatureException e) {
                    log.warn("{} Validation of SALM2 response failed! Assertion Signature is not correct!",
                            getLogPrefix());
                    return false;
                }
            } else if (!assertion.isSigned() && signatureRequired) {
                log.warn(
                        "{} Validation of SALM2 response failed! Assertion Signature is required but this assertion is not sigened!",
                        getLogPrefix());
                return false;
            } else {
                log.warn("{} No signature check required!", getLogPrefix());
            }

            // CONDITIONS OF ASSERTION
            Conditions conditions = assertion.getConditions();
            if (conditions != null) {
                log.debug("{} Check conditions of assertion!", getLogPrefix());

                DateTime now = DateTime.now();
                DateTime notBefore = conditions.getNotBefore();
                log.debug("Evaluating Conditions NotBefore '{}' against now(+5min) '{}'", notBefore,
                        now.plusMinutes(5));
                if (notBefore != null && notBefore.isAfter(now.plusMinutes(5))) {
                    log.warn("{} Validation of SALM2 response failed! The condition not before {} failed.",
                            getLogPrefix(), notBefore);
                    return false;
                }

                DateTime notOnOrAfter = conditions.getNotOnOrAfter();
                log.debug("Evaluating Conditions NotOnOrAfter '{}' against now(-5min) '{}'", notOnOrAfter,
                        now.minusMinutes(5));
                if (notOnOrAfter != null && notOnOrAfter.isBefore(now.minusMinutes(5))) {
                    log.warn("{} Validation of SALM2 response failed! The condition not on or after {} failed.",
                            getLogPrefix(), notOnOrAfter);
                    return false;
                }
            } else {
                log.debug("{} Asertion does not contain conditions!", getLogPrefix());
            }

            // SUBJECT OF ASSERTION
            Subject assertionSubject = assertion.getSubject();

            if (assertionSubject == null) {
                log.warn("{} Validation of SALM2 response failed! No Subject found", getLogPrefix());
                return false;
            }

            // check if the nameid is correct
            String nameID = queryTarget.getNameID();
            if (!assertionSubject.getNameID().getValue().equals(nameID)) {
                log.warn("{} Validation of SALM2 response failed! Wrong nameID: {}, expected: {}", getLogPrefix(),
                        assertion.getSubject().getNameID().getValue(), nameID);
                return false;
            }

            // check if the in response to id is correct
            if (!assertionSubject.getSubjectConfirmations().get(0).getSubjectConfirmationData().getInResponseTo()
                    .equals(randomID)) {
                log.warn("{} Validation of SALM2 response failed! Wrong InResponseTo ID: {}, it has to be {}",
                        getLogPrefix(), response.getInResponseTo(), randomID);
                return false;
            }

            // verify that the assertion has not more than one attribue statements
            if (assertion.getAttributeStatements().size() > 1) {
                log.warn(
                        "{} Validation of SALM2 response failed! {} attribute statements, but expected is 0 or one {}",
                        getLogPrefix(), assertion.getAttributeStatements().size());
                return false;
            }
        } catch (Exception e) {
            log.warn(
                    "{} Validation of SALM2 response failed! {} Can not read all Elements of the assertion to verify response! Exception: {}",
                    getLogPrefix(), e);
            return false;
        }

        log.debug("{} SAML2 Response validated, it's valid", getLogPrefix());
        return true;
    }

    /**
     * Builds the HTTPSOAPCLient to send the attribute query, with client TLS
     * 
     * @return the initialized HTTPSOAPClient object ready to send a soap message
     * @throws ResolutionException throws if there is a problem while creating the HTTPSOAPClient
     */
    @Nonnull
    protected HttpSOAPClient getSOAPClient() throws ResolutionException {
        // Client TLS support
        ArrayList<KeyManager> keyManagerList = new ArrayList<>(1);
        keyManagerList.add(keyManager);
        ArrayList<TrustManager> trustManagerList = new ArrayList<>(1);
        trustManagerList.add(new DelegateToApplicationX509TrustManager());
        TLSSocketFactoryBuilder tlsFactoryBuilder = new TLSSocketFactoryBuilder();
        tlsFactoryBuilder.setKeyManagers(keyManagerList);
        tlsFactoryBuilder.setTrustManagers(trustManagerList);

        // generate http clientbuilder
        HttpClientBuilder clientBuilder = new HttpClientBuilder();
        clientBuilder.setTLSSocketFactory(tlsFactoryBuilder.build());
        // timeout after 5 seconds if no connection possible
        //clientBuilder.setConnectionTimeout(5000);

        // generate parserpool
        BasicParserPool parserPool = new BasicParserPool();
        parserPool.setNamespaceAware(true);
        try {
            parserPool.initialize();
        } catch (Exception e) {
            throw new ResolutionException(getLogPrefix() + "Inizialize Parser pool failed", e);
        }

        // Build client and send the soap message
        HttpSOAPClient soapClient = new HttpSOAPClient();
        try {
            soapClient.setHttpClient(clientBuilder.buildClient());
            soapClient.setParserPool(parserPool);
            soapClient.initialize();
        } catch (Exception e) {
            throw new ResolutionException(getLogPrefix() + "SOAP Client Init failed!!!", e);
        }

        return soapClient;
    }

    /**
     * Method to create a SOAP envelope and add the query to the body of the envelope
     * 
     * @param query the {@link AttributeQuery} Element for the envelope body
     * @return the envelope with the attribute query in its body element
     */
    @Nonnull
    protected Envelope createSOAPEnvelope(@Nonnull AttributeQuery query) {
        final XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
        Envelope envelope = bf.<Envelope>getBuilderOrThrow(Envelope.TYPE_NAME).buildObject(
                Envelope.DEFAULT_ELEMENT_NAME.getNamespaceURI(), Envelope.DEFAULT_ELEMENT_NAME.getLocalPart(),
                Envelope.DEFAULT_ELEMENT_NAME.getPrefix());
        Body body = bf.<Body>getBuilderOrThrow(Body.TYPE_NAME).buildObject(
                Body.DEFAULT_ELEMENT_NAME.getNamespaceURI(), Body.DEFAULT_ELEMENT_NAME.getLocalPart(),
                Body.DEFAULT_ELEMENT_NAME.getPrefix());
        body.getUnknownXMLObjects().add(query);
        envelope.setBody(body);

        return envelope;
    }

    @Override
    protected void doInitialize() throws ComponentInitializationException {
        super.doInitialize();

        if (targetResolvingStrategy == null) {
            throw new ComponentInitializationException(getLogPrefix()
                    + " No targetResolvingStrategy for the AttribtueQueryDataConnector configured, please adjust the DataConnector XML Settings");
        }

        if (attributeQueryBuilder == null) {
            throw new ComponentInitializationException(getLogPrefix()
                    + " No attributeQueryBuilder for the AttribtueQueryDataConnector configured, please adjust the DataConnector XML Settings");
        }

        if (keyManager == null) {
            throw new ComponentInitializationException(getLogPrefix()
                    + " No keyManager AttribtueQueryDataConnector configured, please adjust the DataConnector XML Settings");
        }
    }
}