/*
 *     Copyright (C) 2015 ASIP Santé
 */
package fr.asipsante.mss.connectkit;

import fr.asipsante.mss.connectkit.paos.AbstractSaml2Driver;
import fr.asipsante.mss.connectkit.paos.Saml2ProtocolException;
import fr.asipsante.mss.connectkit.connection.ConnectionUtil;
import fr.asipsante.mss.connectkit.connection.HttpsConnectionFactory;
import fr.asipsante.mss.connectkit.connection.RequestProperty;
import fr.asipsante.mss.connectkit.connection.SSLSecurityException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpCookie;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashSet;
import javax.net.ssl.HttpsURLConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implémentation de la logique de base pour l'authentification PAOS / SAML2.
 * 
 * @author ASIP Santé
 */
public abstract class AbstractSamlAuthenticationManager {

    public static final String CONTENT_TYPE_PAOS = "application/vnd.paos+xml";
    public static final String CONTENT_TYPE_NOM_HEADER = "Content-Type";
    public static final String ACCEPT_NOM_HEADER = "Accept";
    public static final String CONTENT_TYPE_XML = "text/xml";
    public static final String PAOS_VERSION_URI = "ver='urn:liberty:paos:2003-08';'urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp'";
    public static final String VERSION_PAOS_NOM_HEADER = "PAOS";

    private static final Logger LOGGER = LoggerFactory
            .getLogger(AbstractSamlAuthenticationManager.class);
    private ConnectionUtil connUtil = new ConnectionUtil();

    /**
     * @return Jeu de headers à ajouter sur toutes les connexions.
     */
    protected HashSet<RequestProperty> computeHeaderset() {
        HashSet<RequestProperty> hashSet = new HashSet<RequestProperty>();
        hashSet.add(new RequestProperty(CONTENT_TYPE_NOM_HEADER,
                CONTENT_TYPE_XML));
        hashSet.add(new RequestProperty(ACCEPT_NOM_HEADER, CONTENT_TYPE_PAOS));
        hashSet.add(new RequestProperty(VERSION_PAOS_NOM_HEADER,
                PAOS_VERSION_URI));
        return hashSet;
    }

    protected abstract HttpsConnectionFactory getConnFactory();

    protected abstract AbstractSaml2Driver getSamlStrategy();

    /**
     * Processus d'authentification.
     * 
     * @param reponseData
     *            réponse du service métier à la première requête métier
     * @param sslProtocolSpec
     *            version du protocole SSL
     * @param assertionConsumer
     *            URL de consommation de l'assertion SAML
     * @param sessionId
     *            id de la session J2EE ouverte sur le serveur métier
     * @return réponse du service consommateur à l'injection de l'assertion.
     * @throws IOException
     * @throws SSLSecurityException
     * @throws UnsupportedEncodingException
     * @throws URISyntaxException
     * @throws Saml2ProtocolException
     */
    protected byte[] authenticate(byte[] reponseData, String sslProtocolSpec,
            URL assertionConsumer, HttpCookie sessionId) throws IOException,
            SSLSecurityException, Saml2ProtocolException {
        try {
            byte[] authNrequest = getSamlStrategy().createAuthMessage(
                    reponseData);
            if (LOGGER.isDebugEnabled()) {
                String logMessage = connUtil.debugByteResponse(authNrequest,
                        getSamlStrategy().getDefaultEncoding());
                LOGGER.debug(logMessage);
            }

            byte[] authResponse = getSamlStrategy()
                    .getSamlAssertionFromAuthService(sslProtocolSpec,
                            authNrequest);

            HttpsURLConnection connection = getConnFactory()
                    .createHttpsConnection(assertionConsumer.getHost(),
                            assertionConsumer.getPort(),
                            assertionConsumer.getPath(), sslProtocolSpec);
            connection.setRequestProperty(CONTENT_TYPE_NOM_HEADER,
                    CONTENT_TYPE_PAOS);
            connUtil.addCookie(connection, sessionId);

            byte[] authTokenEnvelope = getSamlStrategy().createAuthMessage(
                    authResponse);

            byte[] consumerResponse;
            int authSubmitCode = connUtil.sendPost(connection,
                    new ByteArrayInputStream(authTokenEnvelope));
            if (authSubmitCode == 200) {
                throw new Saml2ProtocolException("Assertion rejected",
                        new String(connUtil.getResponse(connection), "utf-8"));
            } else {
                consumerResponse = connUtil.getResponse(connection);
            }
            return consumerResponse;
        } catch (URISyntaxException e) {
            throw new Saml2ProtocolException("Anomalie URI consommateur", e);
        }
    }
}
