[java-opensaml] branch master updated: Extend EntityAttributesFilter with pre-validation feature and add test.

Scott Cantor cantor.2 at osu.edu
Tue May 2 14:12:27 EDT 2017


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch master
in repository java-opensaml.

View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=80c3f5aa5db3122286386dd2d070173d580b9ee8

The following commit(s) were added to refs/heads/master by this push:
       new  80c3f5a   Extend EntityAttributesFilter with pre-validation feature and add test.
80c3f5a is described below

commit 80c3f5aa5db3122286386dd2d070173d580b9ee8
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue May 2 14:12:19 2017 -0400

    Extend EntityAttributesFilter with pre-validation feature and add test.
---
 .../saml/ext/saml2mdattr/EntityAttributes.java     |   4 +
 .../ext/saml2mdattr/impl/EntityAttributesImpl.java |  18 ++-
 .../filter/impl/EntityAttributesFilter.java        |  73 +++++++++-
 .../filter/impl/EntityAttributesFilterTest.java    | 155 +++++++++++++++++++++
 4 files changed, 244 insertions(+), 6 deletions(-)

diff --git a/opensaml-saml-api/src/main/java/org/opensaml/saml/ext/saml2mdattr/EntityAttributes.java b/opensaml-saml-api/src/main/java/org/opensaml/saml/ext/saml2mdattr/EntityAttributes.java
index 45e807a..784bce9 100644
--- a/opensaml-saml-api/src/main/java/org/opensaml/saml/ext/saml2mdattr/EntityAttributes.java
+++ b/opensaml-saml-api/src/main/java/org/opensaml/saml/ext/saml2mdattr/EntityAttributes.java
@@ -43,6 +43,10 @@ public interface EntityAttributes extends SAMLObject {
     public static final QName TYPE_NAME =
         new QName(SAMLConstants.SAML20MDATTR_NS, TYPE_LOCAL_NAME, SAMLConstants.SAML20MDATTR_PREFIX);
     
+    /* TODO: add a modifiable accessor of the child collection in 4.0  
+    public List<? extends SAMLObject> getEntityAttributesChildren();
+     */
+    
     /**
      * Gets the attributes about the entity.
      * 
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/ext/saml2mdattr/impl/EntityAttributesImpl.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/ext/saml2mdattr/impl/EntityAttributesImpl.java
index e6466e5..f8dd630 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/ext/saml2mdattr/impl/EntityAttributesImpl.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/ext/saml2mdattr/impl/EntityAttributesImpl.java
@@ -28,12 +28,11 @@ import org.opensaml.saml.common.SAMLObject;
 import org.opensaml.saml.ext.saml2mdattr.EntityAttributes;
 import org.opensaml.saml.saml2.core.Assertion;
 import org.opensaml.saml.saml2.core.Attribute;
-import org.opensaml.saml.saml2.core.Evidentiary;
 
 /** Concrete implementation of {@link EntityAttributes}. */
 public class EntityAttributesImpl extends AbstractSAMLObject implements EntityAttributes {
 
-    /** Assertion of the Evidence. */
+    /** Extension data. */
     private final IndexedXMLObjectChildrenList<? extends SAMLObject> attributeInfo;
 
     /**
@@ -45,7 +44,7 @@ public class EntityAttributesImpl extends AbstractSAMLObject implements EntityAt
      */
     protected EntityAttributesImpl(String namespaceURI, String elementLocalName, String namespacePrefix) {
         super(namespaceURI, elementLocalName, namespacePrefix);
-        attributeInfo = new IndexedXMLObjectChildrenList<Evidentiary>(this);
+        attributeInfo = new IndexedXMLObjectChildrenList<>(this);
     }
 
     /** {@inheritDoc} */
@@ -57,6 +56,19 @@ public class EntityAttributesImpl extends AbstractSAMLObject implements EntityAt
     public List<Assertion> getAssertions() {
         return (List<Assertion>) attributeInfo.subList(Assertion.DEFAULT_ELEMENT_NAME);
     }
+    
+    /**
+     * Get the list of {@link Attribute} and {@link Assertion} content.
+     * 
+     * TODO: this is implementation only until 4.0 when we can promote this to the API
+     * 
+     * @return a modifiable list of the children
+     * 
+     * @since 3.4.0
+     */
+    public List<? extends SAMLObject> getEntityAttributesChildren() {
+        return attributeInfo;
+    }
 
     /** {@inheritDoc} */
     public List<XMLObject> getOrderedChildren() {
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/resolver/filter/impl/EntityAttributesFilter.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/resolver/filter/impl/EntityAttributesFilter.java
index 41458a2..947a723 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/resolver/filter/impl/EntityAttributesFilter.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/resolver/filter/impl/EntityAttributesFilter.java
@@ -19,12 +19,16 @@ package org.opensaml.saml.metadata.resolver.filter.impl;
 
 
 import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
 import org.opensaml.core.xml.XMLObject;
@@ -32,8 +36,10 @@ import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
 import org.opensaml.core.xml.io.MarshallingException;
 import org.opensaml.core.xml.io.UnmarshallingException;
 import org.opensaml.core.xml.util.XMLObjectSupport;
+import org.opensaml.saml.common.SAMLObject;
 import org.opensaml.saml.common.SAMLObjectBuilder;
 import org.opensaml.saml.ext.saml2mdattr.EntityAttributes;
+import org.opensaml.saml.ext.saml2mdattr.impl.EntityAttributesImpl;
 import org.opensaml.saml.metadata.resolver.filter.FilterException;
 import org.opensaml.saml.metadata.resolver.filter.MetadataFilter;
 import org.opensaml.saml.saml2.core.Attribute;
@@ -53,10 +59,13 @@ import com.google.common.collect.Multimap;
  * A filter that adds {@link EntityAttributes} extension content to entities in order to drive software
  * behavior based on them.
  * 
- * The entities to annotate are identified with a {@link Predicate}, and multiple attributes can be
- * associated with each.
+ * <p>The entities to annotate are identified with a {@link Predicate}, and multiple attributes can be
+ * associated with each.</p>
+ * 
+ * <p>As of 3.4.0, another predicate can be set to validate pre-existing extension content to better
+ * protect use cases of this component.</p>
  */
-public class EntityAttributesFilter implements MetadataFilter {
+public class EntityAttributesFilter extends AbstractInitializableComponent implements MetadataFilter {
 
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(EntityAttributesFilter.class);
@@ -64,6 +73,9 @@ public class EntityAttributesFilter implements MetadataFilter {
     /** Rules for adding attributes. */
     @Nonnull @NonnullElements private Multimap<Predicate<EntityDescriptor>,Attribute> applyMap;
 
+    /** A condition to apply to pre-existing tags to determine their legitimacy. */
+    @Nullable private Predicate<Attribute> attributeFilter;
+    
     /** Builder for {@link Extensions}. */
     @Nonnull private final SAMLObjectBuilder<Extensions> extBuilder;
 
@@ -78,6 +90,7 @@ public class EntityAttributesFilter implements MetadataFilter {
         entityAttributesBuilder = (SAMLObjectBuilder<EntityAttributes>)
                 XMLObjectProviderRegistrySupport.getBuilderFactory().<EntityAttributes>getBuilderOrThrow(
                         EntityAttributes.DEFAULT_ELEMENT_NAME);
+        applyMap = ArrayListMultimap.create();
     }
     
     /**
@@ -86,6 +99,7 @@ public class EntityAttributesFilter implements MetadataFilter {
      * @param rules rules to apply
      */
     public void setRules(@Nonnull @NonnullElements final Map<Predicate<EntityDescriptor>,Collection<Attribute>> rules) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
         Constraint.isNotNull(rules, "Rules map cannot be null");
         
         applyMap = ArrayListMultimap.create(rules.size(), 1);
@@ -96,6 +110,22 @@ public class EntityAttributesFilter implements MetadataFilter {
         }
     }
 
+    /**
+     * Set a condition to apply to any pre-existing extension attributes, such that failure
+     * causes their removal.
+     * 
+     * <p>If not set, then anything is allowed.</p>
+     * 
+     * @param condition condition to apply
+     * 
+     * @since 3.4.0
+     */
+    public void setAttributeFilter(@Nullable final Predicate<Attribute> condition) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        attributeFilter = condition;
+    }
+
     /** {@inheritDoc} */
     @Override
     @Nullable public XMLObject filter(@Nullable final XMLObject metadata) throws FilterException {
@@ -118,6 +148,10 @@ public class EntityAttributesFilter implements MetadataFilter {
      * @param descriptor entity descriptor to filter
      */
     protected void filterEntityDescriptor(@Nonnull final EntityDescriptor descriptor) {
+        if (attributeFilter != null) {
+            applyFilter(descriptor);
+        }
+        
         for (final Map.Entry<Predicate<EntityDescriptor>,Collection<Attribute>> entry : applyMap.asMap().entrySet()) {
             if (!entry.getValue().isEmpty() && entry.getKey().apply(descriptor)) {
                 
@@ -167,4 +201,37 @@ public class EntityAttributesFilter implements MetadataFilter {
         }
     }
 
+    /**
+     * Apply whitelist to metadata on input.
+     * 
+     * @param descriptor input to evaluate
+     */
+    @Nullable private void applyFilter(@Nonnull final EntityDescriptor descriptor) {
+        final Extensions ext = descriptor.getExtensions();
+        if (ext != null) {
+            final Collection<XMLObject> entityAttributesCollection =
+                    ext.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME);
+            if (!entityAttributesCollection.isEmpty()) {
+                final EntityAttributes entityAttributes =
+                        (EntityAttributes) entityAttributesCollection.iterator().next();
+                if (entityAttributes instanceof EntityAttributesImpl) {
+                    // TODO: bug in original interface requires that we dive into the impl layer
+                    final List<? extends SAMLObject> attributes =
+                            ((EntityAttributesImpl) entityAttributes).getEntityAttributesChildren();
+                    final Iterator<? extends SAMLObject> iter = attributes.iterator();
+                    while (iter.hasNext()) {
+                        final SAMLObject attribute = iter.next();
+                        if (attribute instanceof Attribute) {
+                            if (!attributeFilter.apply((Attribute) attribute)) {
+                                log.warn("Filtering pre-existing attribute '{}' from entity '{}'",
+                                        ((Attribute) attribute).getName(), descriptor.getEntityID());
+                                iter.remove();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
 }
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/filter/impl/EntityAttributesFilterTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/filter/impl/EntityAttributesFilterTest.java
new file mode 100644
index 0000000..077d047
--- /dev/null
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/filter/impl/EntityAttributesFilterTest.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.saml.metadata.resolver.filter.impl;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+import org.opensaml.core.criterion.EntityIdCriterion;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.XMLObjectBaseTestCase;
+import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.ext.saml2mdattr.EntityAttributes;
+import org.opensaml.saml.metadata.resolver.impl.FilesystemMetadataResolver;
+import org.opensaml.saml.metadata.resolver.impl.FilesystemMetadataResolverTest;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeValue;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.opensaml.saml.saml2.metadata.Extensions;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+
+public class EntityAttributesFilterTest extends XMLObjectBaseTestCase implements Predicate<EntityDescriptor> {
+    
+    private SAMLObjectBuilder<Attribute> tagBuilder;
+    
+    private XMLObjectBuilder<XSString> valueBuilder;
+
+    private FilesystemMetadataResolver metadataProvider;
+    
+    private File mdFile;
+    
+    @BeforeMethod
+    protected void setUp() throws Exception {
+
+        tagBuilder = (SAMLObjectBuilder<Attribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
+                        Attribute.DEFAULT_ELEMENT_NAME);
+        valueBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(
+                XSString.TYPE_NAME);
+
+        URL mdURL = FilesystemMetadataResolverTest.class
+                .getResource("/org/opensaml/saml/saml2/metadata/InCommon-metadata.xml");
+        mdFile = new File(mdURL.toURI());
+
+        metadataProvider = new FilesystemMetadataResolver(mdFile);
+        metadataProvider.setParserPool(parserPool);
+    }
+    
+    @Test
+    public void test() throws ComponentInitializationException, ResolverException {
+        
+        final Attribute tag = tagBuilder.buildObject();
+        tag.setName("foo");
+        final XSString value = valueBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);
+        value.setValue("bar");
+        tag.getAttributeValues().add(value);
+        final Collection<Attribute> tags = Collections.singletonList(tag);
+        
+        final EntityAttributesFilter filter = new EntityAttributesFilter();
+        filter.setRules(Collections.<Predicate<EntityDescriptor>,Collection<Attribute>>singletonMap(this, tags));
+        filter.initialize();
+        
+        metadataProvider.setMetadataFilter(filter);
+        metadataProvider.setId("test");
+        metadataProvider.initialize();
+
+        EntityIdCriterion key = new EntityIdCriterion("https://carmenwiki.osu.edu/shibboleth");
+        EntityDescriptor entity = metadataProvider.resolveSingle(new CriteriaSet(key));
+        Assert.assertNotNull(entity);
+        Extensions exts = entity.getExtensions();
+        Assert.assertNotNull(exts);
+        Collection<XMLObject> extElements = exts.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME);
+        Assert.assertFalse(extElements.isEmpty());
+        EntityAttributes extTags = (EntityAttributes) extElements.iterator().next();
+        Assert.assertNotNull(extTags);
+        Assert.assertEquals(extTags.getAttributes().size(), 2);
+        Assert.assertEquals(extTags.getAttributes().get(0).getName(), "http://macedir.org/entity-category");
+        Assert.assertEquals(extTags.getAttributes().get(1).getName(), "foo");
+        
+        key = new EntityIdCriterion("https://cms.psu.edu/Shibboleth");
+        entity = metadataProvider.resolveSingle(new CriteriaSet(key));
+        Assert.assertNotNull(entity);
+        exts = entity.getExtensions();
+        Assert.assertNull(exts);
+    }
+
+    @Test
+    public void testWithWhitelist() throws ComponentInitializationException, ResolverException {
+        
+        final Attribute tag = tagBuilder.buildObject();
+        tag.setName("foo");
+        final XSString value = valueBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);
+        value.setValue("bar");
+        tag.getAttributeValues().add(value);
+        final Collection<Attribute> tags = Collections.singletonList(tag);
+        
+        final EntityAttributesFilter filter = new EntityAttributesFilter();
+        filter.setRules(Collections.<Predicate<EntityDescriptor>,Collection<Attribute>>singletonMap(this, tags));
+        filter.setAttributeFilter(new Predicate<Attribute>() {
+            public boolean apply(Attribute input) {
+                return "foo".equals(input.getName());
+            }
+        });
+        filter.initialize();
+        
+        metadataProvider.setMetadataFilter(filter);
+        metadataProvider.setId("test");
+        metadataProvider.initialize();
+
+        EntityIdCriterion key = new EntityIdCriterion("https://carmenwiki.osu.edu/shibboleth");
+        EntityDescriptor entity = metadataProvider.resolveSingle(new CriteriaSet(key));
+        Assert.assertNotNull(entity);
+        Extensions exts = entity.getExtensions();
+        Assert.assertNotNull(exts);
+        Collection<XMLObject> extElements = exts.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME);
+        Assert.assertFalse(extElements.isEmpty());
+        EntityAttributes extTags = (EntityAttributes) extElements.iterator().next();
+        Assert.assertNotNull(extTags);
+        Assert.assertEquals(extTags.getAttributes().size(), 1);
+        Assert.assertEquals(extTags.getAttributes().get(0).getName(), "foo");
+    }
+
+    /** {@inheritDoc} */
+    public boolean apply(final EntityDescriptor input) {
+        return input.getEntityID().equals("https://carmenwiki.osu.edu/shibboleth");
+    }
+
+}

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list