[java-metadata-aggregator] 02/02: MDA-56 - add stage to add entity attribute values
Ian Young
ian at iay.org.uk
Wed Jun 28 11:55:04 EDT 2017
This is an automated email from the git hooks/post-receive script.
iay pushed a commit to branch master
in repository java-metadata-aggregator.
View the commit online:
http://git.shibboleth.net/view/?p=java-metadata-aggregator.git;a=commit;h=32540df0c35c4cffd43a8cabc4981ffdfb413d22
commit 32540df0c35c4cffd43a8cabc4981ffdfb413d22
Author: Ian Young <ian at iay.org.uk>
AuthorDate: Wed Jun 28 16:54:51 2017 +0100
MDA-56 - add stage to add entity attribute values
---
.../metadata/dom/saml/AttributeElementMaker.java | 63 +++++
.../metadata/dom/saml/AttributeElementMatcher.java | 63 +++++
.../dom/saml/AttributeValueElementMaker.java | 56 +++++
.../dom/saml/AttributeValueElementMatcher.java | 57 +++++
.../shibboleth/metadata/dom/saml/SAMLSupport.java | 15 ++
.../saml/mdattr/EntityAttributeAddingStage.java | 264 +++++++++++++++++++++
.../metadata/dom/saml/mdattr/MDAttrSupport.java | 17 ++
.../dom/saml/AttributeElementMakerTest.java | 49 ++++
.../dom/saml/AttributeElementMatcherTest.java | 56 +++++
.../dom/saml/AttributeValueElementMakerTest.java | 35 +++
.../dom/saml/AttributeValueElementMatcherTest.java | 50 ++++
.../mdattr/EntityAttributeAddingStageTest.java | 165 +++++++++++++
.../mdattr/EntityAttributeAddingStage-added1.xml | 53 +++++
.../mdattr/EntityAttributeAddingStage-added2.xml | 65 +++++
.../mdattr/EntityAttributeAddingStage-added3.xml | 65 +++++
.../EntityAttributeAddingStage-extensions.xml | 52 ++++
.../EntityAttributeAddingStage-noExtensions.xml | 45 ++++
.../mdattr/EntityAttributeAddingStage-some.xml | 60 +++++
18 files changed, 1230 insertions(+)
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/AttributeElementMaker.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/AttributeElementMaker.java
new file mode 100644
index 0000000..0371db3
--- /dev/null
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/AttributeElementMaker.java
@@ -0,0 +1,63 @@
+/*
+ * 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 net.shibboleth.metadata.dom.saml;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.w3c.dom.Element;
+
+import net.shibboleth.metadata.dom.Container;
+import net.shibboleth.metadata.dom.ElementMaker;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * A class for constructing SAML <code>Attribute</code> elements
+ * for use with the {@link Container} system.
+ */
+ at ThreadSafe
+public class AttributeElementMaker extends ElementMaker {
+
+ /** Value for the <code>Name</code> XML attribute. */
+ @Nonnull
+ private final String attributeName;
+
+ /** Value for the <code>NameFormat</code> XML attribute. */
+ @Nonnull
+ private final String attributeNameFormat;
+
+ /**
+ * Constructor.
+ *
+ * @param name value for the <code>Name</code> XML attribute
+ * @param nameFormat value for the <code>NameFormat</code> XML attribute
+ */
+ public AttributeElementMaker(@Nonnull final String name, @Nonnull final String nameFormat) {
+ super(SAMLSupport.ATTRIBUTE_NAME);
+ attributeName = Constraint.isNotNull(name, "attribute name must not be null");
+ attributeNameFormat = Constraint.isNotNull(nameFormat, "attribute name format must not be null");
+ }
+
+ @Override
+ public Element apply(@Nonnull final Container container) {
+ final Element newElement = super.apply(container);
+ newElement.setAttributeNS(null, "Name", attributeName);
+ newElement.setAttributeNS(null, "NameFormat", attributeNameFormat);
+ return newElement;
+ }
+}
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/AttributeElementMatcher.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/AttributeElementMatcher.java
new file mode 100644
index 0000000..4a7345f
--- /dev/null
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/AttributeElementMatcher.java
@@ -0,0 +1,63 @@
+/*
+ * 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 net.shibboleth.metadata.dom.saml;
+
+import javax.annotation.Nonnull;
+
+import org.w3c.dom.Element;
+
+import net.shibboleth.metadata.dom.ElementMatcher;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Match {@link com.google.common.base.Predicate} for SAML <code>Attribute</code> elements with specific
+ * <code>Name</code> and <code>NameFormat</code> attributes,
+ * for use with the {@link net.shibboleth.metadata.dom.Container} system.
+ */
+public class AttributeElementMatcher extends ElementMatcher {
+
+ /** <code>NameFormat</code> attribute value to match. */
+ @Nonnull private final String matchFormat;
+
+ /** <code>Name</code> attribute value to match. */
+ @Nonnull private final String matchName;
+
+ /**
+ * Constructor.
+ *
+ * @param name <code>Name</code> attribute value to match
+ * @param format <code>NameFormat</code> attribute value to match
+ */
+ public AttributeElementMatcher(@Nonnull final String name, @Nonnull final String format) {
+ super(SAMLSupport.ATTRIBUTE_NAME);
+ matchName = Constraint.isNotNull(name, "attribute name must not be null");
+ matchFormat = Constraint.isNotNull(format, "attribute name format must not be null");
+ }
+
+ @Override
+ public boolean apply(@Nonnull final Element element) {
+ // check for element name
+ if (!super.apply(element)) {
+ return false;
+ }
+
+ // now check attributes
+ return matchFormat.equals(SAMLSupport.extractAttributeNameFormat(element)) &&
+ matchName.equals(element.getAttribute("Name"));
+ }
+}
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/AttributeValueElementMaker.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/AttributeValueElementMaker.java
new file mode 100644
index 0000000..9439f26
--- /dev/null
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/AttributeValueElementMaker.java
@@ -0,0 +1,56 @@
+/*
+ * 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 net.shibboleth.metadata.dom.saml;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.w3c.dom.Element;
+
+import net.shibboleth.metadata.dom.Container;
+import net.shibboleth.metadata.dom.ElementMaker;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * A class for constructing SAML <code>AttributeValue</code> elements
+ * for use with the {@link Container} system.
+ */
+ at ThreadSafe
+public class AttributeValueElementMaker extends ElementMaker {
+
+ /** Value for the attribute. */
+ @Nonnull
+ private final String attributeValue;
+
+ /**
+ * Constructor.
+ *
+ * @param value value for the attribute
+ */
+ public AttributeValueElementMaker(@Nonnull final String value) {
+ super(SAMLSupport.ATTRIBUTE_VALUE_NAME);
+ attributeValue = Constraint.isNotNull(value, "attribute value must not be null");
+ }
+
+ @Override
+ public Element apply(@Nonnull final Container container) {
+ final Element newElement = super.apply(container);
+ newElement.setTextContent(attributeValue);
+ return newElement;
+ }
+}
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/AttributeValueElementMatcher.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/AttributeValueElementMatcher.java
new file mode 100644
index 0000000..386cd84
--- /dev/null
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/AttributeValueElementMatcher.java
@@ -0,0 +1,57 @@
+/*
+ * 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 net.shibboleth.metadata.dom.saml;
+
+import javax.annotation.Nonnull;
+
+import org.w3c.dom.Element;
+
+import net.shibboleth.metadata.dom.ElementMatcher;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Match {@link com.google.common.base.Predicate} for SAML <code>AttributeValue</code> elements with specific
+ * text values,
+ * for use with the {@link net.shibboleth.metadata.dom.Container} system.
+ */
+public class AttributeValueElementMatcher extends ElementMatcher {
+
+ /** <code>Attribute</code> value to match. */
+ @Nonnull private final String matchValue;
+
+ /**
+ * Constructor.
+ *
+ * @param value <code>Attribute</code> value to match
+ */
+ public AttributeValueElementMatcher(@Nonnull final String value) {
+ super(SAMLSupport.ATTRIBUTE_VALUE_NAME);
+ matchValue = Constraint.isNotNull(value, "attribute value must not be null");
+ }
+
+ @Override
+ public boolean apply(@Nonnull final Element element) {
+ // check for element name
+ if (!super.apply(element)) {
+ return false;
+ }
+
+ // now check attribute value
+ return matchValue.equals(element.getTextContent());
+ }
+}
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/SAMLSupport.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/SAMLSupport.java
index 7a5a7ae..fe09aa0 100644
--- a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/SAMLSupport.java
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/SAMLSupport.java
@@ -24,6 +24,13 @@ import javax.xml.namespace.QName;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+
+import net.shibboleth.metadata.dom.Container;
+import net.shibboleth.metadata.dom.ElementMaker;
+import net.shibboleth.metadata.dom.ElementMatcher;
+
/** Helper class for dealing with SAML documents. */
@ThreadSafe
public final class SAMLSupport {
@@ -43,6 +50,14 @@ public final class SAMLSupport {
/** saml:AttributeValue element. */
public static final QName ATTRIBUTE_VALUE_NAME = new QName(SAML_NS, "AttributeValue", SAML_PREFIX);
+ /** Matcher for the <code>Extensions</code> element, for use with the {@link Container} system. */
+ public static final Predicate<Element> EXTENSIONS_MATCHER =
+ new ElementMatcher(SAMLMetadataSupport.EXTENSIONS_NAME);
+
+ /** Maker for the <code>Extensions</code> element, for use with the {@link Container} system. */
+ public static final Function<Container, Element> EXTENSIONS_MAKER =
+ new ElementMaker(SAMLMetadataSupport.EXTENSIONS_NAME);
+
/** Constructor. */
private SAMLSupport() {
}
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage.java
new file mode 100644
index 0000000..5812383
--- /dev/null
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage.java
@@ -0,0 +1,264 @@
+/*
+ * 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 net.shibboleth.metadata.dom.saml.mdattr;
+
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.metadata.Item;
+import net.shibboleth.metadata.dom.Container;
+import net.shibboleth.metadata.dom.saml.AttributeElementMaker;
+import net.shibboleth.metadata.dom.saml.AttributeElementMatcher;
+import net.shibboleth.metadata.dom.saml.AttributeValueElementMaker;
+import net.shibboleth.metadata.dom.saml.AttributeValueElementMatcher;
+import net.shibboleth.metadata.dom.saml.SAMLMetadataSupport;
+import net.shibboleth.metadata.dom.saml.SAMLSupport;
+import net.shibboleth.metadata.pipeline.BaseStage;
+import net.shibboleth.metadata.pipeline.StageProcessingException;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+
+/**
+ * A stage which adds entity attribute values to entity definitions.
+ */
+public class EntityAttributeAddingStage extends BaseStage<Element> {
+
+ /** Class logger. */
+ private final Logger log = LoggerFactory.getLogger(EntityAttributeAddingStage.class);
+
+ /**
+ * The <code>Name</code> of the attribute to be added.
+ *
+ * The default value is for an entity attribute specifying an entity category.
+ */
+ @Nonnull
+ private String attributeName = EntityCategorySupport.EC_CATEGORY_ATTR_NAME;
+
+ /**
+ * The <code>NameFormat</code> of the attribute to be added.
+ *
+ * The default value is suitable for most entity attributes.
+ */
+ @Nonnull
+ private String attributeNameFormat = EntityCategorySupport.EC_ATTR_NAME_FORMAT;
+
+ /** The value of the attribute to be added. */
+ @Nonnull
+ private String attributeValue;
+
+ /**
+ * Whether we add <code>mdattr:EntityAttributes</code> as the first child
+ * of <code>md:Extensions</code> if not already present.
+ *
+ * Default: <code>false</code> (add as last child).
+ */
+ private boolean addingFirstChild;
+
+ /** {@link Predicate} used to match existing Attribute elements. */
+ @NonnullAfterInit
+ private Predicate<Element> attributeMatcher;
+
+ /** {@link Function} used to create new Attribute elements. */
+ @NonnullAfterInit
+ private Function<Container, Element> attributeMaker;
+
+ /** {@link Predicate} used to match existing AttributeValue elements. */
+ @NonnullAfterInit
+ private Predicate<Element> attributeValueMatcher;
+
+ /** {@link Function} used to create new AttributeValue elements. */
+ @NonnullAfterInit
+ private Function<Container, Element> attributeValueMaker;
+
+ /**
+ * Returns the attribute name.
+ *
+ * @return the attributeName
+ */
+ @Nonnull
+ public String getAttributeName() {
+ return attributeName;
+ }
+
+ /**
+ * Sets the attribute name.
+ *
+ * @param name the attributeName to set
+ */
+ public void setAttributeName(@Nonnull final String name) {
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ attributeName = Constraint.isNotNull(name, "attributeName must not be null");
+ }
+
+ /**
+ * Gets the attribute name format.
+ *
+ * @return the attributeNameFormat
+ */
+ @Nonnull
+ public String getAttributeNameFormat() {
+ return attributeNameFormat;
+ }
+
+ /**
+ * Sets the attribute name format.
+ *
+ * @param nameFormat the attributeNameFormat to set
+ */
+ public void setAttributeNameFormat(@Nonnull final String nameFormat) {
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ attributeNameFormat = Constraint.isNotNull(nameFormat, "attributeNameFormat must not be null");
+ }
+
+ /**
+ * Gets the attribute value.
+ *
+ * @return the attributeValue
+ */
+ @Nonnull
+ public String getAttributeValue() {
+ return attributeValue;
+ }
+
+ /**
+ * Sets the attribute value.
+ *
+ * @param value the attributeValue to set
+ */
+ public void setAttributeValue(@Nonnull final String value) {
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ attributeValue = Constraint.isNotNull(value, "attributeValue must not be null");
+ }
+
+ /**
+ * Get whether we are adding <code>mdattr:EntityAttributes</code> as the first child
+ * of <code>md:Extensions</code> if not already present.
+ *
+ * @return <code>true</code> if adding as the first child, <code>false</code> if the last
+ */
+ public boolean isAddingFirstChild() {
+ return addingFirstChild;
+ }
+
+ /**
+ * Sets whether to add <code>mdattr:EntityAttributes</code> as the first child
+ * of <code>md:Extensions</code> if not already present.
+ *
+ * @param addFirst <code>true</code> to add as the first child, <code>false</code> as the last
+ */
+ public void setAddingFirstChild(final boolean addFirst) {
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ addingFirstChild = addFirst;
+ }
+
+ /**
+ * Looks for the attribute value we want to add within the contents of a list of
+ * Attribute container elements.
+ *
+ * @param attributes {@link List} of Attribute {@link Container}s
+ * @return true iff the value appears somewhere in the list of containers
+ */
+ private boolean attributeValuePresent(@Nonnull final List<Container> attributes) {
+ for (final Container attribute : attributes) {
+ if (attribute.findChild(attributeValueMatcher) != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void doExecute(@Nonnull final Collection<Item<Element>> itemCollection) throws StageProcessingException {
+ for (final Item<Element> item : itemCollection) {
+ final Element entity = item.unwrap();
+ if (SAMLMetadataSupport.isEntityDescriptor(entity)) {
+ // Start from the entity
+ final Container entityContainer = new Container(entity);
+
+ // Dig down to <Extensions>
+ final Container extensionsContainer =
+ entityContainer.locateChild(SAMLSupport.EXTENSIONS_MATCHER,
+ SAMLSupport.EXTENSIONS_MAKER, Container.FIRST_CHILD);
+
+ // Dig down to <EntityAttributes>
+ final Container attributesContainer =
+ extensionsContainer.locateChild(MDAttrSupport.ENTITY_ATTRIBUTES_MATCHER,
+ MDAttrSupport.ENTITY_ATTRIBUTES_MAKER,
+ addingFirstChild ? Container.FIRST_CHILD : Container.LAST_CHILD);
+
+ // Collect all matching <Attribute> containers
+ final List<Container> attributes =
+ attributesContainer.findChildren(attributeMatcher);
+
+ // If any of the existing attribute values match our value, we're done
+ if (attributeValuePresent(attributes)) {
+ log.debug("attribute value '{}' already present", attributeValue);
+ continue;
+ }
+
+ // If not already present, re-locate an <Attribute> and add it in there.
+ final Container attribute =
+ attributesContainer.locateChild(attributeMatcher, attributeMaker, Container.LAST_CHILD);
+ attribute.addChild(attributeValueMaker, Container.LAST_CHILD);
+ }
+ }
+ }
+
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (attributeValue == null) {
+ throw new ComponentInitializationException("attributeValue property must be supplied");
+ }
+
+ attributeMatcher = new AttributeElementMatcher(attributeName, attributeNameFormat);
+ attributeMaker = new AttributeElementMaker(attributeName, attributeNameFormat);
+ attributeValueMatcher = new AttributeValueElementMatcher(attributeValue);
+ attributeValueMaker = new AttributeValueElementMaker(attributeValue);
+ }
+
+ @Override
+ protected void doDestroy() {
+ attributeMatcher = null;
+ attributeMaker = null;
+ attributeValueMatcher = null;
+ attributeValueMaker = null;
+ super.doDestroy();
+ }
+
+}
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/mdattr/MDAttrSupport.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/mdattr/MDAttrSupport.java
index 97c38b5..93aec6d 100644
--- a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/mdattr/MDAttrSupport.java
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/saml/mdattr/MDAttrSupport.java
@@ -20,6 +20,15 @@ package net.shibboleth.metadata.dom.saml.mdattr;
import javax.annotation.concurrent.ThreadSafe;
import javax.xml.namespace.QName;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+
+import net.shibboleth.metadata.dom.Container;
+import net.shibboleth.metadata.dom.ElementMaker;
+import net.shibboleth.metadata.dom.ElementMatcher;
+
/** Helper class for dealing with MDAttr metadata. */
@ThreadSafe
public final class MDAttrSupport {
@@ -33,6 +42,14 @@ public final class MDAttrSupport {
/** mdattr:EntityAttributes element. */
public static final QName ENTITY_ATTRIBUTES_NAME = new QName(MDATTR_NS, "EntityAttributes", MDATTR_PREFIX);
+ /** Matcher for the <code>EntityAttributes</code> element, for use with the {@link Container} system. */
+ public static final Predicate<Element> ENTITY_ATTRIBUTES_MATCHER =
+ new ElementMatcher(ENTITY_ATTRIBUTES_NAME);
+
+ /** Maker for the <code>EntityAttributes</code> element, for use with the {@link Container} system. */
+ public static final Function<Container, Element> ENTITY_ATTRIBUTES_MAKER =
+ new ElementMaker(ENTITY_ATTRIBUTES_NAME);
+
/** Constructor. */
private MDAttrSupport() {
}
diff --git a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/AttributeElementMakerTest.java b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/AttributeElementMakerTest.java
new file mode 100644
index 0000000..0de81a0
--- /dev/null
+++ b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/AttributeElementMakerTest.java
@@ -0,0 +1,49 @@
+
+package net.shibboleth.metadata.dom.saml;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import com.google.common.base.Function;
+
+import net.shibboleth.metadata.dom.BaseDOMTest;
+import net.shibboleth.metadata.dom.Container;
+
+public class AttributeElementMakerTest extends BaseDOMTest {
+
+ private final Document doc;
+
+ protected AttributeElementMakerTest() throws Exception {
+ super(AttributeElementMaker.class);
+ setUp();
+ doc = getParserPool().newDocument();
+ }
+
+ @Test
+ public void apply() {
+ final Function<Container, Element> maker = new AttributeElementMaker("name", "nameFormat");
+ final Element root = doc.createElementNS("ns", "root");
+ final Container rootContainer = new Container(root);
+ final Element newElement = maker.apply(rootContainer);
+ Assert.assertNotNull(newElement);
+ Assert.assertEquals(newElement.getLocalName(), "Attribute");
+ Assert.assertEquals(newElement.getNamespaceURI(), SAMLSupport.SAML_NS);
+ Assert.assertEquals(newElement.getAttribute("Name"), "name");
+ Assert.assertEquals(newElement.getAttribute("NameFormat"), "nameFormat");
+
+ // Check that the attributes have been created in a namespace-aware way
+ // to avoid problems with schema checkers.
+ final NamedNodeMap attrs = newElement.getAttributes();
+ for (int i = 0; i<attrs.getLength(); i++) {
+ final Node node = attrs.item(i);
+ Assert.assertNotNull(node.getLocalName());
+ }
+
+ // check that the prefix is declared properly
+ Assert.assertEquals(newElement.lookupNamespaceURI(SAMLSupport.SAML_PREFIX), SAMLSupport.SAML_NS);
+ }
+}
diff --git a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/AttributeElementMatcherTest.java b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/AttributeElementMatcherTest.java
new file mode 100644
index 0000000..55af283
--- /dev/null
+++ b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/AttributeElementMatcherTest.java
@@ -0,0 +1,56 @@
+
+package net.shibboleth.metadata.dom.saml;
+
+import net.shibboleth.metadata.dom.BaseDOMTest;
+import net.shibboleth.utilities.java.support.xml.ElementSupport;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Predicate;
+
+public class AttributeElementMatcherTest extends BaseDOMTest {
+
+ private final Document doc;
+ private Element attr;
+
+ protected AttributeElementMatcherTest() throws Exception {
+ super(AttributeElementMatcher.class);
+ setUp();
+ doc = getParserPool().newDocument();
+ }
+
+ @BeforeTest
+ private void beforeTest() throws Exception {
+ attr = ElementSupport.constructElement(doc, SAMLSupport.ATTRIBUTE_NAME);
+ }
+
+ @Test
+ public void matchNormal() throws Exception {
+ final Predicate<Element> matcher1 = new AttributeElementMatcher("name", "name-format");
+ attr.setAttribute("Name", "name");
+ attr.setAttribute("NameFormat", "name-format");
+ Assert.assertTrue(matcher1.apply(attr));
+
+ final Predicate<Element> matcher2 = new AttributeElementMatcher("name2", "name-format");
+ Assert.assertFalse(matcher2.apply(attr));
+
+ final Predicate<Element> matcher3 = new AttributeElementMatcher("name", "name-format2");
+ Assert.assertFalse(matcher3.apply(attr));
+ }
+
+ @Test
+ public void matchDefaultFormat() throws Exception {
+ attr.setAttribute("Name", "name");
+
+ final Predicate<Element> matcher1 = new AttributeElementMatcher("name", SAMLSupport.ATTRNAME_FORMAT_UNSPECIFIED);
+ Assert.assertTrue(matcher1.apply(attr));
+
+ final Predicate<Element> matcher2 = new AttributeElementMatcher("name2", SAMLSupport.ATTRNAME_FORMAT_UNSPECIFIED);
+ Assert.assertFalse(matcher2.apply(attr));
+ }
+
+}
diff --git a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/AttributeValueElementMakerTest.java b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/AttributeValueElementMakerTest.java
new file mode 100644
index 0000000..b9f3ad0
--- /dev/null
+++ b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/AttributeValueElementMakerTest.java
@@ -0,0 +1,35 @@
+
+package net.shibboleth.metadata.dom.saml;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Function;
+
+import net.shibboleth.metadata.dom.BaseDOMTest;
+import net.shibboleth.metadata.dom.Container;
+
+public class AttributeValueElementMakerTest extends BaseDOMTest {
+
+ private final Document doc;
+
+ protected AttributeValueElementMakerTest() throws Exception {
+ super(AttributeValueElementMaker.class);
+ setUp();
+ doc = getParserPool().newDocument();
+ }
+
+ @Test
+ public void apply() {
+ final Function<Container, Element> maker = new AttributeValueElementMaker("value text");
+ final Element root = doc.createElementNS("ns", "root");
+ final Container rootContainer = new Container(root);
+ final Element newElement = maker.apply(rootContainer);
+ Assert.assertNotNull(newElement);
+ Assert.assertEquals(newElement.getLocalName(), "AttributeValue");
+ Assert.assertEquals(newElement.getNamespaceURI(), SAMLSupport.SAML_NS);
+ Assert.assertEquals(newElement.getTextContent(), "value text");
+ }
+}
diff --git a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/AttributeValueElementMatcherTest.java b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/AttributeValueElementMatcherTest.java
new file mode 100644
index 0000000..9dfd16c
--- /dev/null
+++ b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/AttributeValueElementMatcherTest.java
@@ -0,0 +1,50 @@
+
+package net.shibboleth.metadata.dom.saml;
+
+import net.shibboleth.metadata.dom.BaseDOMTest;
+import net.shibboleth.utilities.java.support.xml.ElementSupport;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Predicate;
+
+public class AttributeValueElementMatcherTest extends BaseDOMTest {
+
+ private final Document doc;
+ private Element value;
+
+ protected AttributeValueElementMatcherTest() throws Exception {
+ super(AttributeValueElementMatcher.class);
+ setUp();
+ doc = getParserPool().newDocument();
+ }
+
+ @BeforeTest
+ private void beforeTest() throws Exception {
+ value = ElementSupport.constructElement(doc, SAMLSupport.ATTRIBUTE_VALUE_NAME);
+ }
+
+ @Test
+ public void apply() throws Exception {
+ final Predicate<Element> matcher1 = new AttributeValueElementMatcher("value");
+
+ value.setTextContent("value");
+ Assert.assertTrue(matcher1.apply(value));
+
+ value.setTextContent("other");
+ Assert.assertFalse(matcher1.apply(value));
+
+ final Predicate<Element> matcher2 = new AttributeValueElementMatcher("other");
+
+ value.setTextContent("value");
+ Assert.assertFalse(matcher2.apply(value));
+
+ value.setTextContent("other");
+ Assert.assertTrue(matcher2.apply(value));
+ }
+
+}
diff --git a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStageTest.java b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStageTest.java
new file mode 100644
index 0000000..03fced0
--- /dev/null
+++ b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStageTest.java
@@ -0,0 +1,165 @@
+
+package net.shibboleth.metadata.dom.saml.mdattr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.shibboleth.metadata.Item;
+import net.shibboleth.metadata.dom.BaseDOMTest;
+import net.shibboleth.metadata.dom.DOMElementItem;
+import net.shibboleth.metadata.pipeline.Pipeline;
+import net.shibboleth.metadata.pipeline.SimplePipeline;
+import net.shibboleth.metadata.pipeline.Stage;
+
+import org.testng.annotations.Test;
+import org.w3c.dom.Element;
+
+public class EntityAttributeAddingStageTest extends BaseDOMTest {
+
+ protected EntityAttributeAddingStageTest() throws Exception {
+ super(EntityAttributeAddingStage.class);
+ setUp();
+ }
+
+ private List<Item<Element>> makeItems(final String inputFile) throws Exception {
+ final Element startElement = readXMLData(inputFile);
+ final List<Item<Element>> items = new ArrayList<>();
+ items.add(new DOMElementItem(startElement));
+ return items;
+ }
+
+ private Stage<Element> makeStage(final String value) throws Exception {
+ final EntityAttributeAddingStage stage = new EntityAttributeAddingStage();
+ stage.setId("test");
+ stage.setAttributeValue(value);
+ stage.initialize();
+ return stage;
+ }
+
+ private Stage<Element> makeStage(final String value, final String name) throws Exception {
+ final EntityAttributeAddingStage stage = new EntityAttributeAddingStage();
+ stage.setId("test");
+ stage.setAttributeValue(value);
+ stage.setAttributeName(name);
+ stage.initialize();
+ return stage;
+ }
+
+ private Stage<Element> makeStage(final String value, final String name, final String nameFormat) throws Exception {
+ final EntityAttributeAddingStage stage = new EntityAttributeAddingStage();
+ stage.setId("test");
+ stage.setAttributeValue(value);
+ stage.setAttributeName(name);
+ stage.setAttributeNameFormat(nameFormat);
+ stage.initialize();
+ return stage;
+ }
+
+ private Pipeline<Element> makePipeline(final List<Stage<Element>> stages) throws Exception {
+ final SimplePipeline<Element> pipeline = new SimplePipeline<>();
+ pipeline.setId("test");
+ pipeline.setStages(stages);
+ pipeline.initialize();
+ return pipeline;
+ }
+
+ /*
+ * Checks creation of Extensions if none is there already.
+ */
+ @Test
+ public void addNoExtensions() throws Exception {
+ final List<Item<Element>> itemCollection = makeItems("noExtensions.xml");
+ final List<Stage<Element>> stages = new ArrayList<>();
+ stages.add(makeStage("http://www.geant.net/uri/dataprotection-code-of-conduct/v1"));
+ stages.add(makeStage("another"));
+ // add the same attributes twice, just to make sure duplicates are ignored
+ stages.add(makeStage("http://www.geant.net/uri/dataprotection-code-of-conduct/v1"));
+ stages.add(makeStage("another"));
+ final Pipeline<Element> pipeline = makePipeline(stages);
+ pipeline.execute(itemCollection);
+ final Element result = itemCollection.get(0).unwrap();
+ final Element expected = readXMLData("added1.xml");
+ assertXMLEqual(expected, result);
+ }
+
+ /*
+ * Input has Extensions but no EntityAttributes.
+ */
+ @Test
+ public void addToExtensions() throws Exception {
+ final List<Item<Element>> itemCollection = makeItems("extensions.xml");
+ final List<Stage<Element>> stages = new ArrayList<>();
+ stages.add(makeStage("http://www.geant.net/uri/dataprotection-code-of-conduct/v1"));
+ stages.add(makeStage("http://example.org/category2"));
+ stages.add(makeStage("http://example.org/category2support", "http://macedir.org/entity-category-support"));
+ stages.add(makeStage("http://www.geant.net/uri/dataprotection-code-of-conduct/v1", "http://macedir.org/entity-category-support"));
+ stages.add(makeStage("anotherValue", "anotherAttributeName", "anotherNameFormat"));
+ final Pipeline<Element> pipeline = makePipeline(stages);
+ pipeline.execute(itemCollection);
+ final Element result = itemCollection.get(0).unwrap();
+ final Element expected = readXMLData("added2.xml");
+ assertXMLEqual(expected, result);
+ }
+
+ /*
+ * Input has Extensions but no EntityAttributes; adding at the front.
+ */
+ @Test
+ public void addToExtensionsFirst() throws Exception {
+ final List<Item<Element>> itemCollection = makeItems("extensions.xml");
+ final List<Stage<Element>> stages = new ArrayList<>();
+ final EntityAttributeAddingStage stage = new EntityAttributeAddingStage();
+ stage.setId("test");
+ stage.setAttributeValue("http://www.geant.net/uri/dataprotection-code-of-conduct/v1");
+ stage.setAddingFirstChild(true);
+ stage.initialize();
+ stages.add(stage);
+ stages.add(makeStage("http://example.org/category2"));
+ stages.add(makeStage("http://example.org/category2support", "http://macedir.org/entity-category-support"));
+ stages.add(makeStage("http://www.geant.net/uri/dataprotection-code-of-conduct/v1", "http://macedir.org/entity-category-support"));
+ stages.add(makeStage("anotherValue", "anotherAttributeName", "anotherNameFormat"));
+ final Pipeline<Element> pipeline = makePipeline(stages);
+ pipeline.execute(itemCollection);
+ final Element result = itemCollection.get(0).unwrap();
+ final Element expected = readXMLData("added3.xml");
+ assertXMLEqual(expected, result);
+ }
+
+ /*
+ * Check that adding something has no effect if it's already there.
+ */
+ @Test
+ public void addDuplicates() throws Exception {
+ final List<Item<Element>> itemCollection = makeItems("added2.xml");
+ final List<Stage<Element>> stages = new ArrayList<>();
+ stages.add(makeStage("http://www.geant.net/uri/dataprotection-code-of-conduct/v1"));
+ stages.add(makeStage("http://example.org/category2"));
+ stages.add(makeStage("http://example.org/category2support", "http://macedir.org/entity-category-support"));
+ stages.add(makeStage("http://www.geant.net/uri/dataprotection-code-of-conduct/v1", "http://macedir.org/entity-category-support"));
+ stages.add(makeStage("anotherValue", "anotherAttributeName", "anotherNameFormat"));
+ final Pipeline<Element> pipeline = makePipeline(stages);
+ pipeline.execute(itemCollection);
+ final Element result = itemCollection.get(0).unwrap();
+ final Element expected = readXMLData("added2.xml");
+ assertXMLEqual(expected, result);
+ }
+
+ /*
+ * Add some missing values to an existing EntityAttributes collection.
+ */
+ @Test
+ public void addToExisting() throws Exception {
+ final List<Item<Element>> itemCollection = makeItems("some.xml");
+ final List<Stage<Element>> stages = new ArrayList<>();
+ stages.add(makeStage("http://www.geant.net/uri/dataprotection-code-of-conduct/v1"));
+ stages.add(makeStage("http://example.org/category2"));
+ stages.add(makeStage("http://example.org/category2support", "http://macedir.org/entity-category-support"));
+ stages.add(makeStage("http://www.geant.net/uri/dataprotection-code-of-conduct/v1", "http://macedir.org/entity-category-support"));
+ stages.add(makeStage("anotherValue", "anotherAttributeName", "anotherNameFormat"));
+ final Pipeline<Element> pipeline = makePipeline(stages);
+ pipeline.execute(itemCollection);
+ final Element result = itemCollection.get(0).unwrap();
+ final Element expected = readXMLData("added2.xml");
+ assertXMLEqual(expected, result);
+ }
+}
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-added1.xml b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-added1.xml
new file mode 100644
index 0000000..df601f8
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-added1.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
+ xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi"
+ xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+ entityID="http://example.org/entity">
+ <md:Extensions xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
+ <mdattr:EntityAttributes>
+ <saml:Attribute Name="http://macedir.org/entity-category" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+ <saml:AttributeValue>http://www.geant.net/uri/dataprotection-code-of-conduct/v1</saml:AttributeValue>
+ <saml:AttributeValue>another</saml:AttributeValue>
+ </saml:Attribute>
+ </mdattr:EntityAttributes>
+ </md:Extensions>
+ <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+ <KeyDescriptor>
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>
+ MIIEfzCCA2egAwIBAgIQQSSnV5Mk/EXZxgrsbnU7ajANBgkqhkiG9w0BAQUFADA2
+ MQswCQYDVQQGEwJOTDEPMA0GA1UEChMGVEVSRU5BMRYwFAYDVQQDEw1URVJFTkEg
+ U1NMIENBMB4XDTEzMDMwNjAwMDAwMFoXDTE2MDMwNTIzNTk1OVowRzEhMB8GA1UE
+ CxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMSIwIAYDVQQDExlzcDIwMTMtaGEt
+ YWRmczIuZ2VhbnQubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+ qzeOIPfgr/SneQUtjqDRGLbG4YKxmo2zfAIF+wjaYXnDTq/yWt852nImchvCgjWu
+ eXnHJ6oZZGdaN4kMgPh1oxqJ4UE6dXY9HEoru/Awp2P/CGiYOQ4Q5u5+AyFznGRx
+ y2uNYrD85C6uTIgiKytH6Lai8f6dFxHKO/u8o+kbrl2Z1CkAf6wePu2H5a44tGnF
+ SP/s7lW4ScSthNz1OF8BoPFKpWUmJk/584rZZjR+fqB9fQBwxRoRBCFZTduXP0G9
+ 861BS6Nt6Dfuli9jdCBC0L8ai4fH43a94EQVmMfcjImrkYBg7v6lVS4IY75ONiOt
+ sObamvbT+yNEuqw0jBjmKwIDAQABo4IBdjCCAXIwHwYDVR0jBBgwFoAUDL2TaAzz
+ 3qujSWsrN1dH6pDjue0wHQYDVR0OBBYEFBQXWZZ8RdkPMtC2o0RF+T+gH7VsMA4G
+ A1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMB
+ BggrBgEFBQcDAjAiBgNVHSAEGzAZMA0GCysGAQQBsjEBAgIdMAgGBmeBDAECATA6
+ BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3JsLnRjcy50ZXJlbmEub3JnL1RFUkVO
+ QVNTTENBLmNybDBtBggrBgEFBQcBAQRhMF8wNQYIKwYBBQUHMAKGKWh0dHA6Ly9j
+ cnQudGNzLnRlcmVuYS5vcmcvVEVSRU5BU1NMQ0EuY3J0MCYGCCsGAQUFBzABhhpo
+ dHRwOi8vb2NzcC50Y3MudGVyZW5hLm9yZzAkBgNVHREEHTAbghlzcDIwMTMtaGEt
+ YWRmczIuZ2VhbnQubmV0MA0GCSqGSIb3DQEBBQUAA4IBAQCRJqxXjeInIqlPLH5+
+ iFqL92oLPKiJDqlDWpzR9P0xF7IS4oBApWZ3f2Rgcx1dtwUf7TakMpAuxIxzMjfP
+ xaik/AuInMjxKU9AJS/lRlLogT3YXZ2aAGZbt2P/hYn4mdn5ryB9WF/w7mnyrpBb
+ o7vllsYXYmRX/c0MEPYfSMoKxomRAY2ViIh83m4sYk8+Nkm+3I9t0O3wBqaQYqpc
+ Mg48AG2JwLeuKUIC+faPOckeMXdrknL0Ra7Vb+eRoEKCqw3L9ka5/rloFNEN+hMZ
+ zubyfY3feeIV+kq3YNmjCwhDqckv5kXAdnkFprjB3X/xNfML2Jum02hKkff4AYfP
+ 2IOi
+ </ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+ Location="https://example.org/whatever" index="0" isDefault="true"/>
+ </SPSSODescriptor>
+</EntityDescriptor>
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-added2.xml b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-added2.xml
new file mode 100644
index 0000000..dab899a
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-added2.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
+ xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi"
+ xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+ entityID="http://example.org/entity">
+ <Extensions>
+ <mdrpi:RegistrationInfo registrationAuthority="http://ukfederation.org.uk"
+ registrationInstant="2014-03-18T15:23:31Z">
+ <mdrpi:RegistrationPolicy xml:lang="en"
+ >http://ukfederation.org.uk/doc/mdrps-20130902</mdrpi:RegistrationPolicy>
+ </mdrpi:RegistrationInfo>
+ <mdattr:EntityAttributes>
+ <saml:Attribute Name="http://macedir.org/entity-category" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+ <saml:AttributeValue>http://www.geant.net/uri/dataprotection-code-of-conduct/v1</saml:AttributeValue>
+ <saml:AttributeValue>http://example.org/category2</saml:AttributeValue>
+ </saml:Attribute>
+ <saml:Attribute Name="http://macedir.org/entity-category-support" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+ <saml:AttributeValue>http://example.org/category2support</saml:AttributeValue>
+ <saml:AttributeValue>http://www.geant.net/uri/dataprotection-code-of-conduct/v1</saml:AttributeValue>
+ </saml:Attribute>
+ <saml:Attribute Name="anotherAttributeName" NameFormat="anotherNameFormat">
+ <saml:AttributeValue>anotherValue</saml:AttributeValue>
+ </saml:Attribute>
+ </mdattr:EntityAttributes>
+ </Extensions>
+ <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+ <KeyDescriptor>
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>
+ MIIEfzCCA2egAwIBAgIQQSSnV5Mk/EXZxgrsbnU7ajANBgkqhkiG9w0BAQUFADA2
+ MQswCQYDVQQGEwJOTDEPMA0GA1UEChMGVEVSRU5BMRYwFAYDVQQDEw1URVJFTkEg
+ U1NMIENBMB4XDTEzMDMwNjAwMDAwMFoXDTE2MDMwNTIzNTk1OVowRzEhMB8GA1UE
+ CxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMSIwIAYDVQQDExlzcDIwMTMtaGEt
+ YWRmczIuZ2VhbnQubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+ qzeOIPfgr/SneQUtjqDRGLbG4YKxmo2zfAIF+wjaYXnDTq/yWt852nImchvCgjWu
+ eXnHJ6oZZGdaN4kMgPh1oxqJ4UE6dXY9HEoru/Awp2P/CGiYOQ4Q5u5+AyFznGRx
+ y2uNYrD85C6uTIgiKytH6Lai8f6dFxHKO/u8o+kbrl2Z1CkAf6wePu2H5a44tGnF
+ SP/s7lW4ScSthNz1OF8BoPFKpWUmJk/584rZZjR+fqB9fQBwxRoRBCFZTduXP0G9
+ 861BS6Nt6Dfuli9jdCBC0L8ai4fH43a94EQVmMfcjImrkYBg7v6lVS4IY75ONiOt
+ sObamvbT+yNEuqw0jBjmKwIDAQABo4IBdjCCAXIwHwYDVR0jBBgwFoAUDL2TaAzz
+ 3qujSWsrN1dH6pDjue0wHQYDVR0OBBYEFBQXWZZ8RdkPMtC2o0RF+T+gH7VsMA4G
+ A1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMB
+ BggrBgEFBQcDAjAiBgNVHSAEGzAZMA0GCysGAQQBsjEBAgIdMAgGBmeBDAECATA6
+ BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3JsLnRjcy50ZXJlbmEub3JnL1RFUkVO
+ QVNTTENBLmNybDBtBggrBgEFBQcBAQRhMF8wNQYIKwYBBQUHMAKGKWh0dHA6Ly9j
+ cnQudGNzLnRlcmVuYS5vcmcvVEVSRU5BU1NMQ0EuY3J0MCYGCCsGAQUFBzABhhpo
+ dHRwOi8vb2NzcC50Y3MudGVyZW5hLm9yZzAkBgNVHREEHTAbghlzcDIwMTMtaGEt
+ YWRmczIuZ2VhbnQubmV0MA0GCSqGSIb3DQEBBQUAA4IBAQCRJqxXjeInIqlPLH5+
+ iFqL92oLPKiJDqlDWpzR9P0xF7IS4oBApWZ3f2Rgcx1dtwUf7TakMpAuxIxzMjfP
+ xaik/AuInMjxKU9AJS/lRlLogT3YXZ2aAGZbt2P/hYn4mdn5ryB9WF/w7mnyrpBb
+ o7vllsYXYmRX/c0MEPYfSMoKxomRAY2ViIh83m4sYk8+Nkm+3I9t0O3wBqaQYqpc
+ Mg48AG2JwLeuKUIC+faPOckeMXdrknL0Ra7Vb+eRoEKCqw3L9ka5/rloFNEN+hMZ
+ zubyfY3feeIV+kq3YNmjCwhDqckv5kXAdnkFprjB3X/xNfML2Jum02hKkff4AYfP
+ 2IOi
+ </ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+ Location="https://example.org/whatever" index="0" isDefault="true"/>
+ </SPSSODescriptor>
+</EntityDescriptor>
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-added3.xml b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-added3.xml
new file mode 100644
index 0000000..7b1d4a7
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-added3.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
+ xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi"
+ xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+ entityID="http://example.org/entity">
+ <Extensions>
+ <mdattr:EntityAttributes>
+ <saml:Attribute Name="http://macedir.org/entity-category" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+ <saml:AttributeValue>http://www.geant.net/uri/dataprotection-code-of-conduct/v1</saml:AttributeValue>
+ <saml:AttributeValue>http://example.org/category2</saml:AttributeValue>
+ </saml:Attribute>
+ <saml:Attribute Name="http://macedir.org/entity-category-support" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+ <saml:AttributeValue>http://example.org/category2support</saml:AttributeValue>
+ <saml:AttributeValue>http://www.geant.net/uri/dataprotection-code-of-conduct/v1</saml:AttributeValue>
+ </saml:Attribute>
+ <saml:Attribute Name="anotherAttributeName" NameFormat="anotherNameFormat">
+ <saml:AttributeValue>anotherValue</saml:AttributeValue>
+ </saml:Attribute>
+ </mdattr:EntityAttributes>
+ <mdrpi:RegistrationInfo registrationAuthority="http://ukfederation.org.uk"
+ registrationInstant="2014-03-18T15:23:31Z">
+ <mdrpi:RegistrationPolicy xml:lang="en"
+ >http://ukfederation.org.uk/doc/mdrps-20130902</mdrpi:RegistrationPolicy>
+ </mdrpi:RegistrationInfo>
+ </Extensions>
+ <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+ <KeyDescriptor>
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>
+ MIIEfzCCA2egAwIBAgIQQSSnV5Mk/EXZxgrsbnU7ajANBgkqhkiG9w0BAQUFADA2
+ MQswCQYDVQQGEwJOTDEPMA0GA1UEChMGVEVSRU5BMRYwFAYDVQQDEw1URVJFTkEg
+ U1NMIENBMB4XDTEzMDMwNjAwMDAwMFoXDTE2MDMwNTIzNTk1OVowRzEhMB8GA1UE
+ CxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMSIwIAYDVQQDExlzcDIwMTMtaGEt
+ YWRmczIuZ2VhbnQubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+ qzeOIPfgr/SneQUtjqDRGLbG4YKxmo2zfAIF+wjaYXnDTq/yWt852nImchvCgjWu
+ eXnHJ6oZZGdaN4kMgPh1oxqJ4UE6dXY9HEoru/Awp2P/CGiYOQ4Q5u5+AyFznGRx
+ y2uNYrD85C6uTIgiKytH6Lai8f6dFxHKO/u8o+kbrl2Z1CkAf6wePu2H5a44tGnF
+ SP/s7lW4ScSthNz1OF8BoPFKpWUmJk/584rZZjR+fqB9fQBwxRoRBCFZTduXP0G9
+ 861BS6Nt6Dfuli9jdCBC0L8ai4fH43a94EQVmMfcjImrkYBg7v6lVS4IY75ONiOt
+ sObamvbT+yNEuqw0jBjmKwIDAQABo4IBdjCCAXIwHwYDVR0jBBgwFoAUDL2TaAzz
+ 3qujSWsrN1dH6pDjue0wHQYDVR0OBBYEFBQXWZZ8RdkPMtC2o0RF+T+gH7VsMA4G
+ A1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMB
+ BggrBgEFBQcDAjAiBgNVHSAEGzAZMA0GCysGAQQBsjEBAgIdMAgGBmeBDAECATA6
+ BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3JsLnRjcy50ZXJlbmEub3JnL1RFUkVO
+ QVNTTENBLmNybDBtBggrBgEFBQcBAQRhMF8wNQYIKwYBBQUHMAKGKWh0dHA6Ly9j
+ cnQudGNzLnRlcmVuYS5vcmcvVEVSRU5BU1NMQ0EuY3J0MCYGCCsGAQUFBzABhhpo
+ dHRwOi8vb2NzcC50Y3MudGVyZW5hLm9yZzAkBgNVHREEHTAbghlzcDIwMTMtaGEt
+ YWRmczIuZ2VhbnQubmV0MA0GCSqGSIb3DQEBBQUAA4IBAQCRJqxXjeInIqlPLH5+
+ iFqL92oLPKiJDqlDWpzR9P0xF7IS4oBApWZ3f2Rgcx1dtwUf7TakMpAuxIxzMjfP
+ xaik/AuInMjxKU9AJS/lRlLogT3YXZ2aAGZbt2P/hYn4mdn5ryB9WF/w7mnyrpBb
+ o7vllsYXYmRX/c0MEPYfSMoKxomRAY2ViIh83m4sYk8+Nkm+3I9t0O3wBqaQYqpc
+ Mg48AG2JwLeuKUIC+faPOckeMXdrknL0Ra7Vb+eRoEKCqw3L9ka5/rloFNEN+hMZ
+ zubyfY3feeIV+kq3YNmjCwhDqckv5kXAdnkFprjB3X/xNfML2Jum02hKkff4AYfP
+ 2IOi
+ </ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+ Location="https://example.org/whatever" index="0" isDefault="true"/>
+ </SPSSODescriptor>
+</EntityDescriptor>
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-extensions.xml b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-extensions.xml
new file mode 100644
index 0000000..599ef4d
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-extensions.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
+ xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi"
+ xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+ entityID="http://example.org/entity">
+ <Extensions>
+ <mdrpi:RegistrationInfo registrationAuthority="http://ukfederation.org.uk"
+ registrationInstant="2014-03-18T15:23:31Z">
+ <mdrpi:RegistrationPolicy xml:lang="en"
+ >http://ukfederation.org.uk/doc/mdrps-20130902</mdrpi:RegistrationPolicy>
+ </mdrpi:RegistrationInfo>
+ </Extensions>
+ <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+ <KeyDescriptor>
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>
+ MIIEfzCCA2egAwIBAgIQQSSnV5Mk/EXZxgrsbnU7ajANBgkqhkiG9w0BAQUFADA2
+ MQswCQYDVQQGEwJOTDEPMA0GA1UEChMGVEVSRU5BMRYwFAYDVQQDEw1URVJFTkEg
+ U1NMIENBMB4XDTEzMDMwNjAwMDAwMFoXDTE2MDMwNTIzNTk1OVowRzEhMB8GA1UE
+ CxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMSIwIAYDVQQDExlzcDIwMTMtaGEt
+ YWRmczIuZ2VhbnQubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+ qzeOIPfgr/SneQUtjqDRGLbG4YKxmo2zfAIF+wjaYXnDTq/yWt852nImchvCgjWu
+ eXnHJ6oZZGdaN4kMgPh1oxqJ4UE6dXY9HEoru/Awp2P/CGiYOQ4Q5u5+AyFznGRx
+ y2uNYrD85C6uTIgiKytH6Lai8f6dFxHKO/u8o+kbrl2Z1CkAf6wePu2H5a44tGnF
+ SP/s7lW4ScSthNz1OF8BoPFKpWUmJk/584rZZjR+fqB9fQBwxRoRBCFZTduXP0G9
+ 861BS6Nt6Dfuli9jdCBC0L8ai4fH43a94EQVmMfcjImrkYBg7v6lVS4IY75ONiOt
+ sObamvbT+yNEuqw0jBjmKwIDAQABo4IBdjCCAXIwHwYDVR0jBBgwFoAUDL2TaAzz
+ 3qujSWsrN1dH6pDjue0wHQYDVR0OBBYEFBQXWZZ8RdkPMtC2o0RF+T+gH7VsMA4G
+ A1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMB
+ BggrBgEFBQcDAjAiBgNVHSAEGzAZMA0GCysGAQQBsjEBAgIdMAgGBmeBDAECATA6
+ BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3JsLnRjcy50ZXJlbmEub3JnL1RFUkVO
+ QVNTTENBLmNybDBtBggrBgEFBQcBAQRhMF8wNQYIKwYBBQUHMAKGKWh0dHA6Ly9j
+ cnQudGNzLnRlcmVuYS5vcmcvVEVSRU5BU1NMQ0EuY3J0MCYGCCsGAQUFBzABhhpo
+ dHRwOi8vb2NzcC50Y3MudGVyZW5hLm9yZzAkBgNVHREEHTAbghlzcDIwMTMtaGEt
+ YWRmczIuZ2VhbnQubmV0MA0GCSqGSIb3DQEBBQUAA4IBAQCRJqxXjeInIqlPLH5+
+ iFqL92oLPKiJDqlDWpzR9P0xF7IS4oBApWZ3f2Rgcx1dtwUf7TakMpAuxIxzMjfP
+ xaik/AuInMjxKU9AJS/lRlLogT3YXZ2aAGZbt2P/hYn4mdn5ryB9WF/w7mnyrpBb
+ o7vllsYXYmRX/c0MEPYfSMoKxomRAY2ViIh83m4sYk8+Nkm+3I9t0O3wBqaQYqpc
+ Mg48AG2JwLeuKUIC+faPOckeMXdrknL0Ra7Vb+eRoEKCqw3L9ka5/rloFNEN+hMZ
+ zubyfY3feeIV+kq3YNmjCwhDqckv5kXAdnkFprjB3X/xNfML2Jum02hKkff4AYfP
+ 2IOi
+ </ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+ Location="https://example.org/whatever" index="0" isDefault="true"/>
+ </SPSSODescriptor>
+</EntityDescriptor>
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-noExtensions.xml b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-noExtensions.xml
new file mode 100644
index 0000000..52bdfac
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-noExtensions.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
+ xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi"
+ xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+ entityID="http://example.org/entity">
+ <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+ <KeyDescriptor>
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>
+ MIIEfzCCA2egAwIBAgIQQSSnV5Mk/EXZxgrsbnU7ajANBgkqhkiG9w0BAQUFADA2
+ MQswCQYDVQQGEwJOTDEPMA0GA1UEChMGVEVSRU5BMRYwFAYDVQQDEw1URVJFTkEg
+ U1NMIENBMB4XDTEzMDMwNjAwMDAwMFoXDTE2MDMwNTIzNTk1OVowRzEhMB8GA1UE
+ CxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMSIwIAYDVQQDExlzcDIwMTMtaGEt
+ YWRmczIuZ2VhbnQubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+ qzeOIPfgr/SneQUtjqDRGLbG4YKxmo2zfAIF+wjaYXnDTq/yWt852nImchvCgjWu
+ eXnHJ6oZZGdaN4kMgPh1oxqJ4UE6dXY9HEoru/Awp2P/CGiYOQ4Q5u5+AyFznGRx
+ y2uNYrD85C6uTIgiKytH6Lai8f6dFxHKO/u8o+kbrl2Z1CkAf6wePu2H5a44tGnF
+ SP/s7lW4ScSthNz1OF8BoPFKpWUmJk/584rZZjR+fqB9fQBwxRoRBCFZTduXP0G9
+ 861BS6Nt6Dfuli9jdCBC0L8ai4fH43a94EQVmMfcjImrkYBg7v6lVS4IY75ONiOt
+ sObamvbT+yNEuqw0jBjmKwIDAQABo4IBdjCCAXIwHwYDVR0jBBgwFoAUDL2TaAzz
+ 3qujSWsrN1dH6pDjue0wHQYDVR0OBBYEFBQXWZZ8RdkPMtC2o0RF+T+gH7VsMA4G
+ A1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMB
+ BggrBgEFBQcDAjAiBgNVHSAEGzAZMA0GCysGAQQBsjEBAgIdMAgGBmeBDAECATA6
+ BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3JsLnRjcy50ZXJlbmEub3JnL1RFUkVO
+ QVNTTENBLmNybDBtBggrBgEFBQcBAQRhMF8wNQYIKwYBBQUHMAKGKWh0dHA6Ly9j
+ cnQudGNzLnRlcmVuYS5vcmcvVEVSRU5BU1NMQ0EuY3J0MCYGCCsGAQUFBzABhhpo
+ dHRwOi8vb2NzcC50Y3MudGVyZW5hLm9yZzAkBgNVHREEHTAbghlzcDIwMTMtaGEt
+ YWRmczIuZ2VhbnQubmV0MA0GCSqGSIb3DQEBBQUAA4IBAQCRJqxXjeInIqlPLH5+
+ iFqL92oLPKiJDqlDWpzR9P0xF7IS4oBApWZ3f2Rgcx1dtwUf7TakMpAuxIxzMjfP
+ xaik/AuInMjxKU9AJS/lRlLogT3YXZ2aAGZbt2P/hYn4mdn5ryB9WF/w7mnyrpBb
+ o7vllsYXYmRX/c0MEPYfSMoKxomRAY2ViIh83m4sYk8+Nkm+3I9t0O3wBqaQYqpc
+ Mg48AG2JwLeuKUIC+faPOckeMXdrknL0Ra7Vb+eRoEKCqw3L9ka5/rloFNEN+hMZ
+ zubyfY3feeIV+kq3YNmjCwhDqckv5kXAdnkFprjB3X/xNfML2Jum02hKkff4AYfP
+ 2IOi
+ </ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+ Location="https://example.org/whatever" index="0" isDefault="true"/>
+ </SPSSODescriptor>
+</EntityDescriptor>
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-some.xml b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-some.xml
new file mode 100644
index 0000000..fdd1f35
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/saml/mdattr/EntityAttributeAddingStage-some.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
+ xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi"
+ xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+ entityID="http://example.org/entity">
+ <Extensions>
+ <mdrpi:RegistrationInfo registrationAuthority="http://ukfederation.org.uk"
+ registrationInstant="2014-03-18T15:23:31Z">
+ <mdrpi:RegistrationPolicy xml:lang="en"
+ >http://ukfederation.org.uk/doc/mdrps-20130902</mdrpi:RegistrationPolicy>
+ </mdrpi:RegistrationInfo>
+ <mdattr:EntityAttributes>
+ <saml:Attribute Name="http://macedir.org/entity-category" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+ <saml:AttributeValue>http://www.geant.net/uri/dataprotection-code-of-conduct/v1</saml:AttributeValue>
+ </saml:Attribute>
+ <saml:Attribute Name="http://macedir.org/entity-category-support" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+ <saml:AttributeValue>http://example.org/category2support</saml:AttributeValue>
+ </saml:Attribute>
+ </mdattr:EntityAttributes>
+ </Extensions>
+ <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+ <KeyDescriptor>
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>
+ MIIEfzCCA2egAwIBAgIQQSSnV5Mk/EXZxgrsbnU7ajANBgkqhkiG9w0BAQUFADA2
+ MQswCQYDVQQGEwJOTDEPMA0GA1UEChMGVEVSRU5BMRYwFAYDVQQDEw1URVJFTkEg
+ U1NMIENBMB4XDTEzMDMwNjAwMDAwMFoXDTE2MDMwNTIzNTk1OVowRzEhMB8GA1UE
+ CxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMSIwIAYDVQQDExlzcDIwMTMtaGEt
+ YWRmczIuZ2VhbnQubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+ qzeOIPfgr/SneQUtjqDRGLbG4YKxmo2zfAIF+wjaYXnDTq/yWt852nImchvCgjWu
+ eXnHJ6oZZGdaN4kMgPh1oxqJ4UE6dXY9HEoru/Awp2P/CGiYOQ4Q5u5+AyFznGRx
+ y2uNYrD85C6uTIgiKytH6Lai8f6dFxHKO/u8o+kbrl2Z1CkAf6wePu2H5a44tGnF
+ SP/s7lW4ScSthNz1OF8BoPFKpWUmJk/584rZZjR+fqB9fQBwxRoRBCFZTduXP0G9
+ 861BS6Nt6Dfuli9jdCBC0L8ai4fH43a94EQVmMfcjImrkYBg7v6lVS4IY75ONiOt
+ sObamvbT+yNEuqw0jBjmKwIDAQABo4IBdjCCAXIwHwYDVR0jBBgwFoAUDL2TaAzz
+ 3qujSWsrN1dH6pDjue0wHQYDVR0OBBYEFBQXWZZ8RdkPMtC2o0RF+T+gH7VsMA4G
+ A1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMB
+ BggrBgEFBQcDAjAiBgNVHSAEGzAZMA0GCysGAQQBsjEBAgIdMAgGBmeBDAECATA6
+ BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3JsLnRjcy50ZXJlbmEub3JnL1RFUkVO
+ QVNTTENBLmNybDBtBggrBgEFBQcBAQRhMF8wNQYIKwYBBQUHMAKGKWh0dHA6Ly9j
+ cnQudGNzLnRlcmVuYS5vcmcvVEVSRU5BU1NMQ0EuY3J0MCYGCCsGAQUFBzABhhpo
+ dHRwOi8vb2NzcC50Y3MudGVyZW5hLm9yZzAkBgNVHREEHTAbghlzcDIwMTMtaGEt
+ YWRmczIuZ2VhbnQubmV0MA0GCSqGSIb3DQEBBQUAA4IBAQCRJqxXjeInIqlPLH5+
+ iFqL92oLPKiJDqlDWpzR9P0xF7IS4oBApWZ3f2Rgcx1dtwUf7TakMpAuxIxzMjfP
+ xaik/AuInMjxKU9AJS/lRlLogT3YXZ2aAGZbt2P/hYn4mdn5ryB9WF/w7mnyrpBb
+ o7vllsYXYmRX/c0MEPYfSMoKxomRAY2ViIh83m4sYk8+Nkm+3I9t0O3wBqaQYqpc
+ Mg48AG2JwLeuKUIC+faPOckeMXdrknL0Ra7Vb+eRoEKCqw3L9ka5/rloFNEN+hMZ
+ zubyfY3feeIV+kq3YNmjCwhDqckv5kXAdnkFprjB3X/xNfML2Jum02hKkff4AYfP
+ 2IOi
+ </ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+ Location="https://example.org/whatever" index="0" isDefault="true"/>
+ </SPSSODescriptor>
+</EntityDescriptor>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list