[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