[java-identity-provider] branch master updated: Add mapped / EntitiesDescriptor checking to EntityAttribute rules.

Scott Cantor cantor.2 at osu.edu
Wed May 22 16:48:19 EDT 2019


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

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

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=45d4b04fd1473ff94fdddad3fc8cabb2ed2b7a45

The following commit(s) were added to refs/heads/master by this push:
       new  45d4b04   Add mapped / EntitiesDescriptor checking to EntityAttribute rules.
45d4b04 is described below

commit 45d4b04fd1473ff94fdddad3fc8cabb2ed2b7a45
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed May 22 16:48:14 2019 -0400

    Add mapped / EntitiesDescriptor checking to EntityAttribute rules.
---
 .../impl/AbstractEntityAttributePolicyRule.java    | 316 +++++++++++++++------
 ...uteRequesterEntityAttributeExactPolicyRule.java |  15 +-
 ...uteRequesterEntityAttributeRegexPolicyRule.java |  15 +-
 ...equesterEntityAttributeExactPolicyRuleTest.java |  20 +-
 .../policyrule/saml/impl/BaseMetadataTests.java    |  15 +-
 .../filter/impl/saml/shibboleth.net-metadata.xml   |  18 +-
 .../impl/AbstractEntityAttributeRuleParser.java    |   8 +-
 ...equesterEntityAttributeExactRuleParserTest.java |   6 +-
 ...equesterEntityAttributeRegexRuleParserTest.java |   6 +-
 .../attribute/filter/policyrule/requesterEA2.xml   |   3 +-
 .../src/main/resources/schema/shibboleth-afp.xsd   |  30 +-
 11 files changed, 323 insertions(+), 129 deletions(-)

diff --git a/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AbstractEntityAttributePolicyRule.java b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AbstractEntityAttributePolicyRule.java
index fea7490..f5e7eb7 100644
--- a/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AbstractEntityAttributePolicyRule.java
+++ b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AbstractEntityAttributePolicyRule.java
@@ -17,15 +17,22 @@
 
 package net.shibboleth.idp.attribute.filter.policyrule.saml.impl;
 
-import java.util.ArrayList;
+import java.time.Instant;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import net.shibboleth.idp.attribute.AttributesMapContainer;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
 import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
 import net.shibboleth.idp.attribute.filter.policyrule.impl.AbstractPolicyRule;
+import net.shibboleth.idp.saml.xmlobject.ScopedValue;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
@@ -33,20 +40,33 @@ import net.shibboleth.utilities.java.support.component.ComponentInitializationEx
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.xml.DOMTypeSupport;
 
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSBase64Binary;
+import org.opensaml.core.xml.schema.XSBoolean;
+import org.opensaml.core.xml.schema.XSDateTime;
+import org.opensaml.core.xml.schema.XSInteger;
 import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.core.xml.schema.XSURI;
 import org.opensaml.saml.ext.saml2mdattr.EntityAttributes;
 import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.metadata.EntitiesDescriptor;
 import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.opensaml.saml.saml2.metadata.Extensions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Predicates;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Multimap;
+
 /**
  * Base class for matchers that check whether a particular entity attribute is present and contains a given value.<br/>
  * 
- * Given the metadata for an entity, this class takes care of navigation to the attribute and extracting the values
+ * Given the metadata for an entity, this class takes care of navigation to the attribute and extracting the values,
+ * including optimized handling of mapped attributes.
  * 
  * Classes wishing to implement Entity Attribute matchers implement {@link #getEntityMetadata(AttributeFilterContext)}
  * to navigate to the entity (probably recipient or issuer) and {@link #entityAttributeValueMatches(String)} to
@@ -55,20 +75,23 @@ import org.slf4j.LoggerFactory;
 public abstract class AbstractEntityAttributePolicyRule extends AbstractPolicyRule {
 
     /** Class logger. */
-    private final Logger log = LoggerFactory.getLogger(AbstractEntityAttributePolicyRule.class);
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractEntityAttributePolicyRule.class);
 
     /** The name of the entity attribute the entity must have. */
-    private String attrName;
+    @NonnullAfterInit @NotEmpty private String attrName;
 
     /** The name format of the entity attribute the entity must have. */
-    private String nameFormat;
+    @Nullable @NotEmpty private String nameFormat;
+    
+    /** Whether to ignore unmapped attributes as an optimization. */
+    private boolean ignoreUnmappedEntityAttributes;
 
     /**
      * Gets the name of the entity attribute the entity must have.
      * 
      * @return name of the entity attribute the entity must have
      */
-    @NonnullAfterInit public String getAttributeName() {
+    @NonnullAfterInit @NotEmpty public String getAttributeName() {
         return attrName;
     }
 
@@ -77,7 +100,9 @@ public abstract class AbstractEntityAttributePolicyRule extends AbstractPolicyRu
      * 
      * @param attributeName name of the entity attribute the entity must have
      */
-    public void setAttributeName(@Nullable final String attributeName) {
+    public void setAttributeName(@Nullable @NotEmpty final String attributeName) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
         attrName = StringSupport.trimOrNull(attributeName);
     }
 
@@ -86,7 +111,7 @@ public abstract class AbstractEntityAttributePolicyRule extends AbstractPolicyRu
      * 
      * @return name format of the entity attribute the entity must have
      */
-    @Nullable public String getNameFormat() {
+    @Nullable @NotEmpty public String getNameFormat() {
         return nameFormat;
     }
 
@@ -95,9 +120,43 @@ public abstract class AbstractEntityAttributePolicyRule extends AbstractPolicyRu
      * 
      * @param attributeNameFormat name format of the entity attribute the entity must have
      */
-    public void setNameFormat(@Nullable final String attributeNameFormat) {
+    public void setNameFormat(@Nullable @NotEmpty final String attributeNameFormat) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
         nameFormat = StringSupport.trimOrNull(attributeNameFormat);
     }
+    
+    /**
+     * Gets whether to ignore unmapped/decoded EntityAttribute extensions as an optimization.
+     * 
+     * @return whether to ignore unmapped/decoded EntityAttribute extensions as an optimization
+     */
+    public boolean getIgnoreUnmappedEntityAttributes() {
+        return ignoreUnmappedEntityAttributes;
+    }
+
+    /**
+     * Sets whether to ignore unmapped/decoded EntityAttribute extensions as an optimization.
+     * 
+     * <p>Defaults to false. Only applies if {@link #nameFormat} property is set.</p>
+     * 
+     * @param flag flag to set
+     */
+    public void setIgnoreUnmappedEntityAttributes(final boolean flag) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        ignoreUnmappedEntityAttributes = flag;
+    }
+    
+    /** {@inheritDoc} */
+    @Override protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        if (attrName == null) {
+            throw new ComponentInitializationException(getLogPrefix() + " Attribute name is null");
+        } else if (nameFormat != null) {
+            ignoreUnmappedEntityAttributes = false;
+        }
+    }
 
     /**
      * Checks to see if the entity returned by {@link #getEntityMetadata(AttributeFilterContext)} contains the entity
@@ -114,53 +173,35 @@ public abstract class AbstractEntityAttributePolicyRule extends AbstractPolicyRu
 
         final EntityDescriptor entityDescriptor = getEntityMetadata(filterContext);
         if (entityDescriptor == null) {
-            log.info("{} No metadata available for the entity, returning FALSE", getLogPrefix());
+            log.info("{} No metadata available for entity, returning FALSE", getLogPrefix());
             return Tristate.FALSE;
         }
 
-        final List<XMLObject> attributeValues = getEntityAttributeValues(entityDescriptor);
-        if (attributeValues == null || attributeValues.isEmpty()) {
-            log.debug("{} Entity attribute {} for entity {} does not exist or contains no values, returning FALSE",
+        final Set<String> attributeValues = new HashSet<>();
+        
+        getEntityAttributeValues(entityDescriptor, entityDescriptor.getEntityID(), attributeValues);
+
+        XMLObject parent = entityDescriptor.getParent();
+        while (parent instanceof EntitiesDescriptor) {
+            getEntityAttributeValues(parent, ((EntitiesDescriptor) parent).getName(), attributeValues);
+            parent = parent.getParent();
+        }
+        
+        if (attributeValues.isEmpty()) {
+            log.debug("{} No values found for entity attribute {} for entity {}, returning FALSE",
                     getLogPrefix(), getAttributeName(), entityDescriptor.getEntityID());
             return Tristate.FALSE;
         }
-
-        log.debug("{} Checking if entity attribute {} contains the required value.", getLogPrefix(),
-                getAttributeName());
-        String valueString;
-        for (final XMLObject attributeValue : attributeValues) {
-            if (attributeValue instanceof XSAny) {
-                valueString = ((XSAny) attributeValue).getTextContent();
-            } else if (attributeValue instanceof XSString) {
-                valueString = ((XSString) attributeValue).getValue();
-            } else {
-                log.debug("{} Entity attribute {} contains the unsupported value type {}, skipping it", getLogPrefix(),
-                        getAttributeName(), attributeValue.getClass().getName());
-                continue;
-            }
-
-            if (valueString != null) {
-                if (entityAttributeValueMatches(valueString)) {
-                    log.debug("{} Entity attribute {} value {} meets matching requirements", getLogPrefix(),
-                            getAttributeName(), valueString);
-                    return Tristate.TRUE;
-                }
-                log.debug("{} Entity attribute {} value {} does not meet matching requirements", getLogPrefix(),
-                        getAttributeName(), valueString);
-            }
+        
+        if (entityAttributeValueMatches(attributeValues)) {
+            log.debug("{} Entity attribute values for {} match requirements", getLogPrefix(), getAttributeName());
+            return Tristate.TRUE;
         }
-
+        
+        log.debug("{} Entity attribute values for {} do not match requirements", getLogPrefix(), getAttributeName());
         return Tristate.FALSE;
     }
 
-    /** {@inheritDoc} */
-    @Override protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        if (attrName == null) {
-            throw new ComponentInitializationException(getLogPrefix() + " Attribute name is null");
-        }
-    }
-
     /**
      * Gets the entity descriptor for the entity to check.
      * 
@@ -171,76 +212,179 @@ public abstract class AbstractEntityAttributePolicyRule extends AbstractPolicyRu
     @Nullable protected abstract EntityDescriptor getEntityMetadata(AttributeFilterContext filterContext);
 
     /**
-     * Checks whether the given entity attribute value matches the rules for particular implementation of this functor.
+     * Checks whether the given entity attribute's values match for particular implementations of this functor.
      * 
-     * @param entityAttributeValue the entity attribute value, never null
+     * @param entityAttributeValues the entity attribute values
      * 
      * @return true if the value matches, false if not
      */
-    protected abstract boolean entityAttributeValueMatches(String entityAttributeValue);
+    protected abstract boolean entityAttributeValueMatches(
+            @Nonnull @NotEmpty @NonnullElements final Set<String> entityAttributeValues);
 
     /**
-         * Gets the entity attribute values from the given entity metadata. If both the attribute name and name format
-         * for this match functor is configured then both must match, otherwise only the attribute name must match.
-         * 
-         * @param entityDescriptor the metadata for the entity
-         * 
-         * @return the values of the designated attribute
-         */
+     * Gets the entity attribute values from the given metadata.
+     * 
+     * <p>If both the attribute name and name format for this match functor is configured then both must match,
+     * otherwise only the attribute name must match.</p>
+     * 
+     * @param metadataObject the metadata object
+     * @param name name of metadata object
+     * @param valueAccumulator stores values of the designated attribute
+     */
 // Checkstyle: CyclomaticComplexity OFF
-    @Nullable @NotEmpty @NonnullElements private List<XMLObject> getEntityAttributeValues(
-            @Nonnull final EntityDescriptor entityDescriptor) {
+    private void getEntityAttributeValues(@Nonnull final XMLObject metadataObject,
+            @Nullable @NotEmpty final String name, @Nonnull @NonnullElements final Set<String> valueAccumulator) {
         
-        List<XMLObject> valueAccumulator = null;
+        if (nameFormat == null) {
+            getMappedEntityAttributeValues(metadataObject, valueAccumulator);
+            if (ignoreUnmappedEntityAttributes) {
+                return;
+            }
+        }
         
         List<XMLObject> entityAttributesCollection = null;
-        if (entityDescriptor.getExtensions() != null) {
-            entityAttributesCollection =
-                    entityDescriptor.getExtensions().getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME);
+        
+        Extensions extensions = null;
+        if (metadataObject instanceof EntityDescriptor) {
+            extensions = ((EntityDescriptor) metadataObject).getExtensions();
+        } else if (metadataObject instanceof EntitiesDescriptor) {
+            extensions = ((EntitiesDescriptor) metadataObject).getExtensions();
+        }
+        
+        if (extensions != null) {
+            entityAttributesCollection = extensions.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME);
         }
+        
         if (entityAttributesCollection == null || entityAttributesCollection.isEmpty()) {
-            log.debug("{} EntityDescriptor for {} does not contain any EntityAttributes", getLogPrefix(),
-                    entityDescriptor.getEntityID());
-            return null;
+            log.debug("{} Metadata for {} does not contain EntityAttributes extension", getLogPrefix(), name);
+            return;
         }
-
+    
         if (entityAttributesCollection.size() > 1) {
-            log.debug("{} EntityDescriptor for {} contains more than one EntityAttributes extension,"
-                    + " only using the first one", getLogPrefix(), entityDescriptor.getEntityID());
+            log.debug("{} Metadata for {} contains more than one EntityAttributes extension,"
+                    + " only using the first one", getLogPrefix(), name);
         }
-
+    
         final List<Attribute> entityAttributes =
                 ((EntityAttributes) entityAttributesCollection.get(0)).getAttributes();
         if (entityAttributes == null || entityAttributes.isEmpty()) {
-            log.debug("{} EntityAttributes extension for {} does not contain any Attributes", getLogPrefix(),
-                    entityDescriptor.getEntityID());
-            return null;
+            log.debug("{} EntityAttributes extension for {} does not contain Attributes", getLogPrefix(), name);
+            return;
         }
-
+    
         for (final Attribute entityAttribute : entityAttributes) {
             if (!Objects.equals(getAttributeName(), entityAttribute.getName())) {
                 continue;
             }
-
+    
             if (getNameFormat() == null || (Objects.equals(getNameFormat(), entityAttribute.getNameFormat()))) {
-                log.debug("{} EntityDescriptor for {} contains an entity attribute with the name {} and the format {}",
-                        new Object[] {getLogPrefix(), entityDescriptor.getEntityID(), getAttributeName(),
-                                getNameFormat(),});
-                if (valueAccumulator == null) {
-                    valueAccumulator = new ArrayList<>();
+                log.debug("{} Metadata for {} contains Attribute with name {} and format {}",
+                        new Object[] {getLogPrefix(), name, getAttributeName(), getNameFormat(),});
+                
+                valueAccumulator.addAll(
+                        Collections2.filter(
+                                Collections2.transform(entityAttribute.getAttributeValues(), this::getStringValue),
+                                Predicates.notNull()));
+            }
+        }
+    }
+// Checkstyle: CyclomaticComplexity ON
+
+    /**
+     * Gets the mapped entity attribute values from the given metadata.
+     * 
+     * @param metadataObject the metadata object
+     * @param valueAccumulator stores values of the designated attribute
+     */
+    private void getMappedEntityAttributeValues(@Nonnull final XMLObject metadataObject,
+            @Nonnull @NonnullElements final Set<String> valueAccumulator) {
+        
+        final List<AttributesMapContainer> containerList =
+                metadataObject.getObjectMetadata().get(AttributesMapContainer.class);
+        if (null == containerList || containerList.isEmpty() || containerList.get(0).get() == null ||
+                containerList.get(0).get().isEmpty()) {
+            log.debug("{} No mapped entity attributes found for {}", getLogPrefix(), attrName);
+            return;
+        }
+        
+        log.debug("{} Checking for mapped entity attributes named {}", getLogPrefix(), attrName);
+        
+        int count = 0;
+        
+        final Multimap<String,? extends IdPAttribute> mappedAttributes = containerList.get(0).get();
+        for (final IdPAttribute attribute : mappedAttributes.get(attrName)) {
+            for (final IdPAttributeValue attributeValue : attribute.getValues()) {
+                if (attributeValue instanceof StringAttributeValue) {
+                    valueAccumulator.add(((StringAttributeValue) attributeValue).getValue());
+                    count++;
+                } else {
+                    log.error("{} Ignoring non-string value in mapped entity attribute {}", getLogPrefix(), attrName);
                 }
-                valueAccumulator.addAll(entityAttribute.getAttributeValues());
             }
         }
         
-        if (valueAccumulator != null && !valueAccumulator.isEmpty()) {
-            return valueAccumulator;
+        log.debug("{} Added {} values of mapped entity attribute {} for evaluation", getLogPrefix(), count, attrName);
+    }
+
+    /**
+     * Function to return an XMLObject in string form.
+     * 
+     * @param object object to decode
+     * 
+     * @return decoded string, or null
+     */
+// Checkstyle: CyclomaticComplexity OFF
+    @Nullable private String getStringValue(@Nonnull final XMLObject object) {
+        String retVal = null;
+
+        if (object instanceof XSString) {
+
+            retVal = ((XSString) object).getValue();
+
+        } else if (object instanceof XSURI) {
+
+            retVal = ((XSURI) object).getValue();
+
+        } else if (object instanceof XSBoolean) {
+
+            retVal = ((XSBoolean) object).getValue().getValue() ? "1" : "0";
+
+        } else if (object instanceof XSInteger) {
+
+            retVal = ((XSInteger) object).getValue().toString();
+
+        } else if (object instanceof XSDateTime) {
+
+            final Instant dt = ((XSDateTime) object).getValue();
+            if (dt != null) {
+                retVal = DOMTypeSupport.instantToString(dt);
+            } else {
+                retVal = null;
+            }
+
+        } else if (object instanceof XSBase64Binary) {
+
+            retVal = ((XSBase64Binary) object).getValue();
+            
+        } else if (object instanceof ScopedValue) {
+            
+            retVal = ((ScopedValue) object).getValue();
+
+        } else if (object instanceof XSAny) {
+
+            final XSAny wc = (XSAny) object;
+            if (wc.getUnknownAttributes().isEmpty() && wc.getUnknownXMLObjects().isEmpty()) {
+                retVal = wc.getTextContent();
+            } else {
+                retVal = null;
+            }
         }
 
-        log.debug("{} EntityDescriptor for {} does not contain entity attribute with the name {} and the format {}",
-                new Object[] {getLogPrefix(), entityDescriptor.getEntityID(), getAttributeName(), getNameFormat()});
-        return null;
+        if (null == retVal) {
+            log.info("Value of type {} could not be converted", object.getClass().getSimpleName());
+        }
+        return retVal;
     }
 // Checkstyle: CyclomaticComplexity ON
-
+    
 }
\ No newline at end of file
diff --git a/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AttributeRequesterEntityAttributeExactPolicyRule.java b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AttributeRequesterEntityAttributeExactPolicyRule.java
index 20e0c65..797cb9b 100644
--- a/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AttributeRequesterEntityAttributeExactPolicyRule.java
+++ b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AttributeRequesterEntityAttributeExactPolicyRule.java
@@ -17,13 +17,15 @@
 
 package net.shibboleth.idp.attribute.filter.policyrule.saml.impl;
 
-import java.util.Objects;
+import java.util.Set;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
@@ -41,14 +43,14 @@ public class AttributeRequesterEntityAttributeExactPolicyRule extends AbstractEn
     @Nonnull private final Logger log = LoggerFactory.getLogger(AttributeRequesterEntityAttributeExactPolicyRule.class);
 
     /** The value of the entity attribute the entity must have. */
-    @NonnullAfterInit private String value;
+    @NonnullAfterInit @NotEmpty private String value;
 
     /**
      * Gets the value of the entity attribute the entity must have.
      * 
      * @return value of the entity attribute the entity must have
      */
-    @NonnullAfterInit public String getValue() {
+    @NonnullAfterInit @NotEmpty public String getValue() {
         return value;
     }
 
@@ -57,14 +59,15 @@ public class AttributeRequesterEntityAttributeExactPolicyRule extends AbstractEn
      * 
      * @param attributeValue value of the entity attribute the entity must have
      */
-    public void setValue(@Nonnull final String attributeValue) {
+    public void setValue(@Nonnull @NotEmpty final String attributeValue) {
         value = Constraint.isNotNull(attributeValue, "Attribute value cannot be null.");
     }
 
     /** {@inheritDoc} */
     @Override
-    protected boolean entityAttributeValueMatches(@Nullable final String stringValue) {
-        return Objects.equals(value, stringValue);
+    protected boolean entityAttributeValueMatches(
+            @Nonnull @NotEmpty @NonnullElements final Set<String> entityAttributeValues) {
+        return entityAttributeValues.contains(value);
     }
 
     /** {@inheritDoc} */
diff --git a/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AttributeRequesterEntityAttributeRegexPolicyRule.java b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AttributeRequesterEntityAttributeRegexPolicyRule.java
index cd0889b..803119e 100644
--- a/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AttributeRequesterEntityAttributeRegexPolicyRule.java
+++ b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AttributeRequesterEntityAttributeRegexPolicyRule.java
@@ -17,21 +17,25 @@
 
 package net.shibboleth.idp.attribute.filter.policyrule.saml.impl;
 
-import java.util.regex.Matcher;
+import java.util.Set;
 import java.util.regex.Pattern;
 
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
 
 import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
 import org.opensaml.saml.saml2.metadata.EntityDescriptor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.Iterables;
+
 /**
  * Matcher functor that checks, via matching against a regular expression, if the attribute requester contains an entity
  * attribute with a given value.
@@ -63,9 +67,10 @@ public class AttributeRequesterEntityAttributeRegexPolicyRule extends AbstractEn
     }
 
     /** {@inheritDoc} */
-    @Override protected boolean entityAttributeValueMatches(final String entityAttributeValue) {
-        final Matcher valueMatcher = valueRegex.matcher(StringSupport.trim(entityAttributeValue));
-        return valueMatcher.matches();
+    @Override protected boolean entityAttributeValueMatches(
+            @Nonnull @NotEmpty @NonnullElements final Set<String> entityAttributeValues) {
+        
+        return Iterables.any(entityAttributeValues, v -> valueRegex.matcher(v).matches());
     }
 
     /** {@inheritDoc} */
diff --git a/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AttributeRequesterEntityAttributeExactPolicyRuleTest.java b/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AttributeRequesterEntityAttributeExactPolicyRuleTest.java
index f7b14f4..7e2e924 100644
--- a/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AttributeRequesterEntityAttributeExactPolicyRuleTest.java
+++ b/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/AttributeRequesterEntityAttributeExactPolicyRuleTest.java
@@ -31,16 +31,17 @@ import net.shibboleth.utilities.java.support.component.ComponentInitializationEx
 public class AttributeRequesterEntityAttributeExactPolicyRuleTest extends BaseMetadataTests {
 
     private AttributeRequesterEntityAttributeExactPolicyRule getMatcher() throws ComponentInitializationException {
-        return getMatcher("urn:example.org:policies", "urn:example.org:policy:1234", null);
+        return getMatcher("urn:example.org:policies", "urn:example.org:policy:1234", null, false);
     }
 
     private AttributeRequesterEntityAttributeExactPolicyRule getMatcher(String attributeName, String attributeValue,
-            String attributeNameFormat) throws ComponentInitializationException {
+            String attributeNameFormat, boolean ignoreUnmapped) throws ComponentInitializationException {
         AttributeRequesterEntityAttributeExactPolicyRule matcher = new AttributeRequesterEntityAttributeExactPolicyRule();
         matcher.setId("matcher");
         matcher.setAttributeName(attributeName);
         matcher.setValue(attributeValue);
         matcher.setNameFormat(attributeNameFormat);
+        matcher.setIgnoreUnmappedEntityAttributes(ignoreUnmapped);
         matcher.initialize();
         return matcher;
     }
@@ -51,26 +52,25 @@ public class AttributeRequesterEntityAttributeExactPolicyRuleTest extends BaseMe
         assertEquals(matcher.matches(metadataContext(idpEntity, "Principal")), Tristate.TRUE);
 
         assertEquals(matcher.matches(metadataContext(jiraEntity, "Principal")), Tristate.FALSE);
-
     }
 
     @Test public void testFormat() throws ComponentInitializationException {
 
         AttributeRequesterEntityAttributeExactPolicyRule matcher =
-                getMatcher("urn:example.org:entitlements", "urn:example.org:entitlements:1234", null);
+                getMatcher("urn:example.org:entitlements", "urn:example.org:entitlements:1234", null, true);
         assertEquals(matcher.getValue(), "urn:example.org:entitlements:1234");
         assertEquals(matcher.matches(metadataContext(idpEntity, "Principal")), Tristate.TRUE);
 
         assertEquals(matcher.matches(metadataContext(wikiEntity, "Principal")), Tristate.FALSE);
 
-        matcher = getMatcher("urn:example.org:entitlements", "urn:example.org:entitlements:1234", "foo");
+        matcher = getMatcher("urn:example.org:entitlements", "urn:example.org:entitlements:1234", "foo", true);
         assertEquals(matcher.matches(metadataContext(idpEntity, "Principal")), Tristate.FALSE);
 
         assertEquals(matcher.matches(metadataContext(jiraEntity, "Principal")), Tristate.FALSE);
 
         matcher =
                 getMatcher("urn:example.org:entitlements", "urn:example.org:entitlements:1234",
-                        "urn:oasis:names:tc:SAML:2.0:attrname-format:uri");
+                        "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", true);
         assertEquals(matcher.matches(metadataContext(idpEntity, "Principal")), Tristate.TRUE);
 
         assertEquals(matcher.matches(metadataContext(jiraEntity, "Principal")), Tristate.FALSE);
@@ -79,11 +79,11 @@ public class AttributeRequesterEntityAttributeExactPolicyRuleTest extends BaseMe
     @Test public void testNoMatch() throws ComponentInitializationException {
 
         AttributeRequesterEntityAttributeExactPolicyRule matcher =
-                getMatcher("urn:example.org:policies", "urn:example.org:policy:1235", null);
+                getMatcher("urn:example.org:policies", "urn:example.org:policy:1235", null, false);
         assertEquals(matcher.matches(metadataContext(idpEntity, "Principal")), Tristate.FALSE);
         assertEquals(matcher.matches(metadataContext(jiraEntity, "Principal")), Tristate.FALSE);
 
-        matcher = getMatcher("urn:example.org:policiess", "urn:example.org:policy:1234", null);
+        matcher = getMatcher("urn:example.org:policiess", "urn:example.org:policy:1234", null, false);
         assertEquals(matcher.matches(metadataContext(idpEntity, "Principal")), Tristate.FALSE);
         assertEquals(matcher.matches(metadataContext(noneEntity, "Principal")), Tristate.FALSE);
     }
@@ -91,11 +91,11 @@ public class AttributeRequesterEntityAttributeExactPolicyRuleTest extends BaseMe
     @Test public void testSplitAttribute() throws ComponentInitializationException {
 
         AttributeRequesterEntityAttributeExactPolicyRule matcher =
-                getMatcher("urn:example.org:policies", "urn:example.org:policy:1234", null);
+                getMatcher("urn:example.org:policies", "urn:example.org:policy:1234", null, false);
         assertEquals(matcher.matches(metadataContext(idpEntity, "Principal")), Tristate.TRUE);
         assertEquals(matcher.matches(metadataContext(jiraEntity, "Principal")), Tristate.FALSE);
 
-        matcher = getMatcher("urn:example.org:policies", "urn:example.org:policy:5678", null);
+        matcher = getMatcher("urn:example.org:policies", "urn:example.org:policy:5678", null, false);
         assertEquals(matcher.matches(metadataContext(idpEntity, "Principal")), Tristate.TRUE);
         assertEquals(matcher.matches(metadataContext(noneEntity, "Principal")), Tristate.FALSE);
     }
diff --git a/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/BaseMetadataTests.java b/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/BaseMetadataTests.java
index e07d45b..0f77a6d 100644
--- a/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/BaseMetadataTests.java
+++ b/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/saml/impl/BaseMetadataTests.java
@@ -17,19 +17,24 @@
 
 package net.shibboleth.idp.attribute.filter.policyrule.saml.impl;
 
+import java.util.Arrays;
 import java.util.Collections;
 
 import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
 import net.shibboleth.idp.attribute.filter.policyrule.saml.impl.AttributeRequesterEntityAttributeExactPolicyRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.metadata.impl.AttributeMappingNodeProcessor;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.service.MockReloadableService;
 
 import org.opensaml.core.xml.XMLObjectBaseTestCase;
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
 import org.opensaml.saml.metadata.resolver.filter.FilterException;
-import org.opensaml.saml.metadata.resolver.filter.MetadataNodeProcessor;
 import org.opensaml.saml.metadata.resolver.filter.impl.EntitiesDescriptorNameProcessor;
 import org.opensaml.saml.metadata.resolver.filter.impl.NodeProcessingMetadataFilter;
+import org.opensaml.saml.saml2.core.Attribute;
 import org.opensaml.saml.saml2.metadata.EntitiesDescriptor;
 import org.opensaml.saml.saml2.metadata.EntityDescriptor;
 import org.testng.annotations.BeforeClass;
@@ -61,8 +66,14 @@ public class BaseMetadataTests extends XMLObjectBaseTestCase {
             throws FilterException, ComponentInitializationException {
         metadata = unmarshallElement("/net/shibboleth/idp/filter/impl/saml/shibboleth.net-metadata.xml");
         
+        final AttributeTranscoderRegistryImpl registry = new AttributeTranscoderRegistryImpl();
+        registry.setId("test");
+        registry.setNamingRegistry(Collections.singletonMap(Attribute.class, new AbstractSAML2AttributeTranscoder.NamingFunction()));
+        registry.initialize();
+        
         final NodeProcessingMetadataFilter filter = new NodeProcessingMetadataFilter();
-        filter.setNodeProcessors(Collections.<MetadataNodeProcessor>singletonList(new EntitiesDescriptorNameProcessor()));
+        filter.setNodeProcessors(Arrays.asList(new EntitiesDescriptorNameProcessor(),
+                new AttributeMappingNodeProcessor(new MockReloadableService(registry))));
         filter.initialize();
         filter.filter(metadata);
 
diff --git a/idp-attribute-filter-impl/src/test/resources/net/shibboleth/idp/filter/impl/saml/shibboleth.net-metadata.xml b/idp-attribute-filter-impl/src/test/resources/net/shibboleth/idp/filter/impl/saml/shibboleth.net-metadata.xml
index fd7fadc..690b343 100644
--- a/idp-attribute-filter-impl/src/test/resources/net/shibboleth/idp/filter/impl/saml/shibboleth.net-metadata.xml
+++ b/idp-attribute-filter-impl/src/test/resources/net/shibboleth/idp/filter/impl/saml/shibboleth.net-metadata.xml
@@ -1,6 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport" xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="http://shibboleth.net">
     
+    <Extensions>
+        <mdattr:EntityAttributes>
+            <saml:Attribute Name="urn:example.org:entitlements" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"  xsi:type="xs:boolean">32</saml:AttributeValue>
+                <saml:AttributeValue>urn:example.org:entitlements:ABCD</saml:AttributeValue>
+            </saml:Attribute>
+        </mdattr:EntityAttributes>
+    </Extensions>
+    
     <EntityDescriptor entityID="https://idp.shibboleth.net/idp/shibboleth">
        
         <Extensions>
@@ -17,11 +26,10 @@
                 <saml:Attribute Name="urn:example.org:policies">
                     <saml:AttributeValue>urn:example.org:policy:5678</saml:AttributeValue>
                 </saml:Attribute>
-                <saml:Attribute Name="urn:example.org:entitlements" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
-                    <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"  xsi:type="xs:boolean">32</saml:AttributeValue>
-                    <saml:AttributeValue>urn:example.org:entitlements:ABCD</saml:AttributeValue>
-                    <saml:AttributeValue>urn:example.org:entitlements:1234</saml:AttributeValue>
-                </saml:Attribute>
+	            <saml:Attribute Name="urn:example.org:entitlements" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+	                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"  xsi:type="xs:boolean">32</saml:AttributeValue>
+	                <saml:AttributeValue>urn:example.org:entitlements:1234</saml:AttributeValue>
+	            </saml:Attribute>
             </mdattr:EntityAttributes>
         </Extensions>
 
diff --git a/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/saml/impl/AbstractEntityAttributeRuleParser.java b/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/saml/impl/AbstractEntityAttributeRuleParser.java
index bb03a02..7799388 100644
--- a/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/saml/impl/AbstractEntityAttributeRuleParser.java
+++ b/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/saml/impl/AbstractEntityAttributeRuleParser.java
@@ -27,8 +27,7 @@ import org.springframework.beans.factory.xml.ParserContext;
 import org.w3c.dom.Element;
 
 /**
- * Base definition for all EntityAttribute Parsers.<br/>
- * the common attributes are 'attributeName' and 'attributeNameFormat'.
+ * Base definition for all EntityAttribute Parsers.
  */
 public abstract class AbstractEntityAttributeRuleParser extends BasePolicyRuleParser {
 
@@ -43,5 +42,10 @@ public abstract class AbstractEntityAttributeRuleParser extends BasePolicyRulePa
             builder.addPropertyValue("nameFormat",
                     StringSupport.trimOrNull(element.getAttributeNS(null, "attributeNameFormat")));
         }
+        
+        if (element.hasAttributeNS(null, "ignoreUnmappedEntityAttributes")) {
+            builder.addPropertyValue("ignoreUnmappedEntityAttributes",
+                    StringSupport.trimOrNull(element.getAttributeNS(null, "ignoreUnmappedEntityAttributes")));
+        }
     }
 }
\ No newline at end of file
diff --git a/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/saml/AttributeRequesterEntityAttributeExactRuleParserTest.java b/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/saml/AttributeRequesterEntityAttributeExactRuleParserTest.java
index 2a6d1c9..6bf4453 100644
--- a/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/saml/AttributeRequesterEntityAttributeExactRuleParserTest.java
+++ b/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/saml/AttributeRequesterEntityAttributeExactRuleParserTest.java
@@ -17,7 +17,7 @@
 
 package net.shibboleth.idp.attribute.filter.spring.saml;
 
-import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.*;
 
 import org.testng.annotations.Test;
 
@@ -37,5 +37,7 @@ public class AttributeRequesterEntityAttributeExactRuleParserTest extends BaseAt
 
         assertEquals(rule.getValue(), "urn:example.org:policy:ABCD1234");
         assertEquals(rule.getAttributeName(), "urn:example.org:policy");
+        assertTrue(rule.getIgnoreUnmappedEntityAttributes());
     }
-}
+
+}
\ No newline at end of file
diff --git a/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/saml/AttributeRequesterEntityAttributeRegexRuleParserTest.java b/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/saml/AttributeRequesterEntityAttributeRegexRuleParserTest.java
index 40921c9..79838fc 100644
--- a/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/saml/AttributeRequesterEntityAttributeRegexRuleParserTest.java
+++ b/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/saml/AttributeRequesterEntityAttributeRegexRuleParserTest.java
@@ -17,7 +17,7 @@
 
 package net.shibboleth.idp.attribute.filter.spring.saml;
 
-import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.*;
 
 import org.testng.annotations.Test;
 
@@ -35,5 +35,7 @@ public class AttributeRequesterEntityAttributeRegexRuleParserTest extends  BaseA
         final AttributeRequesterEntityAttributeRegexPolicyRule rule = (AttributeRequesterEntityAttributeRegexPolicyRule) getPolicyRule("requesterEARegex2.xml");
         assertEquals(rule.getValueRegex().pattern(), "^urn:example\\.org:policy:[^:]*$");
         assertEquals(rule.getAttributeName(), "urn:example.org:policy");
+        assertFalse(rule.getIgnoreUnmappedEntityAttributes());
     }
-}
+
+}
\ No newline at end of file
diff --git a/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/policyrule/requesterEA2.xml b/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/policyrule/requesterEA2.xml
index 031d96b..1399c9c 100644
--- a/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/policyrule/requesterEA2.xml
+++ b/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/policyrule/requesterEA2.xml
@@ -3,5 +3,6 @@
         xsi:schemaLocation="urn:mace:shibboleth:2.0:afp http://shibboleth.net/schema/idp/shibboleth-afp.xsd">
 	<PolicyRequirementRule xsi:type="EntityAttributeExactMatch"
                        attributeName="urn:example.org:policy"
-                       attributeValue="urn:example.org:policy:ABCD1234" />
+                       attributeValue="urn:example.org:policy:ABCD1234" 
+                       ignoreUnmappedEntityAttributes="true"/>
 </AttributeFilterPolicy>
diff --git a/idp-schema/src/main/resources/schema/shibboleth-afp.xsd b/idp-schema/src/main/resources/schema/shibboleth-afp.xsd
index dc202a7..8f824cb 100644
--- a/idp-schema/src/main/resources/schema/shibboleth-afp.xsd
+++ b/idp-schema/src/main/resources/schema/shibboleth-afp.xsd
@@ -601,14 +601,21 @@
                         <documentation>The name of the entity attribute to match.</documentation>
                     </annotation>
                 </attribute>
-                <attribute name="attributeNameFormat" type="string" use="optional">
+                <attribute name="attributeValue" type="string" use="required">
                     <annotation>
-                        <documentation>The name format of the entity attribute to match.</documentation>
+                        <documentation>The value of the entity attribute to match.</documentation>
                     </annotation>
                 </attribute>
-                <attribute name="attributeValue" type="string" use="required">
+                <attribute name="attributeNameFormat" type="string">
                     <annotation>
-                        <documentation>The value of the entity attribute to match.</documentation>
+                        <documentation>The NameFormat of the entity attribute to match.</documentation>
+                    </annotation>
+                </attribute>
+                <attribute name="ignoreUnmappedEntityAttributes" type="string">
+                    <annotation>
+                        <documentation>
+                            Boolean attribute, whether to examine only mapped/decoded attributes, default is false.
+                        </documentation>
                     </annotation>
                 </attribute>
             </extension>
@@ -635,15 +642,22 @@
                         <documentation>The name of the entity attribute to match.</documentation>
                     </annotation>
                 </attribute>
-                <attribute name="attributeNameFormat" type="string" use="optional">
+                <attribute name="attributeValueRegex" type="string" use="required">
+                    <annotation>
+                        <documentation>The regular expression that must match the value of the entity attribute to
+                            match.</documentation>
+                    </annotation>
+                </attribute>
+                <attribute name="attributeNameFormat" type="string">
                     <annotation>
                         <documentation>The name format of the entity attribute to match.</documentation>
                     </annotation>
                 </attribute>
-                <attribute name="attributeValueRegex" type="string" use="required">
+                <attribute name="ignoreUnmappedEntityAttributes" type="string">
                     <annotation>
-                        <documentation>The regular expression that must match the value of the entity attribute to
-                            match.</documentation>
+                        <documentation>
+                            Boolean attribute, whether to examine only mapped/decoded attributes, default is false.
+                        </documentation>
                     </annotation>
                 </attribute>
             </extension>

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


More information about the commits mailing list