[java-opensaml] branch main updated: OSJ-392: OpenSAML's strict processing mode does not load ADFS metadata

Brent Putman putmanb at georgetown.edu
Thu Dec 14 03:04:31 UTC 2023


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

putmanb pushed a commit to branch main
in repository java-opensaml.

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

The following commit(s) were added to refs/heads/main by this push:
     new 53a72956b OSJ-392: OpenSAML's strict processing mode does not load ADFS metadata
53a72956b is described below

commit 53a72956b916187f1cdf3a141135b90694239fa3
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Tue Nov 28 19:18:58 2023 -0500

    OSJ-392: OpenSAML's strict processing mode does not load ADFS metadata
---
 .../opensaml/core/xml/AbstractXSAnyAdapter.java    | 243 ++++++++++++++++
 .../java/org/opensaml/core/xml/XSAnyAdapter.java   |  33 +++
 .../saml2/core/impl/AssertionUnmarshaller.java     |   4 +
 .../saml/saml2/core/impl/BaseIDXSAnyAdapter.java   |  61 ++++
 .../saml2/core/impl/ConditionXSAnyAdapter.java     |  35 +++
 .../saml2/core/impl/ConditionsUnmarshaller.java    |   4 +
 .../saml2/core/impl/StatementXSAnyAdapter.java     |  35 +++
 .../core/impl/SubjectConfirmationUnmarshaller.java |   4 +
 .../saml/saml2/core/impl/SubjectUnmarshaller.java  |   4 +
 .../impl/EntityDescriptorUnmarshaller.java         |   4 +
 .../metadata/impl/RoleDescriptorXSAnyAdapter.java  | 317 +++++++++++++++++++++
 .../saml/saml2/metadata/tests/MetadataTest.java    |   4 +-
 12 files changed, 746 insertions(+), 2 deletions(-)

diff --git a/opensaml-core-api/src/main/java/org/opensaml/core/xml/AbstractXSAnyAdapter.java b/opensaml-core-api/src/main/java/org/opensaml/core/xml/AbstractXSAnyAdapter.java
new file mode 100644
index 000000000..84a0fd253
--- /dev/null
+++ b/opensaml-core-api/src/main/java/org/opensaml/core/xml/AbstractXSAnyAdapter.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.core.xml;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.xml.namespace.QName;
+
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSBooleanValue;
+import org.opensaml.core.xml.util.IDIndex;
+import org.w3c.dom.Element;
+
+import net.shibboleth.shared.collection.LockableClassToInstanceMultiMap;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * Abstract base class for implementations that adapt/wrap an instance of {@link XSAny}.
+ */
+public abstract class AbstractXSAnyAdapter implements XSAnyAdapter {
+    
+    /** The adapted XSAny instance. */
+    @Nonnull private XSAny adapted;
+
+    /**
+     * Constructor.
+     *
+     * @param xsAny the adapted XSAny instance
+     */
+    protected AbstractXSAnyAdapter(@Nonnull final XSAny xsAny) {
+        adapted = Constraint.isNotNull(xsAny, "Adapted XSAny may not be null");
+    }
+    
+    /**
+     * Get the adapted {@link XSAny} instance.
+     * 
+     * @return the adapted XSAny
+     */
+    @Nonnull
+    public XSAny getAdapted() {
+        return adapted;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void detach() {
+        adapted.detach();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public Element getDOM() {
+        return adapted.getDOM();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull
+    public Element ensureDOM() {
+        return adapted.ensureDOM();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull
+    public QName getElementQName() {
+        return adapted.getElementQName();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull
+    public IDIndex getIDIndex() {
+        return adapted.getIDIndex();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull
+    public NamespaceManager getNamespaceManager() {
+        return adapted.getNamespaceManager();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull
+    public Set<Namespace> getNamespaces() {
+        return adapted.getNamespaces();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public String getNoNamespaceSchemaLocation() {
+        return adapted.getNoNamespaceSchemaLocation();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public List<XMLObject> getOrderedChildren() {
+        return adapted.getOrderedChildren();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public XMLObject getParent() {
+        return adapted.getParent();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public String getSchemaLocation() {
+        return adapted.getSchemaLocation();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public QName getSchemaType() {
+        return adapted.getSchemaType();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean hasChildren() {
+        return adapted.hasChildren();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean hasParent() {
+        return adapted.hasParent();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void releaseChildrenDOM(boolean propagateRelease) {
+        adapted.releaseChildrenDOM(propagateRelease);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void releaseDOM() {
+        adapted.releaseDOM();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void releaseParentDOM(boolean propagateRelease) {
+        adapted.releaseParentDOM(propagateRelease);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public XMLObject resolveID(@Nonnull String id) {
+        return adapted.resolveID(id);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public XMLObject resolveIDFromRoot(@Nonnull String id) {
+        return adapted.resolveIDFromRoot(id);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setDOM(@Nullable Element dom) {
+        adapted.setDOM(dom);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setNoNamespaceSchemaLocation(@Nullable String location) {
+        adapted.setNoNamespaceSchemaLocation(location);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setParent(@Nullable XMLObject parent) {
+        adapted.setParent(parent);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setSchemaLocation(@Nullable String location) {
+        adapted.setSchemaLocation(location);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public Boolean isNil() {
+        return adapted.isNil();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public XSBooleanValue isNilXSBoolean() {
+        return adapted.isNilXSBoolean();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setNil(@Nullable Boolean newNil) {
+        adapted.setNil(newNil);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setNil(@Nullable XSBooleanValue newNil) {
+        adapted.setNil(newNil);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull
+    public LockableClassToInstanceMultiMap<Object> getObjectMetadata() {
+        return adapted.getObjectMetadata();
+    }
+
+}
diff --git a/opensaml-core-api/src/main/java/org/opensaml/core/xml/XSAnyAdapter.java b/opensaml-core-api/src/main/java/org/opensaml/core/xml/XSAnyAdapter.java
new file mode 100644
index 000000000..4831a3db8
--- /dev/null
+++ b/opensaml-core-api/src/main/java/org/opensaml/core/xml/XSAnyAdapter.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.core.xml;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.core.xml.schema.XSAny;
+
+/**
+ * Interface for implementations which adapt/wrap an instance of {@link XSAny}.
+ */
+public interface XSAnyAdapter extends XMLObject {
+    
+    /**
+     * Get the adapted instance of {@link XSAny}
+     * 
+     * @return the adapted instance of XSAny
+     */
+    @Nonnull XSAny getAdapted();
+
+}
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/AssertionUnmarshaller.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/AssertionUnmarshaller.java
index cb447c474..7c807eec5 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/AssertionUnmarshaller.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/AssertionUnmarshaller.java
@@ -22,6 +22,7 @@ import javax.annotation.Nonnull;
 
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.core.xml.io.UnmarshallingException;
+import org.opensaml.core.xml.schema.XSAny;
 import org.opensaml.saml.common.AbstractSAMLObjectUnmarshaller;
 import org.opensaml.saml.saml2.core.Advice;
 import org.opensaml.saml.saml2.core.Assertion;
@@ -59,6 +60,9 @@ public class AssertionUnmarshaller extends AbstractSAMLObjectUnmarshaller {
             assertion.setAdvice((Advice) childObject);
         } else if (childObject instanceof Statement) {
             assertion.getStatements().add((Statement) childObject);
+        } else if (Statement.DEFAULT_ELEMENT_NAME.equals(childObject.getElementQName())
+                && XSAny.class.isInstance(childObject)) {
+            assertion.getStatements().add(new StatementXSAnyAdapter(XSAny.class.cast(childObject)));
         } else {
             super.processChildElement(parentObject, childObject);
         }
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/BaseIDXSAnyAdapter.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/BaseIDXSAnyAdapter.java
new file mode 100644
index 000000000..6ef1bea7d
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/BaseIDXSAnyAdapter.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.saml.saml2.core.impl;
+
+import javax.xml.namespace.QName;
+
+import org.opensaml.core.xml.AbstractXSAnyAdapter;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.saml.saml2.core.BaseID;
+
+/**
+ * Component that adapts an instance of {@link XSAny} to the interface {@link BaseID}.
+ */
+public class BaseIDXSAnyAdapter extends AbstractXSAnyAdapter implements BaseID {
+
+    /**
+     * Constructor.
+     *
+     * @param xsAny the adapted instance
+     */
+    public BaseIDXSAnyAdapter(XSAny xsAny) {
+        super(xsAny);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getNameQualifier() {
+        return getAdapted().getUnknownAttributes().get(new QName(BaseID.NAME_QUALIFIER_ATTRIB_NAME));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setNameQualifier(String newNameQualifier) {
+        getAdapted().getUnknownAttributes().put(new QName(BaseID.NAME_QUALIFIER_ATTRIB_NAME), newNameQualifier);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getSPNameQualifier() {
+        return getAdapted().getUnknownAttributes().get(new QName(BaseID.SP_NAME_QUALIFIER_ATTRIB_NAME));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setSPNameQualifier(String newSPNameQualifier) {
+        getAdapted().getUnknownAttributes().put(new QName(BaseID.SP_NAME_QUALIFIER_ATTRIB_NAME), newSPNameQualifier);
+    }
+
+}
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/ConditionXSAnyAdapter.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/ConditionXSAnyAdapter.java
new file mode 100644
index 000000000..d1425befd
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/ConditionXSAnyAdapter.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.saml.saml2.core.impl;
+
+import org.opensaml.core.xml.AbstractXSAnyAdapter;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.saml.saml2.core.Condition;
+
+/**
+ * Component that adapts an instance of {@link XSAny} to the interface {@link Condition}.
+ */
+public class ConditionXSAnyAdapter extends AbstractXSAnyAdapter implements Condition {
+
+    /**
+     * Constructor.
+     *
+     * @param xsAny the adapted instance
+     */
+    public ConditionXSAnyAdapter(XSAny xsAny) {
+        super(xsAny);
+    }
+
+}
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/ConditionsUnmarshaller.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/ConditionsUnmarshaller.java
index 8d6d2788c..2c2d0e644 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/ConditionsUnmarshaller.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/ConditionsUnmarshaller.java
@@ -22,6 +22,7 @@ import javax.annotation.Nonnull;
 
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.core.xml.io.UnmarshallingException;
+import org.opensaml.core.xml.schema.XSAny;
 import org.opensaml.saml.common.AbstractSAMLObjectUnmarshaller;
 import org.opensaml.saml.saml2.core.Condition;
 import org.opensaml.saml.saml2.core.Conditions;
@@ -44,6 +45,9 @@ public class ConditionsUnmarshaller extends AbstractSAMLObjectUnmarshaller {
 
         if (childObject instanceof Condition) {
             conditions.getConditions().add((Condition) childObject);
+        } else if (Condition.DEFAULT_ELEMENT_NAME.equals(childObject.getElementQName())
+                && XSAny.class.isInstance(childObject)) {
+            conditions.getConditions().add(new ConditionXSAnyAdapter(XSAny.class.cast(childObject)));
         } else {
             super.processChildElement(parentObject, childObject);
         }
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/StatementXSAnyAdapter.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/StatementXSAnyAdapter.java
new file mode 100644
index 000000000..436024187
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/StatementXSAnyAdapter.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.saml.saml2.core.impl;
+
+import org.opensaml.core.xml.AbstractXSAnyAdapter;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.saml.saml2.core.Statement;
+
+/**
+ * Component that adapts an instance of {@link XSAny} to the interface {@link Statement}.
+ */
+public class StatementXSAnyAdapter extends AbstractXSAnyAdapter implements Statement {
+
+    /**
+     * Constructor.
+     *
+     * @param xsAny the adapted instance
+     */
+    public StatementXSAnyAdapter(XSAny xsAny) {
+        super(xsAny);
+    }
+
+}
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/SubjectConfirmationUnmarshaller.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/SubjectConfirmationUnmarshaller.java
index b2174ee3b..e00cdfb1f 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/SubjectConfirmationUnmarshaller.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/SubjectConfirmationUnmarshaller.java
@@ -18,6 +18,7 @@ import javax.annotation.Nonnull;
 
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.core.xml.io.UnmarshallingException;
+import org.opensaml.core.xml.schema.XSAny;
 import org.opensaml.saml.common.AbstractSAMLObjectUnmarshaller;
 import org.opensaml.saml.saml2.core.BaseID;
 import org.opensaml.saml.saml2.core.EncryptedID;
@@ -39,6 +40,9 @@ public class SubjectConfirmationUnmarshaller extends AbstractSAMLObjectUnmarshal
 
         if (childObject instanceof BaseID) {
             subjectConfirmation.setBaseID((BaseID) childObject);
+        } else if (BaseID.DEFAULT_ELEMENT_NAME.equals(childObject.getElementQName())
+                && XSAny.class.isInstance(childObject)) {
+            subjectConfirmation.setBaseID(new BaseIDXSAnyAdapter(XSAny.class.cast(childObject)));
         } else if (childObject instanceof NameID) {
             subjectConfirmation.setNameID((NameID) childObject);
         } else if (childObject instanceof EncryptedID) {
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/SubjectUnmarshaller.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/SubjectUnmarshaller.java
index 103e932ff..85e2f70a7 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/SubjectUnmarshaller.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/core/impl/SubjectUnmarshaller.java
@@ -18,6 +18,7 @@ import javax.annotation.Nonnull;
 
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.core.xml.io.UnmarshallingException;
+import org.opensaml.core.xml.schema.XSAny;
 import org.opensaml.saml.common.AbstractSAMLObjectUnmarshaller;
 import org.opensaml.saml.saml2.core.BaseID;
 import org.opensaml.saml.saml2.core.EncryptedID;
@@ -38,6 +39,9 @@ public class SubjectUnmarshaller extends AbstractSAMLObjectUnmarshaller {
 
         if (childObject instanceof BaseID) {
             subject.setBaseID((BaseID) childObject);
+        } else if (BaseID.DEFAULT_ELEMENT_NAME.equals(childObject.getElementQName())
+                && XSAny.class.isInstance(childObject)) {
+            subject.setBaseID(new BaseIDXSAnyAdapter(XSAny.class.cast(childObject)));
         } else if (childObject instanceof NameID) {
             subject.setNameID((NameID) childObject);
         } else if (childObject instanceof EncryptedID) {
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/metadata/impl/EntityDescriptorUnmarshaller.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/metadata/impl/EntityDescriptorUnmarshaller.java
index c58fbdf3d..0f9692da3 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/metadata/impl/EntityDescriptorUnmarshaller.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/metadata/impl/EntityDescriptorUnmarshaller.java
@@ -18,6 +18,7 @@ import javax.annotation.Nonnull;
 
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.core.xml.io.UnmarshallingException;
+import org.opensaml.core.xml.schema.XSAny;
 import org.opensaml.saml.common.AbstractSAMLObjectUnmarshaller;
 import org.opensaml.saml.saml2.common.CacheableSAMLObject;
 import org.opensaml.saml.saml2.metadata.Extensions;
@@ -52,6 +53,9 @@ public class EntityDescriptorUnmarshaller extends AbstractSAMLObjectUnmarshaller
             entityDescriptor.setSignature((Signature) childObject);
         } else if (childObject instanceof RoleDescriptor) {
             entityDescriptor.getRoleDescriptors().add((RoleDescriptor) childObject);
+        } else if (RoleDescriptor.DEFAULT_ELEMENT_NAME.equals(childObject.getElementQName())
+                && XSAny.class.isInstance(childObject)) {
+            entityDescriptor.getRoleDescriptors().add(new RoleDescriptorXSAnyAdapter(XSAny.class.cast(childObject)));
         } else if (childObject instanceof AffiliationDescriptor) {
             entityDescriptor.setAffiliationDescriptor((AffiliationDescriptor) childObject);
         } else if (childObject instanceof Organization) {
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/metadata/impl/RoleDescriptorXSAnyAdapter.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/metadata/impl/RoleDescriptorXSAnyAdapter.java
new file mode 100644
index 000000000..b7fc5edee
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/metadata/impl/RoleDescriptorXSAnyAdapter.java
@@ -0,0 +1,317 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.saml.saml2.metadata.impl;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.xml.namespace.QName;
+
+import org.opensaml.core.xml.AbstractXSAnyAdapter;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.util.AttributeMap;
+import org.opensaml.saml.saml2.metadata.ContactPerson;
+import org.opensaml.saml.saml2.metadata.Endpoint;
+import org.opensaml.saml.saml2.metadata.Extensions;
+import org.opensaml.saml.saml2.metadata.KeyDescriptor;
+import org.opensaml.saml.saml2.metadata.Organization;
+import org.opensaml.saml.saml2.metadata.RoleDescriptor;
+import org.opensaml.xmlsec.signature.Signature;
+
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.collection.LazyList;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.xml.DOMTypeSupport;
+
+/**
+ * Component that adapts an instance of {@link XSAny} to the interface {@link RoleDescriptor}.
+ */
+public class RoleDescriptorXSAnyAdapter extends AbstractXSAnyAdapter implements RoleDescriptor {
+
+    /**
+     * Constructor.
+     *
+     * @param xsAny the instance to adapt
+     */
+    public RoleDescriptorXSAnyAdapter(XSAny xsAny) {
+        super(xsAny);
+        getAdapted().getUnknownAttributes().registerID(new QName(RoleDescriptor.ID_ATTRIB_NAME));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getSignatureReferenceID() {
+        return getID();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isSigned() {
+        return getSignature() != null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Signature getSignature() {
+        final List<XMLObject> xmlObjects = getAdapted().getUnknownXMLObjects(Signature.DEFAULT_ELEMENT_NAME);
+        if (xmlObjects.isEmpty()) {
+            return null;
+        }
+        return (Signature) xmlObjects.get(0);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setSignature(Signature newSignature) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isValid() {
+        final Instant validUntil = getValidUntil();
+
+        if (null == validUntil) {
+            return true;
+        }
+        
+        return Instant.now().isBefore(validUntil);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Instant getValidUntil() {
+        return DOMTypeSupport.stringToInstant(getAdapted().getUnknownAttributes().get(
+                RoleDescriptor.VALID_UNTIL_ATTRIB_QNAME));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setValidUntil(Instant validUntil) {
+        getAdapted().getUnknownAttributes().put(RoleDescriptor.VALID_UNTIL_ATTRIB_QNAME,
+                DOMTypeSupport.instantToString(validUntil));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Duration getCacheDuration() {
+        return DOMTypeSupport.stringToDuration(getAdapted().getUnknownAttributes().get(
+                RoleDescriptor.CACHE_DURATION_ATTRIB_QNAME));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setCacheDuration(Duration duration) {
+        getAdapted().getUnknownAttributes().put(RoleDescriptor.CACHE_DURATION_ATTRIB_QNAME,
+                DOMTypeSupport.durationToString(duration));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public AttributeMap getUnknownAttributes() {
+        return getAdapted().getUnknownAttributes();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getID() {
+        return getAdapted().getUnknownAttributes().get(new QName(RoleDescriptor.ID_ATTRIB_NAME));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setID(String newID) {
+        getAdapted().getUnknownAttributes().put(new QName(RoleDescriptor.ID_ATTRIB_NAME), newID);
+    }
+    
+    /**
+     * Internal method for fetching the supported protocols as a list.
+     * 
+     * @return the list, possibly empty
+     */
+    @Nonnull private List<String> fetchSupportedProtocols() {
+        final String rawValue = StringSupport.trimOrNull(getAdapted().getUnknownAttributes().get(
+                new QName(RoleDescriptor.PROTOCOL_ENUMERATION_ATTRIB_NAME)));
+        if (rawValue == null) {
+            return new LazyList<String>();
+        }
+        return StringSupport.stringToList(rawValue, " ");
+    }
+    
+    /**
+     * Internal method for storing the list of supported protocols as a string.
+     * 
+     * @param protocols the list of protocols
+     */
+    private void storeSupportedProtocols(@Nonnull final List<String> protocols) {
+       if (protocols.isEmpty()) {
+          getAdapted().getUnknownAttributes().remove(new QName(RoleDescriptor.PROTOCOL_ENUMERATION_ATTRIB_NAME));
+       } else {
+           getAdapted().getUnknownAttributes().put(new QName(RoleDescriptor.PROTOCOL_ENUMERATION_ATTRIB_NAME),
+                   StringSupport.listToStringValue(protocols, " "));
+       }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public List<String> getSupportedProtocols() {
+        return CollectionSupport.copyToList(fetchSupportedProtocols());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isSupportedProtocol(String protocol) {
+        return fetchSupportedProtocols().contains(protocol);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addSupportedProtocol(String protocol) {
+        final List<String> protocols = fetchSupportedProtocols();
+        protocols.add(protocol);
+        storeSupportedProtocols(protocols);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void removeSupportedProtocol(String protocol) {
+        final List<String> protocols = fetchSupportedProtocols();
+        protocols.remove(protocol);
+        storeSupportedProtocols(protocols);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void removeSupportedProtocols(Collection<String> protocolsToRemove) {
+        final List<String> protocols = fetchSupportedProtocols();
+        protocols.removeAll(protocolsToRemove);
+        storeSupportedProtocols(protocols);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void removeAllSupportedProtocols() {
+        storeSupportedProtocols(Collections.emptyList());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getErrorURL() {
+        return getAdapted().getUnknownAttributes().get(new QName(RoleDescriptor.ERROR_URL_ATTRIB_NAME));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setErrorURL(String errorURL) {
+        getAdapted().getUnknownAttributes().put(new QName(RoleDescriptor.ERROR_URL_ATTRIB_NAME), errorURL);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Extensions getExtensions() {
+        final List<XMLObject> xmlObjects = getAdapted().getUnknownXMLObjects(Extensions.DEFAULT_ELEMENT_NAME);
+        if (xmlObjects.isEmpty()) {
+            return null;
+        }
+
+        return xmlObjects.stream()
+                .filter(Extensions.class::isInstance)
+                .map(Extensions.class::cast)
+                .findFirst().get();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setExtensions(Extensions extensions) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public List<KeyDescriptor> getKeyDescriptors() {
+        final List<XMLObject> xmlObjects = getAdapted().getUnknownXMLObjects(KeyDescriptor.DEFAULT_ELEMENT_NAME);
+        if (xmlObjects.isEmpty()) {
+            return null;
+        }
+        return xmlObjects.stream()
+                .filter(KeyDescriptor.class::isInstance)
+                .map(KeyDescriptor.class::cast)
+                .toList();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Organization getOrganization() {
+        final List<XMLObject> xmlObjects = getAdapted().getUnknownXMLObjects(Organization.DEFAULT_ELEMENT_NAME);
+        if (xmlObjects.isEmpty()) {
+            return null;
+        }
+        return xmlObjects.stream()
+                .filter(Organization.class::isInstance)
+                .map(Organization.class::cast)
+                .findFirst().get();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setOrganization(Organization organization) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public List<ContactPerson> getContactPersons() {
+        final List<XMLObject> xmlObjects = getAdapted().getUnknownXMLObjects(ContactPerson.DEFAULT_ELEMENT_NAME);
+        if (xmlObjects.isEmpty()) {
+            return null;
+        }
+        return xmlObjects.stream()
+                .filter(ContactPerson.class::isInstance)
+                .map(ContactPerson.class::cast)
+                .toList();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public List<Endpoint> getEndpoints() {
+        final List<XMLObject> xmlObjects = getAdapted().getUnknownXMLObjects(Endpoint.DEFAULT_ELEMENT_NAME);
+        if (xmlObjects.isEmpty()) {
+            return null;
+        }
+        return xmlObjects.stream()
+                .filter(Endpoint.class::isInstance)
+                .map(Endpoint.class::cast)
+                .toList();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public List<Endpoint> getEndpoints(QName type) {
+        final List<XMLObject> xmlObjects = getAdapted().getUnknownXMLObjects(type);
+        if (xmlObjects.isEmpty()) {
+            return null;
+        }
+        return xmlObjects.stream()
+                .filter(Endpoint.class::isInstance)
+                .map(Endpoint.class::cast)
+                .toList();
+    }
+
+}
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/metadata/tests/MetadataTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/metadata/tests/MetadataTest.java
index a544f7de1..21b86a8d2 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/metadata/tests/MetadataTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/metadata/tests/MetadataTest.java
@@ -119,8 +119,8 @@ public class MetadataTest extends XMLObjectBaseTestCase {
         }
     }
 
-    /** Tests unmarshalling an ADFS metadata document with their "fun" extensions. */
-    @Test(enabled=false)
+    /** Tests unmarshalling an ADFS metadata document with their "fun" extensions. See OSJ-392. */
+    @Test
     public void testADFSUnmarshall() {
         String adfsMDFile = "/org/opensaml/saml/saml2/metadata/adfs-metadata.xml";
 

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


More information about the commits mailing list