com.alfaariss.oa.util.saml2.SAML2Requestor.java Source code

Java tutorial

Introduction

Here is the source code for com.alfaariss.oa.util.saml2.SAML2Requestor.java

Source

/*
 * Asimba Server
 * 
 * Copyright (C) 2012 Asimba
 * Copyright (C) 2007-2010 Alfa & Ariss B.V.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see www.gnu.org/licenses
 * 
 * Asimba - Serious Open Source SSO - More information on www.asimba.org
 * 
 */
package com.alfaariss.oa.util.saml2;

import java.io.Serializable;
import java.util.Date;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.asimba.util.saml2.metadata.provider.IMetadataProviderManager;
import org.asimba.util.saml2.metadata.provider.MetadataProviderConfiguration;
import org.asimba.util.saml2.metadata.provider.MetadataProviderUtil;
import org.asimba.util.saml2.metadata.provider.management.MdMgrManager;
import org.asimba.utility.filesystem.PathTranslator;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import org.opensaml.saml2.metadata.provider.AbstractReloadingMetadataProvider;
import org.opensaml.saml2.metadata.provider.MetadataProvider;
import org.w3c.dom.Element;

import com.alfaariss.oa.OAException;
import com.alfaariss.oa.SystemErrors;
import com.alfaariss.oa.api.configuration.ConfigurationException;
import com.alfaariss.oa.api.configuration.IConfigurationManager;
import com.alfaariss.oa.api.requestor.IRequestor;
import org.gluu.asimba.util.ldap.sp.RequestorEntry;

/**
 * Requestor object containing requestor specific configuration items.
 * 
 * @author MHO
 * @author Alfa & Ariss 
 */
public class SAML2Requestor implements Serializable {
    private final static long serialVersionUID = 2093412253512956568L;

    /** Local logger instance */
    private static Log _logger = LogFactory.getLog(SAML2Requestor.class);

    /** Default request timeout */
    public final static int HTTP_METADATA_REQUEST_TIMEOUT = 5000;

    /** ID of the Requestor (represents the SAML2 EntityId) */
    protected String _sID;

    /** The configured source(s) for getting SAML2 Metadata from */
    protected MetadataProviderConfiguration _oMetadataProviderConfig;

    /** Signing mandatory. */
    protected boolean _bSigning;

    /** Timestamp when the SAML2Requestor was last modified */
    protected Date _dLastModified;

    /** The name of the MetadataProviderManager that manages this SAML2Requestor */
    protected String _sMPMId = null;

    /**
     * Keep reference to MetadataProvider for this SAML2Requestor
     * 
     * Does not serialize, so it is lost whenever it has been resuscitated. This should
     * not present any problem, as the MetadataProviderManager can re-deliver the 
     * MetadataProvider, or when it can not, this SAML2Requestor can re-create one
     */
    transient protected MetadataProvider _oMetadataProvider;

    private final static String PROPERTY_SIGNING = ".signing";
    private final static String PROPERTY_METADATA_HTTP_TIMEOUT = ".metadata.http.timeout";
    private final static String PROPERTY_METADATA_HTTP_URL = ".metadata.http.url";
    private final static String METADATA_FILE = ".metadata.file";
    private final static String METADATA = ".metadata";

    public final static int DEFAULT_HTTP_CONNECT_TIMEOUT = 3000;
    public final static int DEFAULT_HTTP_READ_TIMEOUT = 3000;

    /**
     * Constructor.
     * 
     * @param configurationManager The config manager.
     * @param config Configuration section.
     * @param bSigning Default signing boolean.
     * @param sMPMId The name of the MetadataProviderManager that manages the MetadataProvider
     *   for this SAML2Requestor
     * @throws OAException If creation fails.
     */
    public SAML2Requestor(IConfigurationManager configurationManager, Element config, boolean bSigning,
            String sMPMId) throws OAException {
        try {
            _sID = configurationManager.getParam(config, "id");
            if (_sID == null) {
                _logger.error("No 'id' item found in 'requestor' section in configuration");
                throw new OAException(SystemErrors.ERROR_CONFIG_READ);
            }

            _bSigning = false;
            String sSigning = configurationManager.getParam(config, "signing");
            if (sSigning == null) {
                _logger.warn("No optional 'signing' item found in configuration for requestor with id: " + _sID);
                _bSigning = bSigning;
            } else {
                if (sSigning.equalsIgnoreCase("TRUE")) {
                    _bSigning = true;
                } else {
                    if (!sSigning.equalsIgnoreCase("FALSE")) {
                        _logger.error(
                                "Invalid 'signing' item found in configuration (must be true or false) for requestor with id: "
                                        + _sID);
                        throw new OAException(SystemErrors.ERROR_CONFIG_READ);
                    }
                }
            }

            _logger.info("Using signing enabled: " + _bSigning);

            String sDateLastModified = configurationManager.getParam(config, "lastmodified");
            _dLastModified = null;

            if (sDateLastModified != null) {
                // Convert to java.util.Date
                try {
                    DateTime dt = ISODateTimeFormat.dateTimeNoMillis().parseDateTime(sDateLastModified);
                    _dLastModified = dt.toDate();
                } catch (IllegalArgumentException iae) {
                    _logger.warn("Invalid 'lastmodified' timestamp provided: " + sDateLastModified + "; ignoring.");
                    _dLastModified = null;
                }
            }

            // Keep reference to the MetadataProviderManager
            _sMPMId = sMPMId;

            // Do some integrity checking:
            IMetadataProviderManager oMPM = MdMgrManager.getInstance().getMetadataProviderManager(_sMPMId);
            if (oMPM == null)
                _logger.warn("The MetadataProviderManager '" + _sMPMId + "' does not (yet?) exist!");

            // Initialize MetadataProviderConfig
            _oMetadataProviderConfig = getMetadataConfigFromConfig(configurationManager, config);

            // Initialize upon construction, as last modification date will not change
            initMetadataProvider();

        } catch (OAException e) {
            throw e;
        } catch (Exception e) {
            _logger.fatal("Internal error while reading requestors configuration", e);
            throw new OAException(SystemErrors.ERROR_INTERNAL);
        }
    }

    /**
    * Constructor which uses business logic requestor properties.
    *  
    * @param oRequestor The OA Requestor object
    * @param  requestor The SAML SP Requestor entry
    * @param bSigning Default signing boolean.
    * @param sProfileId The SAML2 OA Profile id for resolving the attributes.
    * @param sMPMId The name of the MetadataProviderManager that manages the MetadataProvider
    *   for this SAML2Requestor
    * @throws OAException If creation fails.
    * @since 1.1
    */
    public SAML2Requestor(IRequestor oRequestor, RequestorEntry requestor, boolean bSigning, String sProfileId,
            String sMPMId) throws OAException {
        try {
            _sID = oRequestor.getID();

            // Keep reference to the MetadataProviderManager
            _sMPMId = sMPMId;

            // Do some integrity checking:
            IMetadataProviderManager oMPM = MdMgrManager.getInstance().getMetadataProviderManager(_sMPMId);
            if (oMPM == null)
                _logger.warn("The MetadataProviderManager '" + _sMPMId + "' does not (yet?) exist!");

            // Initialize MetadataProvider
            _oMetadataProviderConfig = getMetadataConfigFromLDAPRequestor(requestor);
        } catch (OAException oae) {
            _logger.error("Exception when initializing MetadataProvider: " + oae.getMessage());
            throw oae;
        } catch (Exception e) {
            _logger.fatal("Internal error while reading SAML2 attributes for requestor: " + oRequestor.getID(), e);
            throw new OAException(SystemErrors.ERROR_INTERNAL);
        }
    }

    /**
     * Constructor which uses business logic requestor properties.
     *  
     * @param oRequestor The OA Requestor object
     * @param bSigning Default signing boolean.
     * @param sProfileId The SAML2 OA Profile id for resolving the attributes.
     * @param sMPMId The name of the MetadataProviderManager that manages the MetadataProvider
     *   for this SAML2Requestor
     * @throws OAException If creation fails.
     * @since 1.1
     */
    public SAML2Requestor(IRequestor oRequestor, boolean bSigning, String sProfileId, String sMPMId)
            throws OAException {
        try {
            _sID = oRequestor.getID();

            Map<?, ?> mProperties = oRequestor.getProperties();
            initFromProperties(mProperties, bSigning, sProfileId);

            // Keep reference to the MetadataProviderManager
            _sMPMId = sMPMId;

            // Do some integrity checking:
            IMetadataProviderManager oMPM = MdMgrManager.getInstance().getMetadataProviderManager(_sMPMId);
            if (oMPM == null)
                _logger.warn("The MetadataProviderManager '" + _sMPMId + "' does not (yet?) exist!");

            // Initialize MetadataProvider
            _oMetadataProviderConfig = getMetadataConfigFromProperties(mProperties, sProfileId);

            // Lazy initialization of actual MetadataProvider
            // initMetadataProvider(oMPC, sProfileId, oRequestor.getLastModified());
        } catch (OAException oae) {
            _logger.error("Exception when initializing MetadataProvider: " + oae.getMessage());
            throw oae;
        } catch (Exception e) {
            _logger.fatal("Internal error while reading SAML2 attributes for requestor: " + oRequestor.getID(), e);
            throw new OAException(SystemErrors.ERROR_INTERNAL);
        }
    }

    /**
     * Helper to initialize local instance from provided requestor properties
     * @param oRequestor
     * @param bSigning
     * @param sProfileID
     * @throws OAException
     */
    private void initFromProperties(Map<?, ?> mProperties, boolean bSigning, String sProfileID) throws OAException {
        _bSigning = false;
        String sSigning = (String) mProperties.get(sProfileID + PROPERTY_SIGNING);
        if (sSigning == null) {
            _bSigning = bSigning;
            if (_logger.isDebugEnabled()) {
                _logger.debug("No optional '" + sProfileID + PROPERTY_SIGNING + "' property found for requestor '"
                        + _sID + "'; Using default value: " + _bSigning);
            }
        } else {
            if (sSigning.equalsIgnoreCase("TRUE")) {
                _bSigning = true;
            } else if (!sSigning.equalsIgnoreCase("FALSE")) {
                _logger.error("Invalid '" + sProfileID + PROPERTY_SIGNING + "' property found for requestor '"
                        + _sID + "'; Invalid value: " + sSigning);
                throw new OAException(SystemErrors.ERROR_INTERNAL);
            }
        }
    }

    /**
     * Helper method to initialize the MetadataProvider for the SAML2Requestor
     * Wrapper around MPManager: re-uses cached version, or creates a new version
     * when configuration was changed (since _dLastModified) or when cached version 
     * was expired.<br/>
     * 
     * @throws OAException thrown when unrecoverable error occurred
     */
    private void initMetadataProvider() throws OAException {
        String sInstanceMPFingerprint = _oMetadataProviderConfig.getFingerprint();

        if (sInstanceMPFingerprint.equals(MetadataProviderConfiguration.FINGERPRINT_PROVIDER_UNKNOWN)) {
            _logger.warn("No optional available metadata for requestor with id: " + _sID);
            return;
        }

        // Establish MetadataProvider for Requestor:
        IMetadataProviderManager oMPM = null;
        MetadataProvider oMP = null;

        oMPM = MdMgrManager.getInstance().getMetadataProviderManager(_sMPMId);

        if (oMPM == null)
            _logger.warn("MetadataProviderManager '" + _sMPMId + "'is not available for Requestor '" + _sID
                    + "'; possible thread leak?");

        // Try to get MetadataProvider from manager
        if (oMPM != null)
            oMP = oMPM.getProviderFor(_sID, _dLastModified);

        // Is the cached MetadataProvider still valid?
        if (oMP != null) {
            String sCachedMPFingerprint = MetadataProviderUtil.getMetadataProviderFingerprint(oMP);

            if (!sCachedMPFingerprint.equals(sInstanceMPFingerprint)) {
                _logger.info("Metadata configuration changed; re-initializing metadata for " + _sID);
                // No longer valid; invalidate the version from cache
                oMPM.removeProviderFor(_sID);
                oMP = null;
            } else {
                // For the purpose of logging:
                if (_logger.isDebugEnabled()) {
                    String sNextRefresh = null;

                    if (oMP instanceof AbstractReloadingMetadataProvider) {
                        DateTime oNextRefresh = ((AbstractReloadingMetadataProvider) oMP).getNextRefresh();
                        sNextRefresh = oNextRefresh.toString();
                    }
                    _logger.debug("Using cached MetadataProvider for " + _sID
                            + (sNextRefresh == null ? "" : " (next refresh: " + sNextRefresh + ")"));
                }
            }
        }

        if (oMP == null) {
            oMP = MetadataProviderUtil.createMetadataProvider(_sID, _oMetadataProviderConfig, oMPM);

            if (oMP != null) {
                _logger.debug("New MetadataProvider was established for " + _sID);
            } else {
                _logger.debug("No MetadataProvider could be established for " + _sID);
            }
        }

        _oMetadataProvider = oMP;
    }

    /**
     * Returns the Requestor ID. 
     * @return String with the Requestor ID.
     */
    public String getID() {
        return _sID;
    }

    /**
     * Returns the requestor specific MetadataProvider
     *
     * @return the MetadataProvider or <code>null</code> if no provider was available 
     */
    public MetadataProvider getMetadataProvider() {
        if (_oMetadataProvider != null)
            return _oMetadataProvider;

        try {
            initMetadataProvider();
        } catch (OAException oae) {
            _logger.warn("Exception occurred when establishing MetadataProvider for requestor '" + _sID + "': "
                    + oae.getMessage());
            return null;
        }

        return _oMetadataProvider;
    }

    /**
     * Returns TRUE if requests from this Requestor must be signed.
     * 
     * @return TRUE if signing is required for this requestor.
     */
    public boolean isSigningEnabled() {
        return _bSigning;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sbInfo = new StringBuilder();
        sbInfo.append("Requestor '");
        sbInfo.append(_sID);
        sbInfo.append("'");
        sbInfo.append(": ");
        sbInfo.append(_oMetadataProviderConfig.getFingerprint());

        return sbInfo.toString();
    }

    /**
     * Establish Metadata Provider configuration from a configuration element
     * Does not do any validation of the configured settings
     * @param oConfigManager
     * @param elConfig Configuration element of the Requestor (must contain child <metadata> element)
     * @return
     * @throws ConfigurationException 
     */
    private MetadataProviderConfiguration getMetadataConfigFromConfig(IConfigurationManager oConfigManager,
            Element elConfig) throws OAException {
        MetadataProviderConfiguration oMPC = new MetadataProviderConfiguration();

        Element elMetadata = oConfigManager.getSection(elConfig, "metadata");

        // Establish full qualified filename
        String sFilename = oConfigManager.getParam(elMetadata, "file");
        if (sFilename != null)
            sFilename = PathTranslator.getInstance().map(sFilename);
        oMPC._sFilename = sFilename;

        // Establish HTTP/URL settings
        Element elHTTP = oConfigManager.getSection(elMetadata, "http");
        if (elHTTP != null) {
            oMPC._sURL = oConfigManager.getParam(elHTTP, "url");

            String sTimeout = oConfigManager.getParam(elHTTP, "timeout");
            if (sTimeout != null) {
                try {
                    oMPC._iTimeout = Integer.parseInt(sTimeout);
                } catch (NumberFormatException e) {
                    _logger.error("Invalid value for http@timeout-attribute in configuration: " + sTimeout, e);
                    throw new OAException(SystemErrors.ERROR_CONFIG_READ);
                }
            }
        }

        // establish raw XML (untested) from <metadata><raw>...</raw></metadata>
        oMPC._sMetadata = oConfigManager.getParam(elMetadata, "raw");

        return oMPC;
    }

    /**
     * Establish Metadata Provider configuration from requestor properties
     * @param oProperties The set of Requestor Properties
     * @param sProfileId The SAML2 IDP ProfileId to use to look up properties
     * @return
     * @throws OAException 
     */
    private MetadataProviderConfiguration getMetadataConfigFromProperties(Map<?, ?> mProperties, String sProfileId)
            throws OAException {
        MetadataProviderConfiguration oMPC = new MetadataProviderConfiguration();

        // Establish full qualified filename
        String sFilename = (String) mProperties.get(sProfileId + METADATA_FILE);
        if (sFilename != null)
            sFilename = PathTranslator.getInstance().map(sFilename);
        oMPC._sFilename = sFilename;

        // Establish metadata from properties
        oMPC._sMetadata = (String) mProperties.get(sProfileId + METADATA);

        // Establish HTTP/URL settings
        oMPC._sURL = (String) mProperties.get(sProfileId + PROPERTY_METADATA_HTTP_URL);

        String sTimeout = (String) mProperties.get(sProfileId + PROPERTY_METADATA_HTTP_TIMEOUT);
        if (sTimeout != null) {
            try {
                oMPC._iTimeout = Integer.parseInt(sTimeout);
            } catch (NumberFormatException e) {
                _logger.error("Invalid value for " + sProfileId + PROPERTY_METADATA_HTTP_TIMEOUT + " property: "
                        + sTimeout, e);
                throw new OAException(SystemErrors.ERROR_CONFIG_READ);
            }
        }

        return oMPC;
    }

    /**
     * Establish Metadata Provider configuration from requestor properties
     * @param oProperties The set of Requestor Properties
     * @param sProfileId The SAML2 IDP ProfileId to use to look up properties
     * @return
     * @throws OAException 
     */
    private MetadataProviderConfiguration getMetadataConfigFromLDAPRequestor(RequestorEntry requestor)
            throws OAException {
        MetadataProviderConfiguration oMPC = new MetadataProviderConfiguration();

        // Establish full qualified filename
        if (requestor.getMetadataFile() != null && !"".equals(requestor.getMetadataFile()))
            oMPC._sFilename = PathTranslator.getInstance().map(requestor.getMetadataFile());

        // Establish metadata
        // RequestorEntry can keep metadata as text
        if (requestor.getMetadataXMLText() != null && !"".equals(requestor.getMetadataXMLText())) {
            oMPC._sMetadata = requestor.getMetadataXMLText();
        }

        // Establish HTTP/URL settings
        if (requestor.getMetadataUrl() != null && !"".equals(requestor.getMetadataUrl()))
            oMPC._sURL = requestor.getMetadataUrl();

        oMPC._iTimeout = requestor.getMetadataTimeout();

        return oMPC;
    }

}