From 41ea2fdf782cd64d7d29f73c2e83f9c255810818 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 2 Feb 2020 19:32:21 +0100 Subject: some more OpenSAML3 refactoring stuff --- .../pvp2/api/metadata/IPvp2MetadataProvider.java | 28 +- .../metadata/IPvpMetadataBuilderConfiguration.java | 7 +- .../api/metadata/IRefreshableMetadataProvider.java | 4 +- .../pvp2/impl/builder/PvpMetadataBuilder.java | 92 ++---- .../modules/pvp2/impl/message/InboundMessage.java | 15 +- .../metadata/AbstractChainingMetadataProvider.java | 318 ++++++++++++--------- .../impl/metadata/PvpMetadataResolverAdapter.java | 96 +++++++ .../impl/metadata/PvpMetadataResolverFactory.java | 271 ++++++++++++++++++ .../pvp2/impl/metadata/SimpleMetadataResolver.java | 243 ---------------- .../pvp2/impl/opensaml/EaafHttpPostDecoder.java | 30 +- .../opensaml/EaafHttpRedirectDeflateDecoder.java | 17 +- .../impl/opensaml/OpenSaml3ResourceAdapter.java | 85 ++++++ .../eaaf/modules/pvp2/impl/utils/Saml2Utils.java | 71 ++++- .../AbstractRequestSignedSecurityPolicyRule.java | 196 ------------- .../verification/PvpSignedRequestPolicyRule.java | 83 ------ .../src/main/resources/eaaf_pvp.beans.xml | 3 + .../resources/messages/pvp_messages.properties | 3 + .../pvp2/test/binding/RedirectBindingTest.java | 31 +- .../pvp2/idp/impl/AuthenticationAction.java | 29 +- .../pvp2/idp/impl/builder/AuthResponseBuilder.java | 12 +- 20 files changed, 821 insertions(+), 813 deletions(-) create mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverAdapter.java create mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverFactory.java delete mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/SimpleMetadataResolver.java create mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/opensaml/OpenSaml3ResourceAdapter.java delete mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/AbstractRequestSignedSecurityPolicyRule.java delete mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/PvpSignedRequestPolicyRule.java (limited to 'eaaf_modules') diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/api/metadata/IPvp2MetadataProvider.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/api/metadata/IPvp2MetadataProvider.java index e2ee0c9d..1af8db7b 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/api/metadata/IPvp2MetadataProvider.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/api/metadata/IPvp2MetadataProvider.java @@ -19,33 +19,13 @@ package at.gv.egiz.eaaf.modules.pvp2.api.metadata; -import java.util.List; - -import javax.xml.namespace.QName; - -import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2MetadataException; - -import org.opensaml.core.xml.XMLObject; -import org.opensaml.saml.metadata.resolver.MetadataResolver; -import org.opensaml.saml.saml2.metadata.EntitiesDescriptor; +import org.opensaml.saml.metadata.resolver.ExtendedRefreshableMetadataResolver; import org.opensaml.saml.saml2.metadata.EntityDescriptor; -import org.opensaml.saml.saml2.metadata.RoleDescriptor; - -public interface IPvp2MetadataProvider extends MetadataResolver { - - XMLObject getMetadata() throws Pvp2MetadataException; - - - EntitiesDescriptor getEntitiesDescriptor(String entitiesID) throws Pvp2MetadataException; - - - EntityDescriptor getEntityDescriptor(String entityID) throws Pvp2MetadataException; - - List getRole(String entityID, QName roleName) throws Pvp2MetadataException; +import net.shibboleth.utilities.java.support.resolver.ResolverException; +public interface IPvp2MetadataProvider extends ExtendedRefreshableMetadataResolver { - RoleDescriptor getRole(String entityID, QName roleName, String supportedProtocol) - throws Pvp2MetadataException; + EntityDescriptor getEntityDescriptor(String entityID) throws ResolverException; } diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/api/metadata/IPvpMetadataBuilderConfiguration.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/api/metadata/IPvpMetadataBuilderConfiguration.java index 72cb3f3c..128d4c2f 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/api/metadata/IPvpMetadataBuilderConfiguration.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/api/metadata/IPvpMetadataBuilderConfiguration.java @@ -22,14 +22,15 @@ package at.gv.egiz.eaaf.modules.pvp2.api.metadata; import java.util.Collection; import java.util.List; -import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException; - import org.opensaml.saml.saml2.core.Attribute; import org.opensaml.saml.saml2.metadata.ContactPerson; import org.opensaml.saml.saml2.metadata.Organization; import org.opensaml.saml.saml2.metadata.RequestedAttribute; import org.opensaml.security.credential.Credential; +import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential; +import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException; + /** * PVP Metadata builder configuration. * @@ -109,7 +110,7 @@ public interface IPvpMetadataBuilderConfiguration { * @return Credentials * @throws CredentialsNotAvailableException In case of an error */ - Credential getMetadataSigningCredentials() throws CredentialsNotAvailableException; + EaafX509Credential getMetadataSigningCredentials() throws CredentialsNotAvailableException; /** * Set the credential for request/response signing IDP metadata: this credential diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/api/metadata/IRefreshableMetadataProvider.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/api/metadata/IRefreshableMetadataProvider.java index 5f69ba62..39536771 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/api/metadata/IRefreshableMetadataProvider.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/api/metadata/IRefreshableMetadataProvider.java @@ -19,13 +19,15 @@ package at.gv.egiz.eaaf.modules.pvp2.api.metadata; +import org.opensaml.saml.metadata.resolver.RefreshableMetadataResolver; + /** * Metadata provider that supports dynamic refreshing on external events. * * @author tlenz * */ -public interface IRefreshableMetadataProvider { +public interface IRefreshableMetadataProvider extends RefreshableMetadataResolver{ /** * Refresh a entity or load a entity in a metadata provider. diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/builder/PvpMetadataBuilder.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/builder/PvpMetadataBuilder.java index 42f69a57..d5893d4a 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/builder/PvpMetadataBuilder.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/builder/PvpMetadataBuilder.java @@ -20,31 +20,19 @@ package at.gv.egiz.eaaf.modules.pvp2.impl.builder; import java.io.IOException; -import java.io.StringWriter; import java.util.Collection; import java.util.List; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; +import javax.naming.ConfigurationException; import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import at.gv.egiz.eaaf.core.exceptions.EaafException; -import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvpMetadataBuilderConfiguration; -import at.gv.egiz.eaaf.modules.pvp2.impl.utils.AbstractCredentialProvider; -import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils; - -import org.apache.commons.httpclient.auth.CredentialsNotAvailableException; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; -import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; -import org.opensaml.core.xml.io.Marshaller; import org.opensaml.core.xml.io.MarshallingException; +import org.opensaml.core.xml.util.XMLObjectSupport; +import org.opensaml.saml.common.SignableSAMLObject; import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.saml2.metadata.AssertionConsumerService; import org.opensaml.saml.saml2.metadata.AttributeConsumingService; @@ -64,16 +52,20 @@ import org.opensaml.saml.saml2.metadata.SingleSignOnService; import org.opensaml.security.SecurityException; import org.opensaml.security.credential.Credential; import org.opensaml.security.credential.UsageType; -import org.opensaml.xml.security.SecurityHelper; import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator; import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory; -import org.opensaml.xmlsec.signature.Signature; import org.opensaml.xmlsec.signature.support.SignatureException; -import org.opensaml.xmlsec.signature.support.Signer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; -import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvpMetadataBuilderConfiguration; +import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils; +import net.shibboleth.utilities.java.support.xml.SerializeSupport; /** * PVP metadata builder implementation. @@ -153,19 +145,9 @@ public class PvpMetadataBuilder { } } - - // set metadata signature parameters - final Credential metadataSignCred = config.getMetadataSigningCredentials(); - final Signature signature = AbstractCredentialProvider.getIdpSignature(metadataSignCred); - SecurityHelper.prepareSignatureParams(signature, metadataSignCred, null, null); - - // initialize XML document builder - DocumentBuilder builder; - final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - - builder = factory.newDocumentBuilder(); - final Document document = builder.newDocument(); - + + SignableSAMLObject metadataToSign; + // build entities descriptor if (config.buildEntitiesDescriptorAsRootElement()) { final EntitiesDescriptor entitiesDescriptor = @@ -174,45 +156,29 @@ public class PvpMetadataBuilder { entitiesDescriptor.setID(Saml2Utils.getSecureIdentifier()); entitiesDescriptor.setValidUntil(date.plusHours(config.getMetadataValidUntil())); entitiesDescriptor.getEntityDescriptors().add(entityDescriptor); - - // load default PVP security configurations - entitiesDescriptor.setSignature(signature); - - // marshall document - final Marshaller out = - XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(entitiesDescriptor); - out.marshall(entitiesDescriptor, document); - + metadataToSign = entitiesDescriptor; + } else { entityDescriptor.setValidUntil(date.plusHours(config.getMetadataValidUntil())); entityDescriptor.setID(Saml2Utils.getSecureIdentifier()); - - entityDescriptor.setSignature(signature); - - // marshall document - final Marshaller out = - XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(entityDescriptor); - out.marshall(entityDescriptor, document); - + metadataToSign = entityDescriptor; + } // sign metadata - Signer.signObject(signature); - - // transform metadata object to XML string - final Transformer transformer = TransformerFactory.newInstance().newTransformer(); - - final StringWriter sw = new StringWriter(); - final StreamResult sr = new StreamResult(sw); - final DOMSource source = new DOMSource(document); - transformer.transform(source, sr); - sw.close(); - - return sw.toString(); + final EaafX509Credential metadataSignCred = config.getMetadataSigningCredentials(); + SignableSAMLObject signedMetadata = Saml2Utils.signSamlObject(metadataToSign, metadataSignCred, true); + + + // Serialize metadata + final Element document =XMLObjectSupport.marshall(signedMetadata); + String serializedMetadata = SerializeSupport.nodeToString(document); + return serializedMetadata; + } private RoleDescriptor generateSpMetadata(final IPvpMetadataBuilderConfiguration config) - throws CredentialsNotAvailableException, SecurityException, EaafException { + throws SecurityException, EaafException { final SPSSODescriptor spSsoDescriptor = Saml2Utils.createSamlObject(SPSSODescriptor.class); spSsoDescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS); spSsoDescriptor.setAuthnRequestsSigned(config.wantAuthnRequestSigned()); @@ -353,7 +319,7 @@ public class PvpMetadataBuilder { } private IDPSSODescriptor generateIdpMetadata(final IPvpMetadataBuilderConfiguration config) - throws EaafException, CredentialsNotAvailableException, SecurityException { + throws EaafException, SecurityException { // check response signing credential final Credential responseSignCred = config.getRequestorResponseSigningCredentials(); if (responseSignCred == null) { diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/message/InboundMessage.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/message/InboundMessage.java index 8a741b69..da958d5b 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/message/InboundMessage.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/message/InboundMessage.java @@ -25,18 +25,18 @@ import java.io.Serializable; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; -import at.gv.egiz.eaaf.core.impl.utils.DomUtils; -import at.gv.egiz.eaaf.modules.pvp2.api.message.InboundMessageInterface; -import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; -import at.gv.egiz.eaaf.modules.pvp2.exception.NoMetadataInformationException; -import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2MetadataException; - import org.opensaml.saml.saml2.metadata.EntityDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; import org.xml.sax.SAXException; +import at.gv.egiz.eaaf.core.impl.utils.DomUtils; +import at.gv.egiz.eaaf.modules.pvp2.api.message.InboundMessageInterface; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.exception.NoMetadataInformationException; +import net.shibboleth.utilities.java.support.resolver.ResolverException; + public class InboundMessage implements InboundMessageInterface, Serializable { private static final Logger log = LoggerFactory.getLogger(InboundMessage.class); @@ -65,9 +65,10 @@ public class InboundMessage implements InboundMessageInterface, Serializable { return metadataProvider.getEntityDescriptor(this.entityID); - } catch (final Pvp2MetadataException e) { + } catch (final ResolverException e) { log.warn("No Metadata for EntitiyID " + entityID); throw new NoMetadataInformationException(); + } } diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/AbstractChainingMetadataProvider.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/AbstractChainingMetadataProvider.java index ec59b1df..3fc675e9 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/AbstractChainingMetadataProvider.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/AbstractChainingMetadataProvider.java @@ -27,46 +27,41 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Timer; import java.util.UUID; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.naming.ConfigurationException; -import javax.xml.namespace.QName; - -import at.gv.egiz.components.spring.api.IDestroyableObject; -import at.gv.egiz.eaaf.core.api.IGarbageCollectorProcessing; -import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; -import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; -import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IRefreshableMetadataProvider; import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; import org.opensaml.core.criterion.EntityIdCriterion; import org.opensaml.saml.metadata.resolver.ClearableMetadataResolver; import org.opensaml.saml.metadata.resolver.MetadataResolver; import org.opensaml.saml.metadata.resolver.RefreshableMetadataResolver; import org.opensaml.saml.metadata.resolver.filter.MetadataFilter; import org.opensaml.saml.metadata.resolver.impl.AbstractMetadataResolver; -import org.opensaml.saml.saml2.metadata.EntitiesDescriptor; import org.opensaml.saml.saml2.metadata.EntityDescriptor; -import org.opensaml.saml.saml2.metadata.RoleDescriptor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import at.gv.egiz.components.spring.api.IDestroyableObject; +import at.gv.egiz.eaaf.core.api.IGarbageCollectorProcessing; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IRefreshableMetadataProvider; +import lombok.extern.slf4j.Slf4j; import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements; import net.shibboleth.utilities.java.support.component.IdentifiedComponent; import net.shibboleth.utilities.java.support.resolver.CriteriaSet; import net.shibboleth.utilities.java.support.resolver.ResolverException; -public abstract class AbstractChainingMetadataProvider extends SimpleMetadataResolver - implements IGarbageCollectorProcessing, IRefreshableMetadataProvider, IDestroyableObject, IPvp2MetadataProvider, - RefreshableMetadataResolver, ClearableMetadataResolver { - - private static final Logger log = LoggerFactory.getLogger(AbstractChainingMetadataProvider.class); - +@Slf4j +public abstract class AbstractChainingMetadataProvider implements IGarbageCollectorProcessing, IRefreshableMetadataProvider, + IDestroyableObject, IPvp2MetadataProvider, ClearableMetadataResolver { + @Nonnull @NonnullElements private final List internalResolvers; + private DateTime lastRefeshTimestamp; + private boolean lastRefeshSuccessful; private static Object mutex = new Object(); - private Timer timer = null; /** * Build a chaining metadata resolver that requires valid metadata. @@ -77,10 +72,6 @@ public abstract class AbstractChainingMetadataProvider extends SimpleMetadataRes } - public final Timer getTimer() { - return this.timer; - - } /* * (non-Javadoc) @@ -145,10 +136,6 @@ public abstract class AbstractChainingMetadataProvider extends SimpleMetadataRes } else { // load new Metadata Provider - if (timer == null) { - timer = new Timer(true); - } - internalResolvers.add(createNewMetadataProvider(metadataUrl)); log.info("SAML2 metadata for service provider: " + entityId + " is added."); @@ -183,78 +170,22 @@ public abstract class AbstractChainingMetadataProvider extends SimpleMetadataRes internalResolvers.clear(); - if (timer != null) { - timer.cancel(); - } - } - - /* - * (non-Javadoc) - * - * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider# - * getMetadataFilter() - */ - @Override - public MetadataFilter getMetadataFilter() { - return internalProvider.getMetadataFilter(); - } - - /* - * (non-Javadoc) - * - * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider# - * setMetadataFilter(org. opensaml.saml2.metadata.provider.MetadataFilter) - */ - @Override - public void setMetadataFilter(final MetadataFilter newFilter) throws MetadataProviderException { - internalProvider.setMetadataFilter(newFilter); - } - - /* - * (non-Javadoc) - * - * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider# - * getMetadata() - */ - @Override - public XMLObject getMetadata() throws MetadataProviderException { - return internalProvider.getMetadata(); + /** {@inheritDoc} */ + @Override + public final MetadataFilter getMetadataFilter() { + log.warn("{} does NOT support {}", AbstractChainingMetadataProvider.class.getName(), + MetadataFilter.class.getName()); + return null; } - /* - * (non-Javadoc) - * - * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider# - * getEntitiesDescriptor( java.lang.String) - */ - @Override - public EntitiesDescriptor getEntitiesDescriptor(final String entitiesID) - throws MetadataProviderException { - EntitiesDescriptor entitiesDesc = null; - try { - entitiesDesc = internalProvider.getEntitiesDescriptor(entitiesID); - - if (entitiesDesc == null) { - log.debug("Can not find PVP metadata for entityID: " + entitiesID - + " Start refreshing process ..."); - if (refreshMetadataProvider(entitiesID)) { - return internalProvider.getEntitiesDescriptor(entitiesID); - } - - } - - } catch (final MetadataProviderException e) { - log.debug("Can not find PVP metadata for entityID: " + entitiesID - + " Start refreshing process ..."); - if (refreshMetadataProvider(entitiesID)) { - return internalProvider.getEntitiesDescriptor(entitiesID); - } - - } - - return entitiesDesc; + /** {@inheritDoc} */ + @Override + public final void setMetadataFilter(final MetadataFilter newFilter) { + log.warn("{} does NOT support {}", AbstractChainingMetadataProvider.class.getName(), + MetadataFilter.class.getName()); + throw new UnsupportedOperationException("Metadata filters are not supported on AbstractChainingMetadataProvider"); } /* @@ -265,68 +196,133 @@ public abstract class AbstractChainingMetadataProvider extends SimpleMetadataRes */ @Override public EntityDescriptor getEntityDescriptor(final String entityID) - throws MetadataProviderException { + throws ResolverException { EntityDescriptor entityDesc = null; try { - entityDesc = internalProvider.getEntityDescriptor(entityID); + entityDesc = resolveEntityDescripor(entityID); if (entityDesc == null) { log.debug("Can not find PVP metadata for entityID: " + entityID + " Start refreshing process ..."); if (refreshMetadataProvider(entityID)) { - return internalProvider.getEntityDescriptor(entityID); + return resolveEntityDescripor(entityID); } - } - } catch (final MetadataProviderException e) { + } catch (final ResolverException e) { log.debug( "Can not find PVP metadata for entityID: " + entityID + " Start refreshing process ..."); if (refreshMetadataProvider(entityID)) { - return internalProvider.getEntityDescriptor(entityID); + return resolveEntityDescripor(entityID); } } - // if (entityDesc != null) - // lastAccess.put(entityID, new Date()); - return entityDesc; } - /* - * (non-Javadoc) - * - * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider# - * getRole(java.lang. String, javax.xml.namespace.QName) - */ @Override - public List getRole(final String entityID, final QName roleName) - throws MetadataProviderException { - final List result = internalProvider.getRole(entityID, roleName); + @Nullable + public final EntityDescriptor resolveSingle(@Nullable final CriteriaSet criteria) throws ResolverException { + for (final MetadataResolver resolver : internalResolvers) { + try { + final EntityDescriptor descriptors = resolver.resolveSingle(criteria); + if (descriptors != null) { + return descriptors; + } + + } catch (final ResolverException e) { + continue; + + } - // if (result != null) - // lastAccess.put(entityID, new Date()); + } + + return null; - return result; } - /* - * (non-Javadoc) - * - * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider# - * getRole(java.lang. String, javax.xml.namespace.QName, java.lang.String) - */ @Override - public RoleDescriptor getRole(final String entityID, final QName roleName, - final String supportedProtocol) throws MetadataProviderException { - final RoleDescriptor result = internalProvider.getRole(entityID, roleName, supportedProtocol); + @Nonnull + public final Iterable resolve(@Nullable final CriteriaSet criteria) throws ResolverException { + for (final MetadataResolver resolver : internalResolvers) { + try { + final Iterable descriptors = resolver.resolve(criteria); + if (descriptors != null && descriptors.iterator().hasNext()) { + return descriptors; + + } + + } catch (final ResolverException e) { + continue; + + } + } + + return Collections.emptyList(); + } + + @Override + public final void clear() throws ResolverException { + for (final MetadataResolver resolver : internalResolvers) { + if (resolver instanceof ClearableMetadataResolver) { + ((ClearableMetadataResolver) resolver).clear(); + } + } + } + + @Override + public final void clear(String entityID) throws ResolverException { + for (final MetadataResolver resolver : internalResolvers) { + if (resolver instanceof ClearableMetadataResolver) { + ((ClearableMetadataResolver) resolver).clear(entityID); + } + } + } - // if (result != null) - // lastAccess.put(entityID, new Date()); + @Override final public void refresh() throws ResolverException { + this.lastRefeshSuccessful = false; + for (final MetadataResolver resolver : internalResolvers) { + if (resolver instanceof RefreshableMetadataResolver) { + ((RefreshableMetadataResolver) resolver).refresh(); + + } + } + + this.lastRefeshTimestamp = DateTime.now(); + this.lastRefeshSuccessful = true; + } - return result; + @Override + @Nullable public DateTime getLastUpdate() { + DateTime ret = null; + for (final MetadataResolver resolver : internalResolvers) { + if (resolver instanceof RefreshableMetadataResolver) { + final DateTime lastUpdate = ((RefreshableMetadataResolver) resolver).getLastUpdate(); + if (ret == null || ret.isBefore(lastUpdate)) { + ret = lastUpdate; + } + } + } + + return ret; } + @Override + @Nullable final public DateTime getLastRefresh() { + DateTime ret = null; + for (final MetadataResolver resolver : internalResolvers) { + if (resolver instanceof RefreshableMetadataResolver) { + final DateTime lastRefresh = ((RefreshableMetadataResolver) resolver).getLastRefresh(); + if (ret == null || ret.isBefore(lastRefresh)) { + ret = lastRefresh; + } + } + } + + return ret; + } + + /** * Get the URL to metadata for a specific entityID. * @@ -359,6 +355,20 @@ public abstract class AbstractChainingMetadataProvider extends SimpleMetadataRes throws EaafConfigurationException; + /** + * Get a Id for this metadata provider. + * + * @return + */ + @Nonnull + protected abstract String getMetadataProviderId(); + + protected final MetadataResolver getMetadataResolver() { + log.warn("{} does NOT support 'getMetadataResolver'", AbstractChainingMetadataProvider.class.getName()); + return null; + + } + private Map getAllActuallyLoadedResolvers() { final Map loadedproviders = new HashMap<>(); @@ -447,21 +457,7 @@ public abstract class AbstractChainingMetadataProvider extends SimpleMetadataRes private EntityDescriptor resolveEntityDescripor(String entityId) throws ResolverException { final CriteriaSet criteria = new CriteriaSet(); criteria.add(new EntityIdCriterion(entityId)); - for (final MetadataResolver resolver : internalResolvers) { - try { - final EntityDescriptor descriptors = resolver.resolveSingle(criteria); - if (descriptors != null) { - return descriptors; - } - - } catch (final ResolverException e) { - continue; - - } - - } - - return null; + return resolveSingle(criteria); } @@ -477,4 +473,40 @@ public abstract class AbstractChainingMetadataProvider extends SimpleMetadataRes } } + + + + @Override + public DateTime getLastSuccessfulRefresh() { + return this.lastRefeshTimestamp; + + } + + + @Override + public Boolean wasLastRefreshSuccess() { + return this.lastRefeshSuccessful; + + } + + + + /** {@inheritDoc} */ + @Override public boolean isRequireValidMetadata() { + log.warn("Attempt to access unsupported requireValidMetadata property on ChainingMetadataResolver"); + return false; + } + + /** {@inheritDoc} */ + @Override public void setRequireValidMetadata(final boolean requireValidMetadata) { + throw new UnsupportedOperationException("Setting requireValidMetadata is not supported on chaining resolver"); + } + + + @Override + public String getId() { + return getMetadataProviderId(); + + } + } diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverAdapter.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverAdapter.java new file mode 100644 index 00000000..bd2b79cb --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverAdapter.java @@ -0,0 +1,96 @@ +package at.gv.egiz.eaaf.modules.pvp2.impl.metadata; + +import org.joda.time.DateTime; +import org.opensaml.core.criterion.EntityIdCriterion; +import org.opensaml.saml.metadata.resolver.ExtendedRefreshableMetadataResolver; +import org.opensaml.saml.metadata.resolver.filter.MetadataFilter; +import org.opensaml.saml.saml2.metadata.EntityDescriptor; + +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; +import net.shibboleth.utilities.java.support.resolver.CriteriaSet; +import net.shibboleth.utilities.java.support.resolver.ResolverException; + +public class PvpMetadataResolverAdapter implements IPvp2MetadataProvider { + + private ExtendedRefreshableMetadataResolver internalProvider; + + public PvpMetadataResolverAdapter(ExtendedRefreshableMetadataResolver provider) { + this.internalProvider = provider; + } + + @Override + public void refresh() throws ResolverException { + internalProvider.refresh(); + + } + + @Override + public DateTime getLastRefresh() { + return internalProvider.getLastRefresh(); + + } + + @Override + public DateTime getLastUpdate() { + return internalProvider.getLastUpdate(); + } + + @Override + public boolean isRequireValidMetadata() { + return internalProvider.isRequireValidMetadata(); + + } + + @Override + public void setRequireValidMetadata(boolean requireValidMetadata) { + internalProvider.setRequireValidMetadata(requireValidMetadata); + + } + + @Override + public MetadataFilter getMetadataFilter() { + return internalProvider.getMetadataFilter(); + + } + + @Override + public void setMetadataFilter(MetadataFilter newFilter) { + internalProvider.setMetadataFilter(newFilter); + + } + + @Override + public Iterable resolve(CriteriaSet criteria) throws ResolverException { + return internalProvider.resolve(criteria); + } + + @Override + public EntityDescriptor resolveSingle(CriteriaSet criteria) throws ResolverException { + return internalProvider.resolveSingle(criteria); + + } + + @Override + public String getId() { + return internalProvider.getId(); + } + + @Override + public EntityDescriptor getEntityDescriptor(String entityId) throws ResolverException { + final CriteriaSet criteria = new CriteriaSet(); + criteria.add(new EntityIdCriterion(entityId)); + return internalProvider.resolveSingle(criteria); + + } + + @Override + public DateTime getLastSuccessfulRefresh() { + return internalProvider.getLastSuccessfulRefresh(); + } + + @Override + public Boolean wasLastRefreshSuccess() { + return internalProvider.wasLastRefreshSuccess(); + } + +} diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverFactory.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverFactory.java new file mode 100644 index 00000000..f548bc7b --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverFactory.java @@ -0,0 +1,271 @@ +package at.gv.egiz.eaaf.modules.pvp2.impl.metadata; + +import java.io.IOException; +import java.util.Timer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.net.ssl.SSLHandshakeException; + +import org.apache.http.client.HttpClient; +import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; +import org.opensaml.saml.metadata.resolver.ExtendedRefreshableMetadataResolver; +import org.opensaml.saml.metadata.resolver.filter.MetadataFilter; +import org.opensaml.saml.metadata.resolver.impl.HTTPMetadataResolver; +import org.opensaml.saml.metadata.resolver.impl.ResourceBackedMetadataResolver; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ResourceLoader; + +import at.gv.egiz.components.spring.api.IDestroyableObject; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.impl.utils.FileUtils; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.exception.SchemaValidationException; +import at.gv.egiz.eaaf.modules.pvp2.exception.SignatureValidationException; +import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.OpenSaml3ResourceAdapter; +import lombok.extern.slf4j.Slf4j; +import net.shibboleth.utilities.java.support.resource.Resource; +import net.shibboleth.utilities.java.support.xml.ParserPool; + +@Slf4j +public class PvpMetadataResolverFactory implements IDestroyableObject { + + private static final String URI_PREFIX_HTTP = "http:"; + private static final String URI_PREFIX_HTTPS = "https:"; + + private Timer timer = null; + + @Autowired private IConfiguration authConfig; + @Autowired private ResourceLoader resourceLoader; + + /** + * Create a single SAML2 metadata provider by using the default OpenSAML3 parser-pool. + * + * @param metadataLocation where the metadata should be loaded, but never null. + * If the location starts with http(s):, than a http + * based metadata provider is used. If the location + * starts with file:, than a filesystem based metadata + * provider is used + * @param filter Filters, which should be used to validate the + * metadata + * @param idForLogging Id, which is used for Logging + * @param httpClient Apache commons 4.x http client + * + * @return SAML2 Metadata Provider, or null if the metadata provider can not + * initialized + */ + @Nullable + public IPvp2MetadataProvider createMetadataProvider(@Nonnull final String metadataLocation, + @Nullable final MetadataFilter filter, @Nonnull final String idForLogging, + @Nullable final HttpClient httpClient) { + return createMetadataProvider(metadataLocation, filter, idForLogging, + XMLObjectProviderRegistrySupport.getParserPool(), + httpClient); + + } + + /** + * Create a single SAML2 metadata provider. + * + * @param metadataLocation where the metadata should be loaded, but never null. + * If the location starts with http(s):, than a http + * based metadata provider is used. If the location + * starts with file:, than a filesystem based metadata + * provider is used + * @param filter Filters, which should be used to validate the + * metadata + * @param idForLogging Id, which is used for Logging + * @param httpClient Apache commons 4.x http client + * + * @return SAML2 Metadata Provider, or null if the metadata provider can not + * initialized + */ + @Nullable + public IPvp2MetadataProvider createMetadataProvider(@Nonnull final String metadataLocation, + @Nullable final MetadataFilter filter, @Nonnull final String idForLogging, + @Nullable final ParserPool pool, @Nullable final HttpClient httpClient) { + + ExtendedRefreshableMetadataResolver internalProvider = null; + + if (metadataLocation.startsWith(URI_PREFIX_HTTP) + || metadataLocation.startsWith(URI_PREFIX_HTTPS)) { + if (httpClient != null) { + internalProvider = createNewHttpMetaDataProvider(metadataLocation, filter, idForLogging, timer, pool, + httpClient); + } else { + log.warn("Can not load http(s) based SAML2 metadata without a HTTP client"); + + } + + } else { + String absoluteMetadataLocation; + try { + absoluteMetadataLocation = + FileUtils.makeAbsoluteUrl(metadataLocation, authConfig.getConfigurationRootDirectory()); + + org.springframework.core.io.Resource resource = resourceLoader.getResource(absoluteMetadataLocation); + if (resource.exists()) { + internalProvider = createNewFileSystemMetaDataProvider( + new OpenSaml3ResourceAdapter(resource), + filter, idForLogging, timer, + pool); + } else { + log.warn( + "SAML2 metadata file: " + absoluteMetadataLocation + " not found or not exist"); + + } + + + } catch (final IOException e) { + log.warn("SAML2 metadata URL is invalid: " + metadataLocation, e); + + } + } + + if (internalProvider != null) { + return new PvpMetadataResolverAdapter(internalProvider); + + } else { + log.warn("SAML2 metadata has an unsupported metadata location prefix: " + metadataLocation); + return null; + + } + + } + + /** + * Create a single SAML2 filesystem based metadata provider. + * + * @param metadataFile File, where the metadata should be loaded + * @param filter Filters, which should be used to validate the metadata + * @param idForLogging Id, which is used for Logging + * @param timer {@link Timer} which is used to schedule metadata refresh + * operations + * @param pool + * + * @return SAML2 Metadata Provider + * @throws IOException + */ + private ExtendedRefreshableMetadataResolver createNewFileSystemMetaDataProvider(final Resource metadataFile, + final MetadataFilter filter, final String idForLogging, final Timer timer, + final ParserPool pool) throws IOException { + ResourceBackedMetadataResolver fileSystemResolver = null; + try { + //fileSystemResolver = new FilesystemMetadataResolver(timer, metadataFile); + + fileSystemResolver = new ResourceBackedMetadataResolver(timer, metadataFile); + + if (pool != null) { + fileSystemResolver.setParserPool(pool); + + } else { + fileSystemResolver.setParserPool( + XMLObjectProviderRegistrySupport.getParserPool()); + + } + fileSystemResolver.setRequireValidMetadata(true); + fileSystemResolver.setMinRefreshDelay(1000 * 60 * 15); // 15 minutes + fileSystemResolver.setMaxRefreshDelay(1000 * 60 * 60 * 24); // 24 hours + + fileSystemResolver.setMetadataFilter(filter); + fileSystemResolver.initialize(); + fileSystemResolver.setId(metadataFile.getURI().toASCIIString()); + + fileSystemResolver.setRequireValidMetadata(true); + + return fileSystemResolver; + + } catch (final Exception e) { + log.warn("Failed to load Metadata file for " + idForLogging + "[ " + "File: " + + metadataFile.getURI().toASCIIString() + " Msg: " + e.getMessage() + " ]", e); + + log.warn("Can not initialize SAML2 metadata provider from filesystem: " + + metadataFile.getURI().toASCIIString() + " Reason: " + e.getMessage(), e); + + if (fileSystemResolver != null) { + fileSystemResolver.destroy(); + + } + + } + + return null; + + } + + /** + * Create a single SAML2 HTTP metadata provider. + * + * @param metadataUrl URL, where the metadata should be loaded + * @param filter Filters, which should be used to validate the metadata + * @param idForLogging Id, which is used for Logging + * @param timer {@link Timer} which is used to schedule metadata refresh + * operations + * @param pool + * + * @return SAML2 Metadata Provider + */ + private ExtendedRefreshableMetadataResolver createNewHttpMetaDataProvider(final String metadataUrl, + final MetadataFilter filter, final String idForLogging, final Timer timer, + final ParserPool pool, final HttpClient httpClient) { + HTTPMetadataResolver httpMetadataResolver = null; + try { + httpMetadataResolver = new HTTPMetadataResolver(timer, httpClient, metadataUrl); + httpMetadataResolver.setParserPool(pool); + httpMetadataResolver.setRequireValidMetadata(true); + httpMetadataResolver.setMinRefreshDelay(1000 * 60 * 15); // 15 minutes + httpMetadataResolver.setMaxRefreshDelay(1000 * 60 * 60 * 24); // 24 hours + // httpProvider.setRefreshDelayFactor(0.1F); + + httpMetadataResolver.setMetadataFilter(filter); + httpMetadataResolver.setId(metadataUrl); + httpMetadataResolver.initialize(); + + httpMetadataResolver.setRequireValidMetadata(true); + + return httpMetadataResolver; + + } catch (final Throwable e) { + if (e.getCause() != null && e.getCause().getCause() instanceof SSLHandshakeException) { + log.warn("SSL-Server certificate for metadata " + metadataUrl + " not trusted.", e); + + } + if (e.getCause() != null && e.getCause().getCause() instanceof SignatureValidationException) { + log.warn("Signature verification for metadata" + metadataUrl + " FAILED.", e); + + } + if (e.getCause() != null && e.getCause().getCause() instanceof SchemaValidationException) { + log.warn("Schema validation for metadata " + metadataUrl + " FAILED.", e); + } + + log.warn("Failed to load Metadata file for " + idForLogging + "[ " + e.getMessage() + " ]", + e); + + if (httpMetadataResolver != null) { + log.debug("Destroy failed Metadata provider"); + httpMetadataResolver.destroy(); + + } + + } + + return null; + } + + @Override + public void fullyDestroy() { + if (timer != null) { + log.info("Stopping timer-thread for PVP metadata resolver ... "); + timer.cancel(); + } + } + + @PostConstruct + private void initialize() { + log.info("Initializing timer-thread for PVP metadata resolver ... "); + timer = new Timer("PVP metadata-resolver refresh"); + + } + +} diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/SimpleMetadataResolver.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/SimpleMetadataResolver.java deleted file mode 100644 index 35ad3f97..00000000 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/SimpleMetadataResolver.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a - * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European - * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in - * compliance with the Licence. You may obtain a copy of the Licence at: - * https://joinup.ec.europa.eu/news/understanding-eupl-v12 - * - * Unless required by applicable law or agreed to in writing, software distributed under the Licence - * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the Licence for the specific language governing permissions and limitations under - * the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text file for details on the - * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative - * works that you distribute must include a readable copy of the "NOTICE" text file. -*/ - -package at.gv.egiz.eaaf.modules.pvp2.impl.metadata; - -import java.io.File; -import java.net.MalformedURLException; -import java.util.Timer; - -import javax.net.ssl.SSLHandshakeException; - -import at.gv.egiz.eaaf.core.api.idp.IConfiguration; -import at.gv.egiz.eaaf.core.impl.utils.FileUtils; -import at.gv.egiz.eaaf.modules.pvp2.exception.SchemaValidationException; -import at.gv.egiz.eaaf.modules.pvp2.exception.SignatureValidationException; - -import org.apache.http.client.HttpClient; -import org.opensaml.saml.metadata.resolver.MetadataResolver; -import org.opensaml.saml.metadata.resolver.filter.MetadataFilter; -import org.opensaml.saml.metadata.resolver.impl.FilesystemMetadataResolver; -import org.opensaml.saml.metadata.resolver.impl.HTTPMetadataResolver; -import org.springframework.beans.factory.annotation.Autowired; - -import lombok.extern.slf4j.Slf4j; -import net.shibboleth.utilities.java.support.xml.ParserPool; - -/** - * Simple SAML2 metadata provider. - * - * @author tlenz - * - */ -@Slf4j -public abstract class SimpleMetadataResolver implements MetadataResolver { - private static final String URI_PREFIX_HTTP = "http:"; - private static final String URI_PREFIX_HTTPS = "https:"; - private static final String URI_PREFIX_FILE = "file:"; - - @Autowired - protected IConfiguration authConfig; - - @Override - public final boolean isRequireValidMetadata() { - return true; - - } - - @Override - public final void setRequireValidMetadata(final boolean requireValidMetadata) { - log.warn("EAAF {} requires always valid metadata. Setting will be ignored", - SimpleMetadataResolver.class.getSimpleName()); - - } - - - - /** - * Create a single SAML2 metadata provider. - * - * @param metadataLocation where the metadata should be loaded, but never null. - * If the location starts with http(s):, than a http - * based metadata provider is used. If the location - * starts with file:, than a filesystem based metadata - * provider is used - * @param filter Filters, which should be used to validate the - * metadata - * @param idForLogging Id, which is used for Logging - * @param timer {@link Timer} which is used to schedule metadata - * refresh operations - * @param httpClient Apache commons 3.x http client - * - * @return SAML2 Metadata Provider, or null if the metadata provider can not - * initialized - */ - protected MetadataResolver createNewSimpleMetadataProvider(final String metadataLocation, - final MetadataFilter filter, final String idForLogging, final Timer timer, - final ParserPool pool, final HttpClient httpClient) { - if (metadataLocation.startsWith(URI_PREFIX_HTTP) - || metadataLocation.startsWith(URI_PREFIX_HTTPS)) { - if (httpClient != null) { - return createNewHttpMetaDataProvider(metadataLocation, filter, idForLogging, timer, pool, - httpClient); - } else { - log.warn("Can not load http(s) based SAML2 metadata without a HTTP client"); - return null; - } - - } else { - String absoluteMetadataLocation; - try { - absoluteMetadataLocation = - FileUtils.makeAbsoluteUrl(metadataLocation, authConfig.getConfigurationRootDirectory()); - - if (absoluteMetadataLocation.startsWith(URI_PREFIX_FILE)) { - final File metadataFile = new File(absoluteMetadataLocation); - if (metadataFile.exists()) { - return createNewFileSystemMetaDataProvider(metadataFile, filter, idForLogging, timer, - pool); - } else { - log.warn( - "SAML2 metadata file: " + absoluteMetadataLocation + " not found or not exist"); - return null; - } - - } - - } catch (final MalformedURLException e) { - log.warn("SAML2 metadata URL is invalid: " + metadataLocation, e); - - } - - } - - log.warn("SAML2 metadata has an unsupported metadata location prefix: " + metadataLocation); - return null; - - } - - /** - * Create a single SAML2 filesystem based metadata provider. - * - * @param metadataFile File, where the metadata should be loaded - * @param filter Filters, which should be used to validate the metadata - * @param idForLogging Id, which is used for Logging - * @param timer {@link Timer} which is used to schedule metadata refresh - * operations - * @param pool - * - * @return SAML2 Metadata Provider - */ - private MetadataResolver createNewFileSystemMetaDataProvider(final File metadataFile, - final MetadataFilter filter, final String idForLogging, final Timer timer, - final ParserPool pool) { - FilesystemMetadataResolver fileSystemResolver = null; - try { - fileSystemResolver = new FilesystemMetadataResolver(timer, metadataFile); - fileSystemResolver.setParserPool(pool); - fileSystemResolver.setRequireValidMetadata(true); - fileSystemResolver.setMinRefreshDelay(1000 * 60 * 15); // 15 minutes - fileSystemResolver.setMaxRefreshDelay(1000 * 60 * 60 * 24); // 24 hours - - fileSystemResolver.setMetadataFilter(filter); - fileSystemResolver.initialize(); - fileSystemResolver.setId(metadataFile.getAbsolutePath()); - - fileSystemResolver.setRequireValidMetadata(true); - - return fileSystemResolver; - - } catch (final Exception e) { - log.warn("Failed to load Metadata file for " + idForLogging + "[ " + "File: " - + metadataFile.getAbsolutePath() + " Msg: " + e.getMessage() + " ]", e); - - log.warn("Can not initialize SAML2 metadata provider from filesystem: " - + metadataFile.getAbsolutePath() + " Reason: " + e.getMessage(), e); - - if (fileSystemResolver != null) { - fileSystemResolver.destroy(); - - } - - } - - return null; - - } - - /** - * Create a single SAML2 HTTP metadata provider. - * - * @param metadataUrl URL, where the metadata should be loaded - * @param filter Filters, which should be used to validate the metadata - * @param idForLogging Id, which is used for Logging - * @param timer {@link Timer} which is used to schedule metadata refresh - * operations - * @param pool - * - * @return SAML2 Metadata Provider - */ - private MetadataResolver createNewHttpMetaDataProvider(final String metadataUrl, - final MetadataFilter filter, final String idForLogging, final Timer timer, - final ParserPool pool, final HttpClient httpClient) { - HTTPMetadataResolver httpMetadataResolver = null; - try { - httpMetadataResolver = new HTTPMetadataResolver(timer, httpClient, metadataUrl); - httpMetadataResolver.setParserPool(pool); - httpMetadataResolver.setRequireValidMetadata(true); - httpMetadataResolver.setMinRefreshDelay(1000 * 60 * 15); // 15 minutes - httpMetadataResolver.setMaxRefreshDelay(1000 * 60 * 60 * 24); // 24 hours - // httpProvider.setRefreshDelayFactor(0.1F); - - httpMetadataResolver.setMetadataFilter(filter); - httpMetadataResolver.setId(metadataUrl); - httpMetadataResolver.initialize(); - - httpMetadataResolver.setRequireValidMetadata(true); - - return httpMetadataResolver; - - } catch (final Throwable e) { - if (e.getCause() != null && e.getCause().getCause() instanceof SSLHandshakeException) { - log.warn("SSL-Server certificate for metadata " + metadataUrl + " not trusted.", e); - - } - if (e.getCause() != null && e.getCause().getCause() instanceof SignatureValidationException) { - log.warn("Signature verification for metadata" + metadataUrl + " FAILED.", e); - - } - if (e.getCause() != null && e.getCause().getCause() instanceof SchemaValidationException) { - log.warn("Schema validation for metadata " + metadataUrl + " FAILED.", e); - } - - log.warn("Failed to load Metadata file for " + idForLogging + "[ " + e.getMessage() + " ]", - e); - - if (httpMetadataResolver != null) { - log.debug("Destroy failed Metadata provider"); - httpMetadataResolver.destroy(); - - } - - } - - return null; - } - -} diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/opensaml/EaafHttpPostDecoder.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/opensaml/EaafHttpPostDecoder.java index dc60019a..d23affba 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/opensaml/EaafHttpPostDecoder.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/opensaml/EaafHttpPostDecoder.java @@ -7,10 +7,13 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; +import org.opensaml.core.xml.XMLObject; import org.opensaml.messaging.decoder.MessageDecodingException; import org.opensaml.saml.saml2.binding.decoding.impl.HTTPPostDecoder; import com.google.common.base.Strings; + +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils; import lombok.extern.slf4j.Slf4j; import net.shibboleth.utilities.java.support.codec.Base64Support; @@ -34,8 +37,6 @@ public class EaafHttpPostDecoder extends HTTPPostDecoder { encodedMessage = getLastParameterFromRequest(request, "SAMLResponse"); } - - if (Strings.isNullOrEmpty(encodedMessage)) { log.info("Request did not contain either a SAMLRequest or " + "SAMLResponse paramter. Invalid request for SAML 2 HTTP POST binding."); @@ -54,14 +55,27 @@ public class EaafHttpPostDecoder extends HTTPPostDecoder { } /** - * Always read the last parameter with this name from request to get a strict deterministic behavior. - *

- * If more than one parameters with the same name exists, - * this method always select the last parameter value. + * EAAF specific unmarshaller perform XML schema validation before unmarshalling + * the SAML message. + * + */ + @Override + protected XMLObject unmarshallMessage(final InputStream messageStream) throws MessageDecodingException { + return Saml2Utils.unmarshallMessage(messageStream); + + } + + /** + * Always read the last parameter with this name from request to get a strict + * deterministic behavior.
+ *
+ * If more than one parameters with the same name exists, this method + * always select the last parameter value. * - * @param request Incoming http request + * @param request Incoming http request * @param paramName Name of the http parameter - * @return the last parameter value with this name, or null if the parameter not exists + * @return the last parameter value with this name, or null if the + * parameter not exists */ @Nullable private String getLastParameterFromRequest(@Nonnull HttpServletRequest request, @Nonnull String paramName) { diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/opensaml/EaafHttpRedirectDeflateDecoder.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/opensaml/EaafHttpRedirectDeflateDecoder.java index e9140f26..16d73296 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/opensaml/EaafHttpRedirectDeflateDecoder.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/opensaml/EaafHttpRedirectDeflateDecoder.java @@ -4,6 +4,7 @@ import java.io.InputStream; import javax.servlet.http.HttpServletRequest; +import org.opensaml.core.xml.XMLObject; import org.opensaml.messaging.context.MessageContext; import org.opensaml.messaging.decoder.MessageDecodingException; import org.opensaml.saml.common.SAMLObject; @@ -12,6 +13,8 @@ import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.saml2.binding.decoding.impl.HTTPRedirectDeflateDecoder; import com.google.common.base.Strings; + +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils; import lombok.extern.slf4j.Slf4j; import net.shibboleth.utilities.java.support.net.URISupport; import net.shibboleth.utilities.java.support.primitive.StringSupport; @@ -58,7 +61,7 @@ public class EaafHttpRedirectDeflateDecoder extends HTTPRedirectDeflateDecoder { throw new MessageDecodingException( "No SAMLRequest or SAMLResponse query path parameter, invalid SAML 2 HTTP Redirect message"); } - + final SAMLObject samlMessage = (SAMLObject) unmarshallMessage(samlMessageIns); messageContext.setMessage(samlMessage); log.debug("Decoded SAML message"); @@ -66,6 +69,18 @@ public class EaafHttpRedirectDeflateDecoder extends HTTPRedirectDeflateDecoder { populateBindingContext(messageContext); setMessageContext(messageContext); + + } + + /** + * EAAF specific unmarshaller perform XML schema validation before unmarshalling + * the SAML message. + * + */ + @Override + protected XMLObject unmarshallMessage(final InputStream messageStream) throws MessageDecodingException { + return Saml2Utils.unmarshallMessage(messageStream); + } } diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/opensaml/OpenSaml3ResourceAdapter.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/opensaml/OpenSaml3ResourceAdapter.java new file mode 100644 index 00000000..2e45aea2 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/opensaml/OpenSaml3ResourceAdapter.java @@ -0,0 +1,85 @@ +package at.gv.egiz.eaaf.modules.pvp2.impl.opensaml; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; + +import net.shibboleth.utilities.java.support.resource.Resource; + +/** + * Adapter that connects a Spring {@link org.springframework.core.io.Resource} to a {@link Resource}. + * + * @author tlenz + * + */ +public class OpenSaml3ResourceAdapter implements Resource { + + private org.springframework.core.io.Resource internalResource; + + public OpenSaml3ResourceAdapter(org.springframework.core.io.Resource resource) { + this.internalResource = resource; + } + + @Override + public boolean exists() { + return internalResource.exists(); + } + + @Override + public boolean isReadable() { + return internalResource.isReadable(); + } + + @Override + public boolean isOpen() { + return internalResource.isOpen(); + } + + @Override + public URL getURL() throws IOException { + return internalResource.getURL(); + } + + @Override + public URI getURI() throws IOException { + return internalResource.getURI(); + } + + @Override + public File getFile() throws IOException { + return internalResource.getFile(); + } + + @Override + public InputStream getInputStream() throws IOException { + return internalResource.getInputStream(); + } + + @Override + public long contentLength() throws IOException { + return internalResource.contentLength(); + } + + @Override + public long lastModified() throws IOException { + return internalResource.lastModified(); + } + + @Override + public Resource createRelativeResource(String relativePath) throws IOException { + throw new IOException("This method is not supperted by this adapter"); + } + + @Override + public String getFilename() { + return internalResource.getFilename(); + } + + @Override + public String getDescription() { + return internalResource.getDescription(); + } + +} diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/Saml2Utils.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/Saml2Utils.java index 8b1b041b..763c07f6 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/Saml2Utils.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/Saml2Utils.java @@ -20,6 +20,7 @@ package at.gv.egiz.eaaf.modules.pvp2.impl.utils; import java.io.IOException; +import java.io.InputStream; import java.security.PrivateKey; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; @@ -36,12 +37,6 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.validation.Schema; import javax.xml.validation.Validator; -import at.gv.egiz.eaaf.core.impl.utils.Random; -import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; -import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential; -import at.gv.egiz.eaaf.modules.pvp2.api.reqattr.EaafRequestedAttribute; -import at.gv.egiz.eaaf.modules.pvp2.exception.SamlSigningException; - import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.opensaml.core.xml.XMLObject; @@ -49,8 +44,12 @@ import org.opensaml.core.xml.XMLObjectBuilderFactory; import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; import org.opensaml.core.xml.io.Marshaller; import org.opensaml.core.xml.io.MarshallingException; +import org.opensaml.core.xml.io.Unmarshaller; +import org.opensaml.core.xml.io.UnmarshallingException; import org.opensaml.core.xml.schema.XSString; import org.opensaml.core.xml.schema.impl.XSStringBuilder; +import org.opensaml.core.xml.util.XMLObjectSupport; +import org.opensaml.messaging.decoder.MessageDecodingException; import org.opensaml.saml.common.SAMLObjectContentReference; import org.opensaml.saml.common.xml.SAMLSchemaBuilder; import org.opensaml.saml.common.xml.SAMLSchemaBuilder.SAML1Version; @@ -80,6 +79,18 @@ import org.opensaml.xmlsec.signature.support.Signer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import at.gv.egiz.eaaf.core.impl.utils.DomUtils; +import at.gv.egiz.eaaf.core.impl.utils.Random; +import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; +import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential; +import at.gv.egiz.eaaf.modules.pvp2.api.reqattr.EaafRequestedAttribute; +import at.gv.egiz.eaaf.modules.pvp2.exception.SamlSigningException; +import at.gv.egiz.eaaf.modules.pvp2.exception.SchemaValidationException; +import net.shibboleth.utilities.java.support.xml.QNameSupport; +import net.shibboleth.utilities.java.support.xml.SerializeSupport; public class Saml2Utils { private static final Logger log = LoggerFactory.getLogger(Saml2Utils.class); @@ -152,6 +163,54 @@ public class Saml2Utils { } + /** + * SAML2 message unmarshaller that performs schema validation before unmarshall the message. + * + * @param messageStream SAML2 message that shoulld be unmarshalled + * @return OpenSAML XML object + * @throws MessageDecodingException In case of a schema-validation or unmarshalling error + */ + public static XMLObject unmarshallMessage(final InputStream messageStream) throws MessageDecodingException { + try { + final Element samlElement = DomUtils.parseXmlValidating(messageStream); + + if (log.isTraceEnabled()) { + log.trace("Resultant DOM message was:"); + log.trace(SerializeSupport.nodeToString(samlElement)); + } + + log.debug("Unmarshalling DOM parsed from InputStream"); + final Unmarshaller unmarshaller = XMLObjectSupport.getUnmarshaller(samlElement); + if (unmarshaller == null) { + log.error("Unable to unmarshall InputStream, no unmarshaller registered for element " + + QNameSupport.getNodeQName(samlElement)); + throw new UnmarshallingException( + "Unable to unmarshall InputStream, no unmarshaller registered for element " + + QNameSupport.getNodeQName(samlElement)); + } + + final XMLObject message = unmarshaller.unmarshall(samlElement); + + log.debug("InputStream succesfully unmarshalled"); + + return message; + + } catch (final UnmarshallingException e) { + log.error("Error unmarshalling message from input stream", e); + throw new MessageDecodingException("Error unmarshalling message from input stream", e); + + } catch (ParserConfigurationException | SAXException e) { + log.warn("Message schema-validation failed."); + throw new MessageDecodingException("Message schema-validation failed.", + new SchemaValidationException("internal.pvp.03", new Object[] { e.getMessage() }, e)); + + } catch (final IOException e) { + log.error("Error read message from input stream", e); + throw new MessageDecodingException("Error read message from input stream", e); + + } + } + /** * Select signature algorithm for a given credential. * diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/AbstractRequestSignedSecurityPolicyRule.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/AbstractRequestSignedSecurityPolicyRule.java deleted file mode 100644 index 380e735c..00000000 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/AbstractRequestSignedSecurityPolicyRule.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a - * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European - * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in - * compliance with the Licence. You may obtain a copy of the Licence at: - * https://joinup.ec.europa.eu/news/understanding-eupl-v12 - * - * Unless required by applicable law or agreed to in writing, software distributed under the Licence - * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the Licence for the specific language governing permissions and limitations under - * the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text file for details on the - * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative - * works that you distribute must include a readable copy of the "NOTICE" text file. -*/ - -package at.gv.egiz.eaaf.modules.pvp2.impl.verification; - -import javax.xml.namespace.QName; -import javax.xml.transform.dom.DOMSource; -import javax.xml.validation.Schema; -import javax.xml.validation.Validator; - -import at.gv.egiz.eaaf.modules.pvp2.exception.SchemaValidationException; - -import org.apache.commons.lang3.StringUtils; -import org.opensaml.core.criterion.EntityIdCriterion; -import org.opensaml.saml.common.SignableSAMLObject; -import org.opensaml.saml.common.xml.SAMLConstants; -import org.opensaml.saml.common.xml.SAMLSchemaBuilder; -import org.opensaml.saml.common.xml.SAMLSchemaBuilder.SAML1Version; -import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator; -import org.opensaml.security.MetadataCriteria; -import org.opensaml.security.credential.UsageType; -import org.opensaml.security.criteria.UsageCriterion; -import org.opensaml.ws.security.SecurityPolicyException; -import org.opensaml.ws.security.SecurityPolicyRule; -import org.opensaml.xmlsec.signature.support.SignatureTrustEngine; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.w3c.dom.Element; -import org.xml.sax.SAXException; - -import net.shibboleth.utilities.java.support.resolver.CriteriaSet; - -/** - * Signature Policy for SAML2 redirect-binding. - * - * @author tlenz - * - */ -public abstract class AbstractRequestSignedSecurityPolicyRule implements SecurityPolicyRule { - - private static final Logger log = - LoggerFactory.getLogger(AbstractRequestSignedSecurityPolicyRule.class); - - private static SAMLSchemaBuilder schemaBuilder = new SAMLSchemaBuilder(SAML1Version.SAML_11); - - private SignatureTrustEngine trustEngine = null; - private QName peerEntityRole = null; - - /** - * Role initializer. - * - * @param peerEntityRole - * - */ - public AbstractRequestSignedSecurityPolicyRule(final SignatureTrustEngine trustEngine, - final QName peerEntityRole) { - this.trustEngine = trustEngine; - this.peerEntityRole = peerEntityRole; - - } - - /** - * Reload the PVP metadata for a given entity. - * - * @param entityID for which the metadata should be refreshed. - * @return true if the refresh was successful, otherwise false - */ - protected abstract boolean refreshMetadataProvider(String entityID); - - protected abstract SignableSAMLObject getSignedSamlObject(XMLObject inboundData); - - /* - * (non-Javadoc) - * - * @see - * org.opensaml.ws.security.SecurityPolicyRule#evaluate(org.opensaml.ws.message. - * MessageContext) - */ - @Override - public void evaluate(final MessageContext context) throws SecurityPolicyException { - try { - verifySignature(context); - - } catch (final SecurityPolicyException e) { - if (StringUtils.isEmpty(context.getInboundMessageIssuer())) { - throw e; - - } - log.debug("PVP2X message validation FAILED. Reload metadata for entityID: " - + context.getInboundMessageIssuer()); - if (!refreshMetadataProvider(context.getInboundMessageIssuer())) { - throw e; - } else { - log.trace("PVP2X metadata reload finished. Check validate message again."); - verifySignature(context); - - } - log.trace("Second PVP2X message validation finished"); - - } - - } - - private void verifySignature(final MessageContext context) throws SecurityPolicyException { - final SignableSAMLObject samlObj = getSignedSamlObject(context.getInboundMessage()); - if (samlObj != null && samlObj.getSignature() != null) { - - final SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator(); - try { - profileValidator.validate(samlObj.getSignature()); - performSchemaValidation(samlObj.getDOM()); - - } catch (final ValidationException e) { - log.warn("Signature is not conform to SAML signature profile", e); - throw new SecurityPolicyException("Signature is not conform to SAML signature profile"); - - } catch (final SchemaValidationException e) { - log.warn("Signature is not conform to SAML signature profile", e); - throw new SecurityPolicyException("Signature is not conform to SAML signature profile"); - - } - - final CriteriaSet criteriaSet = new CriteriaSet(); - criteriaSet.add(new EntityIdCriterion(context.getInboundMessageIssuer())); - criteriaSet.add(new MetadataCriteria(peerEntityRole, SAMLConstants.SAML20P_NS)); - criteriaSet.add(new UsageCriterion(UsageType.SIGNING)); - - try { - if (!trustEngine.validate(samlObj.getSignature(), criteriaSet)) { - throw new SecurityPolicyException("Signature validation FAILED."); - - } - log.debug("PVP message signature valid."); - - } catch (final org.opensaml.xml.security.SecurityException e) { - log.info("PVP2x message signature validation FAILED. Message:" + e.getMessage()); - throw new SecurityPolicyException("Signature validation FAILED."); - - } - - } else { - throw new SecurityPolicyException("PVP Message is not signed."); - - } - - } - - private void performSchemaValidation(final Element source) throws SchemaValidationException { - - String err = null; - try { - final Schema test = schemaBuilder.getSAMLSchema(); - final Validator val = test.newValidator(); - val.validate(new DOMSource(source)); - log.debug("Schema validation check done OK"); - return; - - } catch (final SAXException e) { - err = e.getMessage(); - if (log.isDebugEnabled() || log.isTraceEnabled()) { - log.warn("Schema validation FAILED with exception:", e); - } else { - log.warn("Schema validation FAILED with message: " + e.getMessage()); - } - - } catch (final Exception e) { - err = e.getMessage(); - if (log.isDebugEnabled() || log.isTraceEnabled()) { - log.warn("Schema validation FAILED with exception:", e); - } else { - log.warn("Schema validation FAILED with message: " + e.getMessage()); - } - - } - - throw new SchemaValidationException("pvp2.22", new Object[] { err }); - - } - -} diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/PvpSignedRequestPolicyRule.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/PvpSignedRequestPolicyRule.java deleted file mode 100644 index 9c02221c..00000000 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/PvpSignedRequestPolicyRule.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a - * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European - * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in - * compliance with the Licence. You may obtain a copy of the Licence at: - * https://joinup.ec.europa.eu/news/understanding-eupl-v12 - * - * Unless required by applicable law or agreed to in writing, software distributed under the Licence - * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the Licence for the specific language governing permissions and limitations under - * the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text file for details on the - * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative - * works that you distribute must include a readable copy of the "NOTICE" text file. -*/ - -package at.gv.egiz.eaaf.modules.pvp2.impl.verification; - -import javax.xml.namespace.QName; - -import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IRefreshableMetadataProvider; - -import org.opensaml.saml.common.SignableSAMLObject; -import org.opensaml.saml2.metadata.provider.MetadataProvider; -import org.opensaml.xmlsec.signature.support.SignatureTrustEngine; - -public class PvpSignedRequestPolicyRule extends AbstractRequestSignedSecurityPolicyRule { - - private IRefreshableMetadataProvider metadataProvider = null; - - /** - * EAAF specific signature rule for OpenSAML2 redirect-binding. - * - * @param metadataProvider SAML2 metadata provider - * @param trustEngine SAML2 TrustEngine - * @param peerEntityRole Role of the Entity - */ - public PvpSignedRequestPolicyRule(final MetadataProvider metadataProvider, - final SignatureTrustEngine trustEngine, final QName peerEntityRole) { - super(trustEngine, peerEntityRole); - if (metadataProvider instanceof IRefreshableMetadataProvider) { - this.metadataProvider = (IRefreshableMetadataProvider) metadataProvider; - } - - } - - /* - * (non-Javadoc) - * - * @see at.gv.egovernment.moa.id.protocols.pvp2x.validation. - * AbstractRequestSignedSecurityPolicyRule# - * refreshMetadataProvider(java.lang.String) - */ - @Override - protected boolean refreshMetadataProvider(final String entityID) { - if (metadataProvider != null) { - return metadataProvider.refreshMetadataProvider(entityID); - } - - return false; - - } - - /* - * (non-Javadoc) - * - * @see at.gv.egovernment.moa.id.protocols.pvp2x.validation. - * AbstractRequestSignedSecurityPolicyRule# - * getSignedSAMLObject(org.opensaml.xml.XMLObject) - */ - @Override - protected SignableSAMLObject getSignedSamlObject(final XMLObject inboundData) { - if (inboundData instanceof SignableSAMLObject) { - return (SignableSAMLObject) inboundData; - } else { - return null; - } - } - -} diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/resources/eaaf_pvp.beans.xml b/eaaf_modules/eaaf_module_pvp2_core/src/main/resources/eaaf_pvp.beans.xml index 2d5dc6ea..a2b52fbc 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/resources/eaaf_pvp.beans.xml +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/resources/eaaf_pvp.beans.xml @@ -13,6 +13,9 @@ + + diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/resources/messages/pvp_messages.properties b/eaaf_modules/eaaf_module_pvp2_core/src/main/resources/messages/pvp_messages.properties index 6e647bd0..cee622c2 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/resources/messages/pvp_messages.properties +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/resources/messages/pvp_messages.properties @@ -1,6 +1,9 @@ internal.pvp.00=KeyStore: {0} Key with alias: {0} not found or contains no PrivateKey. internal.pvp.01=KeyStore: {0} contains an unsupported key with alias: {1} internal.pvp.02=PVP message contains no signature. +internal.pvp.03=Schema-validation of SAML2 message failed with error: {0} +internal.pvp.04=Can not initialize metadata provider for metadata: {0} +internal.pvp.05=Can not initialize metadata provider for metadata: {0}. Reason: {1} internal.pvp.95=OpenSAML {0}-binding message {1} failed. Reason: {2} internal.pvp.96=OpenSAML signing FAILED with key: {0}. Reason: {1} diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java index 80dfc400..a99c8461 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java @@ -6,18 +6,6 @@ import java.util.Base64; import javax.xml.parsers.ParserConfigurationException; -import at.gv.egiz.eaaf.core.api.IRequest; -import at.gv.egiz.eaaf.core.impl.idp.module.test.TestRequestImpl; -import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; -import at.gv.egiz.eaaf.modules.pvp2.api.message.InboundMessageInterface; -import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; -import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException; -import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2Exception; -import at.gv.egiz.eaaf.modules.pvp2.impl.binding.RedirectBinding; -import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.initialize.EaafOpenSaml3xInitializer; -import at.gv.egiz.eaaf.modules.pvp2.impl.validation.EaafUriCompare; -import at.gv.egiz.eaaf.modules.pvp2.test.dummy.DummyCredentialProvider; - import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomStringUtils; import org.junit.Assert; @@ -42,6 +30,18 @@ import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.xml.sax.SAXException; +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.impl.idp.module.test.TestRequestImpl; +import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; +import at.gv.egiz.eaaf.modules.pvp2.api.message.InboundMessageInterface; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException; +import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2Exception; +import at.gv.egiz.eaaf.modules.pvp2.impl.binding.RedirectBinding; +import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.PvpMetadataResolverFactory; +import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.initialize.EaafOpenSaml3xInitializer; +import at.gv.egiz.eaaf.modules.pvp2.impl.validation.EaafUriCompare; +import at.gv.egiz.eaaf.modules.pvp2.test.dummy.DummyCredentialProvider; import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import net.shibboleth.utilities.java.support.net.URIComparator; import net.shibboleth.utilities.java.support.xml.XMLParserException; @@ -60,6 +60,7 @@ public class RedirectBindingTest { @Autowired private RedirectBinding bindingImpl; @Autowired private DummyCredentialProvider credentialProvider; + @Autowired private PvpMetadataResolverFactory metadataResolverFactory; protected MockHttpServletRequest httpReq; protected MockHttpServletResponse httpResp; @@ -98,8 +99,10 @@ public class RedirectBindingTest { @Test public void decodeRequestSuccess() throws MessageDecodingException, SecurityException, IOException, Pvp2Exception { final String serviceUrl = "http://testservice.org"; - - final IPvp2MetadataProvider metadataProvider = null; + + final IPvp2MetadataProvider metadataProvider = + metadataResolverFactory.createMetadataProvider( + "classpath:/data/metadata_1.xml", null, "jUnit metadata resolver", null); final boolean isSpEndPoint = false; final URIComparator comparator = new EaafUriCompare(serviceUrl); diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AuthenticationAction.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AuthenticationAction.java index 74224dbe..200d98c4 100644 --- a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AuthenticationAction.java +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AuthenticationAction.java @@ -23,6 +23,19 @@ import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.joda.time.DateTime; +import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.saml2.core.Assertion; +import org.opensaml.saml.saml2.core.AuthnRequest; +import org.opensaml.saml.saml2.core.Response; +import org.opensaml.saml.saml2.metadata.AssertionConsumerService; +import org.opensaml.saml.saml2.metadata.EntityDescriptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.idp.IAction; import at.gv.egiz.eaaf.core.api.idp.IAuthData; @@ -44,20 +57,6 @@ import at.gv.egiz.eaaf.modules.pvp2.impl.message.PvpSProfileRequest; import at.gv.egiz.eaaf.modules.pvp2.impl.utils.AbstractCredentialProvider; import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils; -import org.joda.time.DateTime; -import org.opensaml.messaging.encoder.MessageEncodingException; -import org.opensaml.saml.common.xml.SAMLConstants; -import org.opensaml.saml.saml2.core.Assertion; -import org.opensaml.saml.saml2.core.AuthnRequest; -import org.opensaml.saml.saml2.core.Response; -import org.opensaml.saml.saml2.metadata.AssertionConsumerService; -import org.opensaml.saml.saml2.metadata.EntityDescriptor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Service; - @Service("PVPAuthenticationRequestAction") public class AuthenticationAction implements IAction { private static final Logger log = LoggerFactory.getLogger(AuthenticationAction.class); @@ -142,7 +141,7 @@ public class AuthenticationAction implements IAction { sloInformation.setSpEntityID(req.getServiceProviderConfiguration().getUniqueIdentifier()); return sloInformation; - } catch (MessageEncodingException | SecurityException e) { + } catch (SecurityException e) { log.warn("Message Encoding exception", e); throw new ResponderErrorException("pvp2.01", null, e); diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/AuthResponseBuilder.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/AuthResponseBuilder.java index ac551612..8cafebb9 100644 --- a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/AuthResponseBuilder.java +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/AuthResponseBuilder.java @@ -22,10 +22,6 @@ package at.gv.egiz.eaaf.modules.pvp2.idp.impl.builder; import java.util.ArrayList; import java.util.List; -import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; -import at.gv.egiz.eaaf.modules.pvp2.idp.exception.InvalidAssertionEncryptionException; -import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils; - import org.joda.time.DateTime; import org.opensaml.core.criterion.EntityIdCriterion; import org.opensaml.saml.common.xml.SAMLConstants; @@ -38,17 +34,21 @@ import org.opensaml.saml.saml2.core.Response; import org.opensaml.saml.saml2.encryption.Encrypter.KeyPlacement; import org.opensaml.saml.saml2.metadata.SPSSODescriptor; import org.opensaml.saml.security.impl.MetadataCredentialResolver; -import org.opensaml.saml2.metadata.provider.MetadataProvider; import org.opensaml.security.MetadataCriteria; import org.opensaml.security.credential.UsageType; import org.opensaml.security.criteria.UsageCriterion; import org.opensaml.security.x509.X509Credential; import org.opensaml.xmlsec.EncryptionParameters; +import org.opensaml.xmlsec.encryption.support.EncryptionException; import org.opensaml.xmlsec.encryption.support.KeyEncryptionParameters; import org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.idp.exception.InvalidAssertionEncryptionException; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils; import net.shibboleth.utilities.java.support.resolver.CriteriaSet; /** @@ -73,7 +73,7 @@ public class AuthResponseBuilder { * @return PVP2 S-Profile authentication response * @throws InvalidAssertionEncryptionException In case of an error */ - public static Response buildResponse(final MetadataProvider metadataProvider, + public static Response buildResponse(final IPvp2MetadataProvider metadataProvider, final String issuerEntityID, final RequestAbstractType req, final DateTime date, final Assertion assertion, final boolean enableEncryption) throws InvalidAssertionEncryptionException { -- cgit v1.2.3