
#region Header

/*
 * Copyright (c) eHealth
 */

#endregion Header

namespace be.ehealth.technicalconnector.ws
{
    using System;
    using System.IO;
    using System.Text;
    using org.apache.commons.codec.binary;

    using be.ehealth.technicalconnector.handler;
    using be.ehealth.technicalconnector.config;
        using be.ehealth.technicalconnector.enumeration;
    using be.ehealth.technicalconnector.exception;
    using be.ehealth.technicalconnector.service;
    using be.ehealth.technicalconnector.service.etee;
    using be.ehealth.technicalconnector.service.etee.impl;
    using be.ehealth.technicalconnector.service.keydepot;
    using be.ehealth.technicalconnector.service.keydepot.impl;
    using be.ehealth.technicalconnector.service.kgss;
    using be.ehealth.technicalconnector.service.kgss.impl;
    using be.ehealth.technicalconnector.service.sts;
    using be.ehealth.technicalconnector.service.sts.domain;
    using be.ehealth.technicalconnector.service.sts.impl;
    using be.ehealth.technicalconnector.service.sts.security;
    using be.ehealth.technicalconnector.service.sts.security.impl;
	using be.ehealth.technicalconnector.service.etee.domain;
	using be.ehealth.technicalconnector.utils;
	using be.fgov.ehealth.etee.kgss._1_0.protocol;
	using be.fgov.ehealth.etkdepot._1_0.protocol;
	using java.util;
    using be.ehealth.technicalconnector.utils.impl;
    using be.ehealth.technicalconnector.utils.net;
    using be.ehealth.technicalconnector.ws;
    using be.ehealth.technicalconnector.ws.domain;
    using be.ehealth.technicalconnector.ws.impl;
    using be.fgov.ehealth.etee.crypto.utils;

    using NUnit.Framework;

    using org.w3c.dom;

    /**
     * The Class TestGenericWsSender.
     */
    [TestFixture]
    class GenericWsSAMLSecuredITTest : be.ehealth.technicalconnector.session.AbstractServiceIntegrationTest
    {
        #region Fields

        private static java.util.Properties config;

        #endregion Fields

        #region Methods

        [SetUp]
        public static void initProperties()
        {
            config = TestPropertiesLoader.getProperties("be.ehealth.technicalconnector.test.properties",".net");
        }

        /**
         * This operation tests the invocation of a SAML Assertion / STS Token secured web service. It is clear that as pre-requisite a valid
         * STS Token retrieved via the STS Service is required. However, the web service used as end-point in this test case is the KGSS
         * Service's getKey operation. In order to test this successfully, there are extra methods in this class that are specific to the KGSS
         * Service and not to the general invocation of a SAML Assertion / STS Token secured web service.
         *
         * This test includes the following main steps: 1. Set the URL to the web service end-point 2. Requesting a new STS Token/SAML Assertion
         * via the STS Service (mandatory when invoking a SAML Assertion/STS Token secured web service) 3. Creation of the body pay-load to send
         * to the secured web service (mandatory when invoking a SAML Assertion/STS Token secured web service) 4. Request new key from KGSS
         * through the Technical Connector's Generic Web Service's SAML Secured operation
         *
         * @throws TechnicalConnectorException the technical connector exception
         * @throws GeneralSecurityException the general security exception
         */
        [Test]
        public void testSendSAMLSecured()
        {
	        GenericRequest request = new GenericRequest();
	        // 1. Set the URL to the web service end-point
	        request.setEndpoint(config.getProperty("test.endpoint.kgss"));
	        
	        // 2. Requesting a new STS Token/SAML Assertion via the STS Service
	        Credential authentication = new KeyStoreCredential(config.getProperty("test.keystore.location"), config.getProperty("test.keystore.alias"), config.getProperty("test.keystore.password"));
	        Credential service = new KeyStoreCredential(config.getProperty("test.keystore.location"), config.getProperty("test.keystore.alias"), config.getProperty("test.keystore.password"));
	        Element tokenElement = getToken(authentication, service);
	        SAMLToken token = SAMLTokenFactory.getInstance().createSamlToken(tokenElement, service);
	        request.getSecurityHandlerChain().add(new SAMLHolderOfKeyHandler(token));
	        
	        // 3. Creation of the body pay-load to send to the secured web service
	        request.setPayload(getPayload(authentication, service));
	        
	        // 4. Request new key from KGSS through the Technical Connector's Generic Web Service's SAML Secured operation
	        GenericWsSender sender = be.ehealth.technicalconnector.ws.ServiceFactory.getGenericWsSender();
	        request.getSecurityHandlerChain().add(new SoapActionHandler());
	        request.setDefaultHandlerChain();
	        
	        String result = null;
	        try {
	            result = sender.send(request).asString();
	        } catch (javax.xml.soap.SOAPException e) {
	            throw new TechnicalConnectorException(TechnicalConnectorExceptionValues.ERROR_WS, e, e.getMessage());
	        }
	        
	        // check if something has been received
	        Assert.NotNull(result);
        }

        /**
         * This operation tests the invocation of a SAML Assertion / STS Token secured web service. It is clear that as pre-requisite a valid
         * STS Token retrieved via the STS Service is required. However, the web service used as end-point in this test case is the KGSS
         * Service's getKey operation. In order to test this successfully, there are extra methods in this class that are specific to the KGSS
         * Service and not to the general invocation of a SAML Assertion / STS Token secured web service.
         *
         * This test includes the following main steps: 1. Set the URL to the web service end-point 2. Requesting a new STS Token/SAML Assertion
         * via the STS Service (mandatory when invoking a SAML Assertion/STS Token secured web service) 3. Creation of the body pay-load to send
         * to the secured web service (mandatory when invoking a SAML Assertion/STS Token secured web service) 4. Request new key from KGSS
         * through the Technical Connector's Generic Web Service's SAML Secured operation
         *
         * @throws TechnicalConnectorException the technical connector exception
         * @throws GeneralSecurityException the general security exception
         * @throws IOException
         * @throws SAXException
         * @throws ParserConfigurationException
         */
        [Test]
        public void testSendSAMLSecuredDOM()
        {
	         GenericRequest request = new GenericRequest();
	        // 1. Set the URL to the web service end-point
	        request.setEndpoint(config.getProperty("test.endpoint.kgss"));
	        
	        // 2. Requesting a new STS Token/SAML Assertion via the STS Service
	        Credential authentication = new KeyStoreCredential(config.getProperty("test.keystore.location"), config.getProperty("test.keystore.alias"), config.getProperty("test.keystore.password"));
	        Credential service = new KeyStoreCredential(config.getProperty("test.keystore.location"), config.getProperty("test.keystore.alias"), config.getProperty("test.keystore.password"));
	        Element tokenElement = getToken(authentication, service);
	        SAMLToken token = SAMLTokenFactory.getInstance().createSamlToken(tokenElement, service);
	        request.getSecurityHandlerChain().add(new SAMLHolderOfKeyHandler(token));
	        
	        // 3. Creation of the body pay-load to send to the secured web service
	        request.setPayload(getPayload(authentication, service));
	        
	        // 4. Request new key from KGSS through the Technical Connector's Generic Web Service's SAML Secured operation
	        GenericWsSender sender = be.ehealth.technicalconnector.ws.ServiceFactory.getGenericWsSender();
	        request.getSecurityHandlerChain().add(new SoapActionHandler());
	        request.setDefaultHandlerChain();
	        
	        Node result = null;
	        try {
	        	result = sender.send(request).asNode();
	        } catch (javax.xml.soap.SOAPException e) {
	            throw new TechnicalConnectorException(TechnicalConnectorExceptionValues.ERROR_WS, e, e.getMessage());
	        }
	        
	        // check if something has been received
	        Assert.NotNull(result);
        }
        
         /**
	     * Helper method for testSendSAMLSecured Retrieves an SAML Assertion / Secured token from STS. See StsServiceTest for more details about
	     * STS calls
	     */
	    private org.w3c.dom.Element getToken(Credential authentication, Credential service) {
	        // create the list of SAML Attributes Designators
	        java.util.List designators = new ArrayList();
	        designators.add(new SAMLAttributeDesignator("urn:be:fgov:ehealth:1.0:certificateholder:person:ssin", "urn:be:fgov:identification-namespace"));
	        designators.add(new SAMLAttributeDesignator("urn:be:fgov:person:ssin", "urn:be:fgov:identification-namespace"));
	
	        // create the list of SAML Attributes
	        java.util.List attributes = new ArrayList();
	        attributes.add(new SAMLAttribute("urn:be:fgov:person:ssin", "urn:be:fgov:identification-namespace", config.getProperty("test.INSS")));
	        attributes.add(new SAMLAttribute("urn:be:fgov:ehealth:1.0:certificateholder:person:ssin", "urn:be:fgov:identification-namespace", config.getProperty("test.INSS")));
	
	        int validity = 24;
	
	        // get the token
	        STSService sts = STSServiceFactory.getInstance();
	        return sts.getToken(authentication, service, attributes, designators, AbstractSTSService.HOK_METHOD, validity);
	    }
	    
	        /**
	     * Helper method for testSendSAMLSecured The web service used as end-point in this test case is the KGSS Service's getKey operation. In
	     * order to test this successfully, the following intermediate steps are required that are specific to the KGSS Service and not to the
	     * general invocation of a SAML Assertion / STS Token secured web service.
	     * 
	     * 1a. retrieve the ETK of KGSS 1b. retrieve the ETK of your application 2. Use the eHealth Crypto Framework to retrieve the decryption
	     * keys 3. Request new key from KGSS 4. Build a request to retrieve the generated key
	     * 
	     * Builds the payload (string) for the request. In order to do this, a key is generated in KGSS first.
	     * 
	     */
	    private String getPayload(Credential authentication, Credential service) {
	        // 1a. retrieve the ETK of KGSS (used to seal the request)
	        byte[] kgssEtk = getKgssEtk();
	
	        // 1b. retrieve the ETK of your application (inserted into the request, to seal the response)
	        byte[] myEtk = getSystemEtk();
	
	        //
	        // 2. Use the eHealth Crypto Framework to retrieve the decryption keys
	        //
	        // load in the keystoremanager to be able to easily work with the keystores
	        KeyStoreInfo ksInfo = new KeyStoreInfo(config.getProperty("test.keystore.location"), config.getProperty("test.keystore.password").ToCharArray(), config.getProperty("test.keystore.alias"), config.getProperty("test.keystore.password").ToCharArray());
	        KeyStoreManager encryptionKeystoreManager = new KeyStoreManager(ksInfo.getKeystorePath(), ksInfo.getKeystorePassword());
	        // use the eHealth Crypto Framework to get the decryption keys, it will need the keystore and the password for the private keys
	        Map decryptionKeys = KeyManager.getDecryptionKeys(encryptionKeystoreManager.getKeyStore(), ksInfo.getKeystorePassword());
	
	        // 3. Request new key from KGSS
	        byte[] keyIdentifier = getNewKey(service, decryptionKeys, kgssEtk, myEtk);
	
	        // 4. Build a request to retrieve the generated key
	        String request = buildRequest(service, decryptionKeys, kgssEtk, myEtk, keyIdentifier);
	
	        return request;
	    }
	    
	     /**
	     * Helper method for testSendSAMLSecured Build the SOAP Request 1. Create the request content 2. Marshall the request to a byte array 3.
	     * Seal the request content(byte array) 4. Encapsulate the encoded request content in the SOAP request
	     */
	    private String buildRequest(Credential service, Map decryptionKeys, byte[] kgssEtk, byte[] myEtk, byte[] keyIdentifier) {
	        // 1. Build the request content to get the key
	        GetKeyRequestContent content = new GetKeyRequestContent();
	        content.setETK(myEtk);
	        content.setKeyIdentifier(keyIdentifier);
	
	        // 2. Marshall the request to a byte array
	        // Convert XML Request to byte array
	        MarshallerHelper helper = new MarshallerHelper(typeof(GetKeyResponseContent), typeof(GetKeyRequestContent));
	        byte[] xmlByteArray = helper.toXMLByteArray(content);
	
	        // 3. Seal the request content
	        Crypto crypto = CryptoFactory.getCrypto(service, decryptionKeys);
	        // convert the byte array to a token
	        EncryptionToken token = new EncryptionToken(kgssEtk);
	        byte[] sealedByte = crypto.seal(token, xmlByteArray);
	        // encode so it is ready to send
	
	        String s = ConnectorIOUtils.toString(Base64.encodeBase64(sealedByte),Charset.UTF_8);
	        StringBuilder sb = new StringBuilder();
	        sb.Append("<GetKeyRequest xmlns=\"urn:be:fgov:ehealth:etee:kgss:1_0:protocol\">");
	        sb.Append("<SealedKeyRequest>");
	        sb.Append("<SealedContent>");
	        sb.Append(s);
	        sb.Append("</SealedContent>");
	        sb.Append("</SealedKeyRequest>");
	        sb.Append("</GetKeyRequest>");
	
	        return sb.ToString();
	    }
	
	    /**
	     * Helper method for testSendSAMLSecured Request the generation of a new key from KGSS. See KGSService Integration tests for more
	     * details about the GetNewKey method
	     * 
	     * @param service the service
	     * @param decryptionKeys the decryption keys
	     * @param kgssEtk the kgss etk
	     * @param systemEtk the system etk
	     * @author EHP
	     * @author EHP
	     * @author EHP
	     * @author EHP
	     */
	    private byte[] getNewKey(Credential service, Map decryptionKeys, byte[] kgssEtk, byte[] systemEtk) {
	        // create the request content
	        GetNewKeyRequestContent content = new GetNewKeyRequestContent();
	        content.setETK(systemEtk);
	        // add the ACL (configurable via properties)
	        CredentialType ct = new CredentialType();
	        ct.setNamespace(config.getProperty("test.kgss.ns"));
	        ct.setName(config.getProperty("test.kgss.name"));
	        ct.getValues().add(config.getProperty("test.kgss.value"));
	        content.getAllowedReaders().add(ct);
	
	        KgssService kgss = be.ehealth.technicalconnector.service.ServiceFactory.getKgssService();
	        GetNewKeyResponseContent response = kgss.getNewKey(content, service, decryptionKeys, kgssEtk);
	
	        return response.getNewKeyIdentifier();
	    }
	
	    /**
	     * Helper method for GetNewKey and GetKey. Returns the ETK of KGSS
	     * 
	     * @author EHP
	     * @author EHP
	     */
	    private byte[] getKgssEtk() {
	        // The values to use in the ETK Request
	        String applicationName = "KGSS";
	        String type = "CBE";
	        String value = "0809394427";
	
	        return getEtk(type, value, applicationName);
	    }
	
	    /**
	     * Helper method for GetNewKey and GetKey. Returns the ETK of the client application
	     * 
	     * @author EHP
	     * @author EHP
	     */
	    private byte[] getSystemEtk() {
	        // The values to use in the ETK Request
	        String applicationName = config.getProperty("test.myEtk.app");
	        String type = config.getProperty("test.myEtk.type");
	        String value = config.getProperty("test.myEtk.value");
	
	        return getEtk(type, value, applicationName);
	    }
	
	    /**
	     * Helper method for GetNewKey and GetKey. Returns the ETK for a given type/value/app combination. See Etk Service Integration tests for
	     * more details about the getEtk method
	     * 
	     * @param type the type
	     * @param value the value
	     * @param app the app
	     * @author EHP
	     * @author EHP
	     */
	    private byte[] getEtk(String type, String value, String app) {
	        // create request
	        GetEtkRequest request = new GetEtkRequest();
	        SearchCriteriaType searchCriteria = new SearchCriteriaType();
	        java.util.List listIdentifiers = new ArrayList();
	        be.fgov.ehealth.etkdepot._1_0.protocol.IdentifierType identifier = new be.fgov.ehealth.etkdepot._1_0.protocol.IdentifierType();
	        identifier.setApplicationID(app);
	        identifier.setType(type);
	        identifier.setValue(value);
	        listIdentifiers.add(identifier);
	        searchCriteria.getIdentifiers().addAll(listIdentifiers);
	        request.setSearchCriteria(searchCriteria);
	
	        // get the Etk
	        KeyDepotService service = be.ehealth.technicalconnector.service.ServiceFactory.getKeyDepotService();
	        GetEtkResponse response = service.getETK(request);
	        // return the Etk
	        return response.getETK();
	    }

        #endregion Methods
    }
}