From bee5dd259a4438d45ecd1bcc26dfba12875236d6 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Tue, 26 Jun 2018 11:03:48 +0200 Subject: initial commit --- .../pvp2/impl/validation/EAAFURICompare.java | 36 ++++ .../pvp2/impl/validation/TrustEngineFactory.java | 41 ++++ .../metadata/AbstractMetadataSignatureFilter.java | 128 +++++++++++++ .../metadata/PVPEntityCategoryFilter.java | 211 +++++++++++++++++++++ .../metadata/SchemaValidationFilter.java | 81 ++++++++ 5 files changed, 497 insertions(+) create mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/EAAFURICompare.java create mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/TrustEngineFactory.java create mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/AbstractMetadataSignatureFilter.java create mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/PVPEntityCategoryFilter.java create mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/SchemaValidationFilter.java (limited to 'eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation') diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/EAAFURICompare.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/EAAFURICompare.java new file mode 100644 index 00000000..4d0963cb --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/EAAFURICompare.java @@ -0,0 +1,36 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.impl.validation; + +import org.opensaml.common.binding.decoding.URIComparator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class EAAFURICompare implements URIComparator { + private static final Logger log = LoggerFactory.getLogger(EAAFURICompare.class); + + private String serviceURL = ""; + + /** + * + * + * @param serviceURL public URL of the PVP S-Profile endpoint + */ + public EAAFURICompare(String serviceURL) { + this.serviceURL = serviceURL; + } + + public boolean compare(String uri1, String uri2) { + if (this.serviceURL.equals(uri1)) + return true; + + else { + log.warn("PVP request destination-endpoint: " + uri1 + + " does not match to IDP endpoint:" + serviceURL); + return false; + + } + } + +} diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/TrustEngineFactory.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/TrustEngineFactory.java new file mode 100644 index 00000000..529f1ab6 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/TrustEngineFactory.java @@ -0,0 +1,41 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.impl.validation; + +import java.util.ArrayList; +import java.util.List; + +import org.opensaml.saml2.metadata.provider.MetadataProvider; +import org.opensaml.security.MetadataCredentialResolver; +import org.opensaml.xml.security.keyinfo.BasicProviderKeyInfoCredentialResolver; +import org.opensaml.xml.security.keyinfo.KeyInfoCredentialResolver; +import org.opensaml.xml.security.keyinfo.KeyInfoProvider; +import org.opensaml.xml.security.keyinfo.provider.DSAKeyValueProvider; +import org.opensaml.xml.security.keyinfo.provider.InlineX509DataProvider; +import org.opensaml.xml.security.keyinfo.provider.RSAKeyValueProvider; +import org.opensaml.xml.signature.SignatureTrustEngine; +import org.opensaml.xml.signature.impl.ExplicitKeySignatureTrustEngine; + +public class TrustEngineFactory { + + public static SignatureTrustEngine getSignatureKnownKeysTrustEngine(MetadataProvider provider) { + MetadataCredentialResolver resolver; + + resolver = new MetadataCredentialResolver(provider); + + List keyInfoProvider = new ArrayList(); + keyInfoProvider.add(new DSAKeyValueProvider()); + keyInfoProvider.add(new RSAKeyValueProvider()); + keyInfoProvider.add(new InlineX509DataProvider()); + + KeyInfoCredentialResolver keyInfoResolver = new BasicProviderKeyInfoCredentialResolver( + keyInfoProvider); + + ExplicitKeySignatureTrustEngine engine = new ExplicitKeySignatureTrustEngine( + resolver, keyInfoResolver); + + return engine; + + } + +} diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/AbstractMetadataSignatureFilter.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/AbstractMetadataSignatureFilter.java new file mode 100644 index 00000000..286c1999 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/AbstractMetadataSignatureFilter.java @@ -0,0 +1,128 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.opensaml.saml2.metadata.EntitiesDescriptor; +import org.opensaml.saml2.metadata.EntityDescriptor; +import org.opensaml.saml2.metadata.provider.MetadataFilter; +import org.opensaml.xml.XMLObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2MetadataException; +import at.gv.egiz.eaaf.modules.pvp2.exception.SignatureValidationException; + +public abstract class AbstractMetadataSignatureFilter implements MetadataFilter { + private static final Logger log = LoggerFactory.getLogger(AbstractMetadataSignatureFilter.class); + + public void doFilter(XMLObject metadata) throws SignatureValidationException { + try { + if (metadata instanceof EntitiesDescriptor) { + EntitiesDescriptor entitiesDescriptor = (EntitiesDescriptor) metadata; + if(entitiesDescriptor.getSignature() == null) { + throw new PVP2MetadataException("Root element of metadata file has to be signed", null); + } + processEntitiesDescriptor(entitiesDescriptor); + + + if (entitiesDescriptor.getEntityDescriptors().size() == 0) { + throw new PVP2MetadataException("No valid entity in metadata " + + entitiesDescriptor.getName() + ". Metadata is not loaded.", null); + } + + + } else if (metadata instanceof EntityDescriptor) { + EntityDescriptor entityDescriptor = (EntityDescriptor) metadata; + processEntityDescriptorr(entityDescriptor); + + } else { + throw new PVP2MetadataException("Invalid Metadata file Root element is no EntitiesDescriptor", null); + } + + + + log.info("Metadata signature policy check done OK"); + } catch (EAAFException e) { + log.warn("Metadata signature policy check FAILED.", e); + throw new SignatureValidationException(e); + } + } + + /** + * Signature verification of a SAML2 EntityDescriptor element + * + * @param desc + * @throws PVP2MetadataException if the signature is not valid or can not verified + */ + protected abstract void verify(EntityDescriptor desc) throws PVP2MetadataException; + + /** + * Signature verification of a SAML2 EntitiesDescriptor element + * + * @param desc + * @throws PVP2MetadataException if the signature is not valid or can not verified + */ + protected abstract void verify(EntitiesDescriptor desc) throws PVP2MetadataException; + + /** + * Verify a EntityDescriptor element of an EntitiesDescriptor + * + * @param entity EntityDescriptor to verify + * @param desc Full EntitiesDescriptor that contains the EntityDescriptor + * @throws PVP2MetadataException + */ + protected abstract void verify(EntityDescriptor entity, EntitiesDescriptor desc) throws PVP2MetadataException; + + + private void processEntityDescriptorr(EntityDescriptor desc) throws EAAFException { + verify(desc); + + } + + private void processEntitiesDescriptor(EntitiesDescriptor desc) throws EAAFException { + Iterator entID = desc.getEntitiesDescriptors().iterator(); + + if(desc.getSignature() != null) { + verify(desc); + + } + + while(entID.hasNext()) { + processEntitiesDescriptor(entID.next()); + } + + Iterator entIT = desc.getEntityDescriptors().iterator(); + List verifiedEntIT = new ArrayList(); + + //check every Entity + while(entIT.hasNext()) { + EntityDescriptor entity = entIT.next(); + log.debug("Validate metadata for entityID: " + entity.getEntityID() + " ..... "); + try { + verify(entity, desc); + + //add entity to verified entity-list + verifiedEntIT.add(entity); + log.debug("Metadata for entityID: " + entity.getEntityID() + " valid"); + + + } catch (Exception e) { + //remove entity of signature can not be verified. + log.info("Entity " + entity.getEntityID() + " is removed from metadata " + + desc.getName() + ". Entity verification error: " + e.getMessage()); + + } + + } + + //set only verified entity elements + desc.getEntityDescriptors().clear(); + desc.getEntityDescriptors().addAll(verifiedEntIT); + } + +} diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/PVPEntityCategoryFilter.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/PVPEntityCategoryFilter.java new file mode 100644 index 00000000..e29fb145 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/PVPEntityCategoryFilter.java @@ -0,0 +1,211 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata; + +import java.util.ArrayList; +import java.util.List; + +import org.opensaml.common.xml.SAMLConstants; +import org.opensaml.saml2.common.Extensions; +import org.opensaml.saml2.core.Attribute; +import org.opensaml.saml2.metadata.AttributeConsumingService; +import org.opensaml.saml2.metadata.EntitiesDescriptor; +import org.opensaml.saml2.metadata.EntityDescriptor; +import org.opensaml.saml2.metadata.LocalizedString; +import org.opensaml.saml2.metadata.RequestedAttribute; +import org.opensaml.saml2.metadata.SPSSODescriptor; +import org.opensaml.saml2.metadata.ServiceName; +import org.opensaml.saml2.metadata.provider.FilterException; +import org.opensaml.saml2.metadata.provider.MetadataFilter; +import org.opensaml.samlext.saml2mdattr.EntityAttributes; +import org.opensaml.xml.XMLObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.eaaf.core.impl.data.Trible; +import at.gv.egiz.eaaf.modules.pvp2.PVPConstants; +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2MetadataException; +import at.gv.egiz.eaaf.modules.pvp2.impl.builder.PVPAttributeBuilder; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.SAML2Utils; + +/** + * @author tlenz + * + */ +public class PVPEntityCategoryFilter implements MetadataFilter { + private static final Logger log = LoggerFactory.getLogger(PVPEntityCategoryFilter.class); + + private boolean isUsed = false; + + /** + * Filter to map PVP EntityCategories into a set of single PVP attributes + * + * @param isUsed if true PVP EntityCategories are mapped, otherwise they are ignored + * + */ + public PVPEntityCategoryFilter(boolean isUsed) { + this.isUsed = isUsed; + } + + + /* (non-Javadoc) + * @see org.opensaml.saml2.metadata.provider.MetadataFilter#doFilter(org.opensaml.xml.XMLObject) + */ + @Override + public void doFilter(XMLObject metadata) throws FilterException { + + if (isUsed) { + log.trace("Map PVP EntityCategory to single PVP Attributes ... "); + String entityId = null; + try { + if (metadata instanceof EntitiesDescriptor) { + log.trace("Find EnitiesDescriptor ... "); + EntitiesDescriptor entitiesDesc = (EntitiesDescriptor) metadata; + if (entitiesDesc.getEntityDescriptors() != null) { + for (EntityDescriptor el : entitiesDesc.getEntityDescriptors()) + resolveEntityCategoriesToAttributes(el); + + } + + } else if (metadata instanceof EntityDescriptor) { + log.trace("Find EntityDescriptor"); + resolveEntityCategoriesToAttributes((EntityDescriptor)metadata); + + + } else + throw new PVP2MetadataException("Invalid Metadata file Root element is no Entities- or EntityDescriptor", null); + + + + } catch (Exception e) { + log.warn("SAML2 Metadata processing FAILED: Can not resolve EntityCategories for metadata: " + entityId, e); + + } + + } else + log.trace("Filter to map PVP EntityCategory to single PVP Attributes is deactivated"); + + } + + private void resolveEntityCategoriesToAttributes(EntityDescriptor metadata) { + log.debug("Resolving EntityCategorie for Entity: " + metadata.getEntityID() + " ..."); + Extensions extensions = metadata.getExtensions(); + if (extensions != null) { + List listOfExt = extensions.getUnknownXMLObjects(); + if (listOfExt != null && !listOfExt.isEmpty()) { + log.trace("Find #" + listOfExt.size() + " 'Extension' elements "); + for (XMLObject el : listOfExt) { + log.trace("Find ExtensionElement: " + el.getElementQName().toString()); + if (el instanceof EntityAttributes) { + EntityAttributes entityAttrElem = (EntityAttributes)el; + if (entityAttrElem.getAttributes() != null) { + log.trace("Find EntityAttributes. Start attribute processing ..."); + for (Attribute entityAttr : entityAttrElem.getAttributes()) { + if (entityAttr.getName().equals(PVPConstants.ENTITY_CATEGORY_ATTRIBITE)) { + if (!entityAttr.getAttributeValues().isEmpty()) { + String entityAttrValue = entityAttr.getAttributeValues().get(0).getDOM().getTextContent(); + if (PVPConstants.EGOVTOKEN.equals(entityAttrValue)) { + log.debug("Find 'EGOVTOKEN' EntityAttribute. Adding single pvp attributes ... "); + addAttributesToEntityDescriptor(metadata, + buildAttributeList(PVPConstants.EGOVTOKEN_PVP_ATTRIBUTES), + entityAttrValue); + + + } else if (PVPConstants.CITIZENTOKEN.equals(entityAttrValue)) { + log.debug("Find 'CITIZENTOKEN' EntityAttribute. Adding single pvp attributes ... "); + addAttributesToEntityDescriptor(metadata, + buildAttributeList(PVPConstants.CITIZENTOKEN_PVP_ATTRIBUTES), + entityAttrValue); + + } else + log.info("EntityAttributeValue: " + entityAttrValue + " is UNKNOWN!"); + + } else + log.info("EntityAttribute: No attribute value"); + + } else + log.info("EntityAttribute: " + entityAttr.getName() + " is NOT supported"); + + } + + } else + log.info("Can NOT resolve EntityAttributes! Reason: Only EntityAttributes are supported!"); + + } + } + + } else + log.trace("'Extension' element is 'null' or empty"); + + } else + log.trace("No 'Extension' element found"); + + } + + /** + * @param metadata + * @param attrList + */ + private void addAttributesToEntityDescriptor(EntityDescriptor metadata, List attrList, String entityAttr) { + SPSSODescriptor spSSODesc = metadata.getSPSSODescriptor(SAMLConstants.SAML20P_NS); + if (spSSODesc != null) { + if (spSSODesc.getAttributeConsumingServices() == null || + spSSODesc.getAttributeConsumingServices().isEmpty()) { + log.trace("No 'AttributeConsumingServices' found. Added it ..."); + + AttributeConsumingService attributeService = SAML2Utils.createSAMLObject(AttributeConsumingService.class); + attributeService.setIndex(0); + attributeService.setIsDefault(true); + ServiceName serviceName = SAML2Utils.createSAMLObject(ServiceName.class); + serviceName.setName(new LocalizedString("Default Service", "en")); + attributeService.getNames().add(serviceName); + + if (attrList != null && !attrList.isEmpty()) { + attributeService.getRequestAttributes().addAll(attrList); + log.info("Add " + attrList.size() + " attributes for 'EntityAttribute': " + entityAttr); + + } + + spSSODesc.getAttributeConsumingServices().add(attributeService); + + } else { + log.debug("Find 'AttributeConsumingServices'. Starting updating process ... "); + for (AttributeConsumingService el : spSSODesc.getAttributeConsumingServices()) { + log.debug("Update 'AttributeConsumingService' with Index: " + el.getIndex()); + + //load currently requested attributes + List currentlyReqAttr = new ArrayList(); + for (RequestedAttribute reqAttr : el.getRequestAttributes()) + currentlyReqAttr.add(reqAttr.getName()); + + + //check against EntityAttribute List + for (RequestedAttribute entityAttrListEl : attrList) { + if (!currentlyReqAttr.contains(entityAttrListEl.getName())) { + el.getRequestAttributes().add(entityAttrListEl); + + } else + log.debug("'AttributeConsumingService' already contains attr: " + entityAttrListEl.getName()); + + } + + } + + } + + } else + log.info("Can ONLY add 'EntityAttributes' to 'SPSSODescriptor'"); + + } + + private List buildAttributeList(List> attrSet) { + List requestedAttributes = new ArrayList(); + for (Trible el : attrSet) + requestedAttributes.add(PVPAttributeBuilder.buildReqAttribute(el.getFirst(), el.getSecond(), el.getThird())); + + return requestedAttributes; + + + } + +} diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/SchemaValidationFilter.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/SchemaValidationFilter.java new file mode 100644 index 00000000..a7dddd32 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/SchemaValidationFilter.java @@ -0,0 +1,81 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata; + +import javax.xml.transform.dom.DOMSource; +import javax.xml.validation.Schema; +import javax.xml.validation.Validator; + +import org.opensaml.common.xml.SAMLSchemaBuilder; +import org.opensaml.saml2.metadata.provider.FilterException; +import org.opensaml.saml2.metadata.provider.MetadataFilter; +import org.opensaml.xml.XMLObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.SAXException; + +import at.gv.egiz.eaaf.modules.pvp2.exception.SchemaValidationException; + +/** + * @author tlenz + * + */ +public class SchemaValidationFilter implements MetadataFilter { + private static final Logger log = LoggerFactory.getLogger(SchemaValidationFilter.class); + private boolean isActive = true; + + public SchemaValidationFilter() { + } + + /** + * + */ + public SchemaValidationFilter(boolean useSchemaValidation) { + this.isActive = useSchemaValidation; + } + + + /* (non-Javadoc) + * @see org.opensaml.saml2.metadata.provider.MetadataFilter#doFilter(org.opensaml.xml.XMLObject) + */ + @Override + public void doFilter(XMLObject arg0) throws FilterException { + + String errString = null; + + if (isActive) { + try { + Schema test = SAMLSchemaBuilder.getSAML11Schema(); + Validator val = test.newValidator(); + DOMSource source = new DOMSource(arg0.getDOM()); + val.validate(source); + log.info("Metadata Schema validation check done OK"); + return; + + } catch (SAXException e) { + if (log.isDebugEnabled() || log.isTraceEnabled()) + log.warn("Metadata Schema validation FAILED with exception:", e); + else + log.warn("Metadata Schema validation FAILED with message: "+ e.getMessage()); + + errString = e.getMessage(); + + } catch (Exception e) { + if (log.isDebugEnabled() || log.isTraceEnabled()) + log.warn("Metadata Schema validation FAILED with exception:", e); + else + log.warn("Metadata Schema validation FAILED with message: "+ e.getMessage()); + + errString = e.getMessage(); + + } + + throw new FilterException( + new SchemaValidationException("Metadata Schema validation FAILED with message: "+ errString, null)); + + } else + log.info("Metadata Schema validation check is DEACTIVATED!"); + + } + +} -- cgit v1.2.3