[java-identity-provider] branch master updated: IDP-1455 Allow data Connectors to export attributes
Rod Widdowson
rdw at steadingsoftware.com
Sat May 18 10:54:16 EDT 2019
This is an automated email from the git hooks/post-receive script.
rdw pushed a commit to branch master
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=c6d172722aab1e4e73233f9e40917f1d234ba6de
The following commit(s) were added to refs/heads/master by this push:
new c6d1727 IDP-1455 Allow data Connectors to export attributes
c6d1727 is described below
commit c6d172722aab1e4e73233f9e40917f1d234ba6de
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Sat May 18 15:47:34 2019 +0100
IDP-1455 Allow data Connectors to export attributes
https://issues.shibboleth.net/jira/browse/IDP-1455
Most of the work is just colelcting and storing the control
values in various beans.
All the heavy lifting is done during attribute resolution where we will
provoke resolution of exporting data connectors and then inject exported
attributes after attribute resolution is done.
Much of the change to the resolver is to refactor out code which is now
common to recording attribute resolution or exporting attributes from
data connectors.
---
.../attribute/resolver/AbstractDataConnector.java | 63 +++++-
.../idp/attribute/resolver/DataConnector.java | 19 ++
.../attribute/resolver/ResolvedDataConnector.java | 30 ++-
.../resolver/AbstractDataConnectorTest.java | 37 +++-
.../resolver/impl/AttributeResolverImpl.java | 213 +++++++++++++++------
.../resolver/impl/AttributeResolverImplTest.java | 45 +++++
.../spring/dc/AbstractDataConnectorParser.java | 22 +++
.../spring/dc/impl/DataConnectorFactoryBean.java | 52 +++--
.../dc/ComputedIdDataConnectorParserTest.java | 2 +
.../spring/dc/StaticDataConnectorParserTest.java | 13 +-
.../dc/http/HTTPDataConnectorParserTest.java | 5 +
.../dc/http/http-attribute-resolver-v2-hybrid.xml | 1 +
.../spring/dc/resolver/staticAttributes.xml | 1 +
.../spring/dc/resolver/staticAttributesHybrid.xml | 1 +
.../schema/shibboleth-attribute-resolver.xsd | 10 +
15 files changed, 421 insertions(+), 93 deletions(-)
diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractDataConnector.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractDataConnector.java
index 04a24ea..e004ff9 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractDataConnector.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractDataConnector.java
@@ -19,23 +19,28 @@ package net.shibboleth.idp.attribute.resolver;
import java.time.Duration;
import java.time.Instant;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Map;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import net.shibboleth.idp.attribute.IdPAttribute;
import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.primitive.StringSupport;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
/** Base class for data connector resolver plugins. */
@ThreadSafe
public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<String, IdPAttribute>> implements
@@ -56,9 +61,16 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
/** How long to wait until we declare the connector live again. */
@Nonnull private Duration noRetryDelay;
+ /** Do we release all attributes?. */
+ private boolean exportAllAttributes;
+
+ /** Which named attributes do we release?. */
+ @Nonnull @NonnullElements private Collection<String> exportAttributes;
+
/** Constructor. */
public AbstractDataConnector() {
noRetryDelay = Duration.ZERO;
+ exportAttributes = Collections.emptySet();
}
/**
@@ -68,7 +80,7 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
* @return ID of the {@link AbstractDataConnector} whose values will be used in the event that this data connector
* experiences an error
*/
- @Nullable public String getFailoverDataConnectorId() {
+ @Override @Nullable public String getFailoverDataConnectorId() {
return failoverDataConnectorId;
}
@@ -98,7 +110,7 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
/**
* {@inheritDoc}
*/
- @Nullable public Instant getLastFail() {
+ @Override @Nullable public Instant getLastFail() {
return lastFail;
}
@@ -112,11 +124,50 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
}
/** {@inheritDoc} */
- @Nonnull public Duration getNoRetryDelay() {
+ @Override @Nonnull public Duration getNoRetryDelay() {
return noRetryDelay;
}
/**
+ * Set whether we export all attributes.
+ *
+ * @param what whether we export all attributes
+ */
+ public void setExportAllAttributes(final boolean what) {
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ exportAllAttributes = what;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override public boolean isExportAllAttributes() {
+ return exportAllAttributes;
+ }
+
+ /**
+ * Sets the list of attribute names to export during resolution.
+ *
+ * @param what the list
+ */
+ public void setExportAttributes(@Nonnull final Collection<String> what) {
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ exportAttributes = what.stream().map(StringSupport::trimOrNull).
+ filter(e -> e != null).
+ collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override @Nonnull @NonnullElements @Unmodifiable public Collection<String> getExportAttributes() {
+ return exportAttributes;
+ }
+
+
+ /**
* {@inheritDoc}
*
* This method delegates to
diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/DataConnector.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/DataConnector.java
index e8fc731..ca85576 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/DataConnector.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/DataConnector.java
@@ -19,6 +19,7 @@ package net.shibboleth.idp.attribute.resolver;
import java.time.Duration;
import java.time.Instant;
+import java.util.Collection;
import java.util.Map;
import javax.annotation.Nonnull;
@@ -26,6 +27,8 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
/**
* Base class for data connector resolver plugins.
@@ -57,4 +60,20 @@ public interface DataConnector extends ResolverPlugin<Map<String, IdPAttribute>>
*/
@Nullable String getFailoverDataConnectorId();
+ /**
+ * Gets whether we export all attributes during resolution.
+ * <p>
+ * If this returns false then {@link #getExportAttributes()} returns the
+ * list of attributes to return.
+ *
+ * @return whether we export all attributes
+ */
+ boolean isExportAllAttributes();
+
+ /**
+ * Gets the list of attribute names to export during resolution.
+ *
+ * @return the list of attribute names to export during resolution
+ */
+ @Nonnull @NonnullElements @Unmodifiable Collection<String> getExportAttributes();
}
\ No newline at end of file
diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/ResolvedDataConnector.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/ResolvedDataConnector.java
index e6adb2d..1024d5f 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/ResolvedDataConnector.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/ResolvedDataConnector.java
@@ -17,6 +17,7 @@
package net.shibboleth.idp.attribute.resolver;
+import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
@@ -25,14 +26,16 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
+import org.opensaml.profile.context.ProfileRequestContext;
+
import net.shibboleth.idp.attribute.IdPAttribute;
import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
-import org.opensaml.profile.context.ProfileRequestContext;
-
/**
* A proxy which wraps a resolved data connector and always returns the same attributes. The goal being that once a data
* connector is resolved this can be used in its place and calls to
@@ -125,6 +128,29 @@ public final class ResolvedDataConnector extends AbstractDataConnector {
return resolvedConnector.toString();
}
+ /** {@inheritDoc} */
+ @Override public void setExportAllAttributes(@Nullable final boolean what) {
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isExportAllAttributes() {
+ return resolvedConnector.isExportAllAttributes();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setExportAttributes(@Nonnull final Collection<String> what) {
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nonnull @NonnullElements @Unmodifiable public Collection<String> getExportAttributes() {
+ return resolvedConnector.getExportAttributes();
+ }
+
+
/**
* Gets the resolved attributes.
*
diff --git a/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/AbstractDataConnectorTest.java b/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/AbstractDataConnectorTest.java
index 272c4af..1f02ab3 100644
--- a/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/AbstractDataConnectorTest.java
+++ b/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/AbstractDataConnectorTest.java
@@ -17,20 +17,27 @@
package net.shibboleth.idp.attribute.resolver;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
import net.shibboleth.idp.attribute.IdPAttribute;
import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
/**
* Unit test for {@link DataConnector}. This test does not test any methods inherited from
* {@link ResolverPlugin}, those are covered in {@link AbstractResolverPluginTest}.
@@ -49,13 +56,13 @@ public class AbstractDataConnectorTest {
MockBaseDataConnector connector = new MockBaseDataConnector("foo", Collections.EMPTY_MAP);
connector.setFailoverDataConnectorId(" foo ");
- Assert.assertEquals(connector.getFailoverDataConnectorId(), "foo");
+ assertEquals(connector.getFailoverDataConnectorId(), "foo");
connector.setFailoverDataConnectorId("");
- Assert.assertNull(connector.getFailoverDataConnectorId());
+ assertNull(connector.getFailoverDataConnectorId());
connector.setFailoverDataConnectorId(null);
- Assert.assertNull(connector.getFailoverDataConnectorId());
+ assertNull(connector.getFailoverDataConnectorId());
}
/** Test the resolution of the data connector. */
@@ -65,7 +72,7 @@ public class AbstractDataConnectorTest {
MockBaseDataConnector connector = new MockBaseDataConnector("foo", (Map<String, IdPAttribute>) null);
connector.initialize();
- Assert.assertNull(connector.resolve(context));
+ assertNull(connector.resolve(context));
HashMap<String, IdPAttribute> values = new HashMap<>();
connector = new MockBaseDataConnector("foo", values);
@@ -78,9 +85,19 @@ public class AbstractDataConnectorTest {
connector = new MockBaseDataConnector("foo", values);
connector.initialize();
Map<String, IdPAttribute> result = connector.resolve(context);
- Assert.assertNotNull(result);
- Assert.assertTrue(result.containsKey(attribute.getId()));
- Assert.assertEquals(result.get(attribute.getId()), attribute);
+ assertNotNull(result);
+ assertTrue(result.containsKey(attribute.getId()));
+ assertEquals(result.get(attribute.getId()), attribute);
+ }
+
+ @Test public void testExports() {
+ final AbstractDataConnector connector = new MockBaseDataConnector("foo", null);
+ assertFalse(connector.isExportAllAttributes());
+ assertTrue(connector.getExportAttributes().isEmpty());
+ connector.setExportAllAttributes(true);
+ assertTrue(connector.isExportAllAttributes());
+ connector.setExportAttributes(List.of("", "foo", "bar", "foo"));
+ assertEquals(connector.getExportAttributes().size(), 2);
}
/**
diff --git a/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImpl.java b/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImpl.java
index 064f762..75c5dd7 100644
--- a/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImpl.java
+++ b/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImpl.java
@@ -23,9 +23,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
@@ -51,6 +52,7 @@ import net.shibboleth.idp.attribute.resolver.AttributeResolver;
import net.shibboleth.idp.attribute.resolver.DataConnector;
import net.shibboleth.idp.attribute.resolver.ResolutionException;
import net.shibboleth.idp.attribute.resolver.ResolvedAttributeDefinition;
+import net.shibboleth.idp.attribute.resolver.ResolvedDataConnector;
import net.shibboleth.idp.attribute.resolver.ResolverAttributeDefinitionDependency;
import net.shibboleth.idp.attribute.resolver.ResolverDataConnectorDependency;
import net.shibboleth.idp.attribute.resolver.ResolverPlugin;
@@ -61,7 +63,7 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElemen
import net.shibboleth.utilities.java.support.annotation.constraint.NullableElements;
import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
import net.shibboleth.utilities.java.support.collection.LazyList;
-import net.shibboleth.utilities.java.support.collection.LazySet;
+import net.shibboleth.utilities.java.support.collection.LazyMap;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -124,7 +126,6 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
checkedDefinitions = Collections.emptyMap();
}
attributeDefinitions = ImmutableMap.copyOf(checkedDefinitions);
-
}
/**
@@ -218,22 +219,32 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
Constraint.isNotNull(resolutionContext, "Attribute resolution context cannot be null");
+ final AttributeResolverWorkContext workContext =
+ resolutionContext.getSubcontext(AttributeResolverWorkContext.class, true);
+
final boolean timerStarted = startTimer(resolutionContext);
try {
log.debug("{} Initiating attribute resolution", logPrefix);
-
- if (attributeDefinitions.size() == 0) {
- log.debug("{} No attribute definition available, no attributes were resolved", logPrefix);
+
+ boolean hasExportingDataConnector = false;
+
+ for (final Entry<String, DataConnector> dataConnectorEntry : dataConnectors.entrySet()) {
+ if (dataConnectorEntry.getValue().isExportAllAttributes() ||
+ !dataConnectorEntry.getValue().getExportAttributes().isEmpty()) {
+ hasExportingDataConnector = true;
+ resolveDataConnector(dataConnectorEntry.getKey(), resolutionContext);
+ }
+ }
+
+ if (attributeDefinitions.size() == 0 && !hasExportingDataConnector) {
+ log.debug("{} No attribute definition available or exporting data connectors" +
+ ", no attributes were resolved", logPrefix);
return;
}
final Collection<String> attributeIds = getToBeResolvedAttributeIds(resolutionContext);
log.debug("{} Attempting to resolve the following attribute definitions {}", logPrefix, attributeIds);
- // Create work context to hold intermediate results.
- final AttributeResolverWorkContext workContext =
- resolutionContext.getSubcontext(AttributeResolverWorkContext.class, true);
-
for (final String attributeId : attributeIds) {
resolveAttributeDefinition(attributeId, resolutionContext);
}
@@ -241,11 +252,11 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
log.debug("{} Finalizing resolved attributes", logPrefix);
finalizeResolvedAttributes(resolutionContext);
- resolutionContext.removeSubcontext(workContext);
-
log.debug("{} Final resolved attribute collection: {}", logPrefix,
resolutionContext.getResolvedIdPAttributes().keySet());
} finally {
+ resolutionContext.removeSubcontext(workContext);
+
if (timerStarted) {
stopTimer(resolutionContext);
}
@@ -425,25 +436,53 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
log.debug("{} Finished resolving dependencies for '{}'", logPrefix, plugin.getId());
}
- /**
- * Finalizes the set of resolved attributes and places them in the {@link AttributeResolutionContext}. The result of
- * each {@link AttributeDefinition} resolution is inspected. If the result is not null, a dependency-only attribute,
- * or an attribute that contains no values then it becomes part of the final set of resolved attributes.
- *
- * <p>
- * Values are also de-duplicated here, so that all the intermediate operations maintain the coherency of
- * multi-valued result sets produced by data connectors.
- * </p>
- *
- * @param resolutionContext current resolution context
+ /** Helper method for exporting attributes.
+ * @param attributeId the if (for logging)
+ * @param input the inout list
+ * @return a null stripped, or null list of values
*/
- // Checkstyle: CyclomaticComplexity OFF
- protected void finalizeResolvedAttributes(@Nonnull final AttributeResolutionContext resolutionContext) {
- Constraint.isNotNull(resolutionContext, "Attribute resolution context cannot be null");
- final AttributeResolverWorkContext workContext =
- resolutionContext.getSubcontext(AttributeResolverWorkContext.class, false);
+ private @Nullable @NonnullElements List<IdPAttributeValue> filterAttributeValues(final String attributeId,
+ final List<IdPAttributeValue> input) {
+
+ log.debug("{} De-duping (and null filtering) attribute definition {} result",
+ logPrefix, attributeId);
+ final List<IdPAttributeValue> result = new ArrayList<>(input.size());
+ final Set<IdPAttributeValue> monitor = new HashSet<>(input.size());
+
+ for (final IdPAttributeValue value : input) {
+ if (isStripNulls()) {
+ if (null == value) {
+ log.debug("{} Stripping null value", logPrefix);
+ continue;
+ } else if (value instanceof EmptyAttributeValue) {
+ log.debug("{} Stripping {} value", logPrefix, ((EmptyAttributeValue)value).getValue());
+ continue;
+ }
+ // ByteAttributeValue, StringAttributeValue and XMLObjectValue are Constrained to not be empty
+ }
- final LazySet<IdPAttribute> resolvedAttributes = new LazySet<>();
+ if (!monitor.add(value)) {
+ log.debug("{} Removing duplicate value {} of attribute '{}' from resolution result", logPrefix,
+ value, attributeId);
+ } else {
+ result.add(value);
+ }
+ }
+
+ // No values
+ if (monitor.isEmpty()) {
+ return null;
+ }
+ return result;
+ }
+
+ /**
+ * Helper function to collect suitably resolved attributes.
+ * @param resolvedAttributes bucket to collect attributes into
+ * @param workContext context to extract attributes from
+ */
+ private void collectResolvedAttributes(final Map<String, IdPAttribute> resolvedAttributes,
+ final AttributeResolverWorkContext workContext) {
for (final ResolvedAttributeDefinition definition : workContext.getResolvedIdPAttributeDefinitions().values()) {
final IdPAttribute resolvedAttribute = definition.getResolvedAttribute();
@@ -461,37 +500,11 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
continue;
}
- // Remove duplicate attribute values.
- log.debug("{} De-duping (and null filtering) attribute definition {} result",
- logPrefix, definition.getId());
- final Iterator<IdPAttributeValue> valueIter = resolvedAttribute.getValues().iterator();
- final List<IdPAttributeValue> result = new ArrayList<>(resolvedAttribute.getValues().size());
- final Set<IdPAttributeValue> monitor = new HashSet<>(resolvedAttribute.getValues().size());
-
- while (valueIter.hasNext()) {
- final IdPAttributeValue value = valueIter.next();
-
- if (isStripNulls()) {
- if (null == value) {
- log.debug("{} Stripping null value", logPrefix);
- continue;
- } else if (value instanceof EmptyAttributeValue) {
- log.debug("{} Stripping {} value", logPrefix, ((EmptyAttributeValue)value).getValue());
- continue;
- }
- // ByteAttributeValue, StringAttributeValue and XMLObjectValue are Constrained to not be empty
- }
-
- if (!monitor.add(value)) {
- log.debug("{} Removing duplicate value {} of attribute '{}' from resolution result", logPrefix,
- value, resolvedAttribute.getId());
- } else {
- result.add(value);
- }
- }
+ final List<IdPAttributeValue> result =
+ filterAttributeValues(definition.getId(), resolvedAttribute.getValues());
// Remove value-less attributes.
- if (monitor.isEmpty()) {
+ if (result == null) {
log.debug("{} Removing result of attribute definition '{}', contains no values", logPrefix,
definition.getId());
continue;
@@ -499,14 +512,90 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
resolvedAttribute.setValues(result);
log.debug("{} Attribute '{}' has {} values after post-processing", logPrefix, resolvedAttribute.getId(),
- monitor.size());
+ result.size());
+
+ resolvedAttributes.put(resolvedAttribute.getId(), resolvedAttribute);
+ }
+ }
- resolvedAttributes.add(resolvedAttribute);
+ /**
+ * Helper function to collect attributes from suitabl data connectors.
+ * @param resolvedAttributes bucket to collect attributes into
+ * @param workContext context to extract attributes from
+ */
+ // CheckStyle: CyclomaticComplexit OFF
+ private void collectExportingDataConnectors(final Map<String, IdPAttribute> resolvedAttributes,
+ final AttributeResolverWorkContext workContext) {
+
+ for (final ResolvedDataConnector dataConnector: workContext.getResolvedDataConnectors().values()) {
+
+ if (!dataConnector.isExportAllAttributes() && dataConnector.getExportAttributes().isEmpty()) {
+ continue;
+ }
+
+ for (final IdPAttribute attribute:dataConnector.getResolvedAttributes().values()) {
+ if (!dataConnector.isExportAllAttributes() &&
+ !dataConnector.getExportAttributes().contains(attribute.getId())) {
+ continue;
+ }
+ if (resolvedAttributes.get(attribute.getId()) != null) {
+ log.warn("{} could not export attibute '{}' from data connector '{}' since an attribute of " +
+ "that name already exists.", logPrefix, attribute.getId(), dataConnector.getId());
+ continue;
+ }
+ final List<IdPAttributeValue> values = filterAttributeValues(attribute.getId(), attribute.getValues());
+ if (values == null) {
+ log.debug("{} Removing attribute '{}' from data connector '{}' with no values", logPrefix,
+ dataConnector.getId(), attribute.getId());
+ continue;
+ }
+ final IdPAttribute newAttr = new IdPAttribute(attribute.getId());
+ newAttr.setValues(values);
+ if (attribute.getDisplayDescriptions().size() > 0) {
+ newAttr.setDisplayDescriptions(attribute.getDisplayDescriptions());
+ } else {
+ newAttr.setDisplayDescriptions(Map.of(Locale.getDefault(),
+ dataConnector.getId() + "/" + attribute.getId()));
+ }
+ if (attribute.getDisplayNames().size() > 0) {
+ newAttr.setDisplayNames(attribute.getDisplayNames());
+ } else {
+ newAttr.setDisplayNames(Map.of(Locale.getDefault(),
+ dataConnector.getId() + "/" + attribute.getId()));
+ }
+ resolvedAttributes.put(attribute.getId(), newAttr);
+ }
}
+ }
+ // CheckStyle: CyclomaticComplexit ON
+
+ /**
+ * Finalizes the set of resolved attributes and places them in the {@link AttributeResolutionContext}. The result of
+ * each {@link AttributeDefinition} resolution is inspected. If the result is not null, a dependency-only attribute,
+ * or an attribute that contains no values then it becomes part of the final set of resolved attributes.
+ * <p>
+ * Then we handle attribute exports from DataConnectors.
+ *
+ * <p>
+ * Values are also de-duplicated here, so that all the intermediate operations maintain the coherency of
+ * multi-valued result sets produced by data connectors.
+ * </p>
+ *
+ * @param resolutionContext current resolution context
+ */
+ protected void finalizeResolvedAttributes(@Nonnull final AttributeResolutionContext resolutionContext) {
+ Constraint.isNotNull(resolutionContext, "Attribute resolution context cannot be null");
+ final AttributeResolverWorkContext workContext =
+ resolutionContext.getSubcontext(AttributeResolverWorkContext.class, false);
+
+ final Map<String, IdPAttribute> resolvedAttributes = new LazyMap<>();
+
+ collectResolvedAttributes(resolvedAttributes, workContext);
+
+ collectExportingDataConnectors(resolvedAttributes, workContext);
- resolutionContext.setResolvedIdPAttributes(resolvedAttributes);
+ resolutionContext.setResolvedIdPAttributes(resolvedAttributes.values());
}
- // Checkstyle: CyclomaticComplexity ON
/** {@inheritDoc} */
@Override protected void doInitialize() throws ComponentInitializationException {
diff --git a/idp-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImplTest.java b/idp-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImplTest.java
index 095df0b..82ab9d8 100644
--- a/idp-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImplTest.java
+++ b/idp-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImplTest.java
@@ -55,6 +55,7 @@ import net.shibboleth.idp.attribute.resolver.ResolverAttributeDefinitionDependen
import net.shibboleth.idp.attribute.resolver.ResolverDataConnectorDependency;
import net.shibboleth.idp.attribute.resolver.ad.impl.SimpleAttributeDefinition;
import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
+import net.shibboleth.idp.attribute.resolver.dc.impl.StaticDataConnector;
import net.shibboleth.idp.saml.impl.TestSources;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.annotation.constraint.NullableElements;
@@ -169,6 +170,50 @@ public class AttributeResolverImplTest {
assertEquals(context.getResolvedIdPAttributes().get("ad1"), attribute);
}
+ @Test public void resolveWithExports() throws Exception {
+
+ final IdPAttribute attribute1 = new IdPAttribute("ad1");
+ attribute1.setValues(Collections.singletonList(new StringAttributeValue("value1")));
+ final IdPAttribute attribute2 = new IdPAttribute("ad2");
+ attribute2.setValues(Collections.singletonList(new StringAttributeValue("value2")));
+ final IdPAttribute attribute3 = new IdPAttribute("ad3");
+ attribute3.setValues(Collections.singletonList(new StringAttributeValue("value3")));
+ final IdPAttribute attribute4 = new IdPAttribute("ad4");
+ attribute4.setValues(Collections.singletonList(new StringAttributeValue("value3")));
+
+ // Connector1 contributes attribute1 and attribute2
+ final StaticDataConnector connector1 = new StaticDataConnector();
+ connector1.setId("dc1");
+ connector1.setValues(List.of(attribute1, attribute2));
+ connector1.setExportAllAttributes(true);
+
+ // Connector1 contributes attribute2 and attribute3 (but not 4 or 1)
+ final StaticDataConnector connector2 = new StaticDataConnector();
+ connector2.setId("dc2");
+ connector2.setValues(List.of(attribute2, attribute3, attribute4));
+ connector2.setExportAttributes(List.of(attribute2.getId(), attribute3.getId(), attribute1.getId()));
+
+ // Connector 3 contributes nothing
+ final StaticDataConnector connector3 = new StaticDataConnector();
+ connector3.setId("dc3");
+ connector3.setValues(List.of(attribute4));
+
+ final AttributeResolverImpl resolver = newAttributeResolverImpl("foo", null, List.of(connector1, connector2, connector3));
+ for (DataConnector connector : resolver.getDataConnectors().values()) {
+ connector.initialize();
+ }
+ resolver.initialize();
+
+ final AttributeResolutionContext context = new AttributeResolutionContext();
+ resolver.resolveAttributes(context);
+
+ // should have resolved 1, 2 and 3
+ assertEquals(context.getResolvedIdPAttributes().size(), 3);
+ assertEquals(context.getResolvedIdPAttributes().get(attribute1.getId()), attribute1);
+ assertEquals(context.getResolvedIdPAttributes().get(attribute2.getId()), attribute2);
+ assertEquals(context.getResolvedIdPAttributes().get(attribute3.getId()), attribute3);
+ }
+
/** Test that a simple resolve returns the expected results. */
@Test public void resolveSpecificAttribute() throws Exception {
final IdPAttribute attribute = new IdPAttribute("ad1");
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/AbstractDataConnectorParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/AbstractDataConnectorParser.java
index eff04c2..4933d27 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/AbstractDataConnectorParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/AbstractDataConnectorParser.java
@@ -22,6 +22,7 @@ import java.util.List;
import javax.annotation.Nonnull;
import javax.xml.namespace.QName;
+import net.shibboleth.ext.spring.util.SpringSupport;
import net.shibboleth.idp.attribute.resolver.AbstractDataConnector;
import net.shibboleth.idp.attribute.resolver.spring.BaseResolverPluginParser;
import net.shibboleth.idp.attribute.resolver.spring.dc.impl.DataConnectorFactoryBean;
@@ -73,6 +74,16 @@ public abstract class AbstractDataConnectorParser extends BaseResolverPluginPars
*/
@Nonnull @NotEmpty public static final String ATTR_POSTPROCESSORS_REF = "postProcessorsRef";
+ /**
+ * Whether to export all attributes.
+ */
+ @Nonnull @NotEmpty public static final String ATTR_EXPORT_ALL = "exportAllAttributes";
+
+ /**
+ * Which attributes to export.
+ */
+ @Nonnull @NotEmpty public static final String ATTR_EXPORT_NAMES = "exportAttributes";
+
/** Failover data connector attribute name. */
@Nonnull public static final QName FAILOVER_DATA_CONNECTOR_ELEMENT_NAME = new QName(
AttributeResolverNamespaceHandler.NAMESPACE, "FailoverDataConnector");
@@ -130,6 +141,17 @@ public abstract class AbstractDataConnectorParser extends BaseResolverPluginPars
StringSupport.trimOrNull(config.getAttributeNS(null, ATTR_NORETRYDELAY)));
}
+ if (config.hasAttributeNS(null, ATTR_EXPORT_ALL)) {
+ if (config.hasAttributeNS(null, ATTR_EXPORT_NAMES)) {
+ log.warn("{} {} overrides {}", getLogPrefix(), ATTR_EXPORT_ALL, ATTR_EXPORT_NAMES);
+ }
+ builder.addPropertyValue("exportAllAttributes",
+ StringSupport.trimOrNull(config.getAttributeNS(null, ATTR_EXPORT_ALL)));
+ } else if (config.hasAttributeNS(null, ATTR_EXPORT_NAMES)) {
+ builder.addPropertyValue("exportAttributes",
+ SpringSupport.getAttributeValueAsList(config.getAttributeNodeNS(null, ATTR_EXPORT_NAMES)));
+ }
+
if (isNative(config)) {
// parse the configuration into a beanfactory and inject the resources as well
builder.addPropertyValue("objectType", getNativeBeanClass());
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/impl/DataConnectorFactoryBean.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/impl/DataConnectorFactoryBean.java
index 1148e9e..4099541 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/impl/DataConnectorFactoryBean.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/impl/DataConnectorFactoryBean.java
@@ -22,6 +22,7 @@ import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.time.Duration;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -86,9 +87,15 @@ public class DataConnectorFactoryBean extends AbstractResolverPluginFactoryBean<
/** Data Connector property "noRetryDelay". */
@Nullable private Duration noRetryDelay;
+ /** Do we release all attributes?. */
+ private Boolean exportAllAttributes;
+
+ /** Which named attributes do we release?. */
+ @Nonnull @NonnullElements private Collection<String> exportAttributes = Collections.EMPTY_SET;
+
/**
* Data Connector property "failoverDataConnectorId".
- *
+ *
* @return the value of property to set or null if never set
*/
@Nullable public String getFailoverDataConnectorId() {
@@ -97,7 +104,7 @@ public class DataConnectorFactoryBean extends AbstractResolverPluginFactoryBean<
/**
* Data Connector property "failoverDataConnectorId".
- *
+ *
* @param id the value to set
*/
public void setFailoverDataConnectorId(@Nullable final String id) {
@@ -106,7 +113,7 @@ public class DataConnectorFactoryBean extends AbstractResolverPluginFactoryBean<
/**
* Data Connector property "noRetryDelay".
- *
+ *
* @return the value of property to set or null if never set
*/
@Nullable public Duration getNoRetryDelay() {
@@ -115,7 +122,7 @@ public class DataConnectorFactoryBean extends AbstractResolverPluginFactoryBean<
/**
* Data Connector property "noRetryDelay".
- *
+ *
* @param delay the value to set
*/
public void setNoRetryDelay(@Nullable final Duration delay) {
@@ -124,7 +131,7 @@ public class DataConnectorFactoryBean extends AbstractResolverPluginFactoryBean<
/**
* The resources to use.
- *
+ *
* @param theResources the resources to look at
*/
public void setResources(@Nonnull @NonnullElements final List<Resource> theResources) {
@@ -134,7 +141,7 @@ public class DataConnectorFactoryBean extends AbstractResolverPluginFactoryBean<
/**
* The resources to use.
- *
+ *
* @return the resources to look at
*/
@Nonnull @NonnullElements public List<Resource> getResources() {
@@ -142,8 +149,26 @@ public class DataConnectorFactoryBean extends AbstractResolverPluginFactoryBean<
}
/**
+ * Sets the list of attribute names to export during resolution.
+ *
+ * @param what the list
+ */
+ public void setExportAttributes(@Nonnull final Collection<String> what) {
+ exportAttributes = what;
+ }
+
+ /**
+ * Set whether we export all attributes.
+ *
+ * @param what whether we export all attributes
+ */
+ public void setExportAllAttributes(final boolean what) {
+ exportAllAttributes = what;
+ }
+
+ /**
* Set the list of bean factory post processors for this connector.
- *
+ *
* @param processors bean factory post processors to apply
*/
public void setBeanFactoryPostProcessors(@Nonnull @NonnullElements
@@ -152,8 +177,8 @@ public class DataConnectorFactoryBean extends AbstractResolverPluginFactoryBean<
}
/**
- * get the post processors.
- *
+ * Get the post processors.
+ *
* @return the bean factory post processors
*/
@Nonnull @NonnullElements public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {
@@ -162,7 +187,7 @@ public class DataConnectorFactoryBean extends AbstractResolverPluginFactoryBean<
/**
* Set the list of bean post processors for this connector.
- *
+ *
* @param processors bean post processors to apply
*/
public void setBeanPostProcessors(@Nonnull @NonnullElements final List<BeanPostProcessor> processors) {
@@ -171,7 +196,7 @@ public class DataConnectorFactoryBean extends AbstractResolverPluginFactoryBean<
/**
* Get the list of bean post processors for this connector.
- *
+ *
* @return processors bean post processors to apply
*/
@Nonnull @NonnullElements public List<BeanPostProcessor> getBeanPostProcessors() {
@@ -184,6 +209,11 @@ public class DataConnectorFactoryBean extends AbstractResolverPluginFactoryBean<
if (null != getFailoverDataConnectorId()) {
what.setFailoverDataConnectorId(getFailoverDataConnectorId());
}
+ if (null != exportAllAttributes) {
+ what.setExportAllAttributes(exportAllAttributes);
+ } else if (!exportAttributes.isEmpty()) {
+ what.setExportAttributes(exportAttributes);
+ }
}
/** {@inheritDoc} We do not allow non-singleton beans, if we did then we loose constructability. */
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/ComputedIdDataConnectorParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/ComputedIdDataConnectorParserTest.java
index cca7b0b..1b4f98e 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/ComputedIdDataConnectorParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/ComputedIdDataConnectorParserTest.java
@@ -18,6 +18,7 @@
package net.shibboleth.idp.attribute.resolver.spring.dc;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import org.springframework.context.support.GenericApplicationContext;
@@ -58,6 +59,7 @@ public class ComputedIdDataConnectorParserTest extends BaseAttributeDefinitionPa
@Test public void resolverDataConnector() throws ComponentInitializationException {
final PairwiseIdDataConnector connector = getDataConnector("resolver/computedDataConnector.xml", PairwiseIdDataConnector.class);
final ComputedPairwiseIdStore store = (ComputedPairwiseIdStore) connector.getPairwiseIdStore();
+ assertFalse(connector.isExportAllAttributes());
assertEquals(connector.getId(), "computed");
assertEquals(connector.getGeneratedAttributeId(), "jenny");
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/StaticDataConnectorParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/StaticDataConnectorParserTest.java
index fec1ba7..c9ad730 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/StaticDataConnectorParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/StaticDataConnectorParserTest.java
@@ -18,6 +18,7 @@
package net.shibboleth.idp.attribute.resolver.spring.dc;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.util.List;
@@ -38,7 +39,12 @@ public class StaticDataConnectorParserTest extends BaseAttributeDefinitionParser
@Test public void simple() {
final StaticDataConnector connector = getDataConnector("resolver/staticAttributes.xml", StaticDataConnector.class);
-
+
+ assertFalse(connector.isExportAllAttributes());
+ assertEquals(connector.getExportAttributes().size(), 2);
+ assertTrue(connector.getExportAttributes().contains("foo"));
+ assertTrue(connector.getExportAttributes().contains("bar"));
+
assertEquals(connector.getAttributes().keySet().size(), 2);
final IdPAttribute epe = connector.getAttributes().get("eduPersonEntitlement");
List<IdPAttributeValue> values = epe.getValues();
@@ -50,9 +56,10 @@ public class StaticDataConnectorParserTest extends BaseAttributeDefinitionParser
assertEquals(values.size(), 1);
assertTrue(values.contains(new StringAttributeValue("member")));
}
-
+
@Test public void hybrid() {
final StaticDataConnector connector = getDataConnector("resolver/staticAttributesHybrid.xml", StaticDataConnector.class);
+ assertTrue(connector.isExportAllAttributes());
assertEquals(connector.getAttributes().keySet().size(), 2);
final IdPAttribute epe = connector.getAttributes().get("eduPersonEntitlement");
@@ -70,6 +77,8 @@ public class StaticDataConnectorParserTest extends BaseAttributeDefinitionParser
@Test public void nativesimple() {
final StaticDataConnector connector = getDataConnector("staticAttributesNative.xml", StaticDataConnector.class);
+ assertFalse(connector.isExportAllAttributes());
+ assertTrue(connector.getExportAttributes().isEmpty());
assertEquals(connector.getAttributes().keySet().size(), 2);
final IdPAttribute epe = connector.getAttributes().get("eduPersonEntitlement");
List<IdPAttributeValue> values = epe.getValues();
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/HTTPDataConnectorParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/HTTPDataConnectorParserTest.java
index c1d9fea..7fdb32e 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/HTTPDataConnectorParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/HTTPDataConnectorParserTest.java
@@ -18,6 +18,7 @@
package net.shibboleth.idp.attribute.resolver.spring.dc.http;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
@@ -295,6 +296,10 @@ public class HTTPDataConnectorParserTest {
"net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-hybrid.xml",
"net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-spring-context.xml");
assertNotNull(connector);
+ assertFalse(connector.isExportAllAttributes());
+ assertEquals(connector.getExportAttributes().size(), 2);
+ assertTrue(connector.getExportAttributes().contains("foo"));
+ assertTrue(connector.getExportAttributes().contains("bar"));
final AttributeResolutionContext context =
TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-hybrid.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-hybrid.xml
index 881459c..f4dafbb 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-hybrid.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-hybrid.xml
@@ -6,6 +6,7 @@
<DataConnector id="myHTTP" xsi:type="HTTP"
httpClientRef="TrustEngineHttpClient"
executableSearchBuilderRef="template"
+ exportAttributes="foo bar"
certificateAuthority="%{certificateAuthority}"
mappingStrategyRef="mapping">
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/staticAttributes.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/staticAttributes.xml
index e056442..772a323 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/staticAttributes.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/staticAttributes.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<DataConnector id="staticAttributes" xsi:type="Static"
xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ exportAttributes="foo bar"
xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
<Attribute id="staticEpA">
<Value>member</Value>
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/staticAttributesHybrid.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/staticAttributesHybrid.xml
index 661871e..7cafd7b 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/staticAttributesHybrid.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/staticAttributesHybrid.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<DataConnector id="staticAttributes" xsi:type="Static"
+ exportAllAttributes="true"
xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
springResources="classpath:/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/staticAttributesHybridSpring.xml"/>
\ No newline at end of file
diff --git a/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd b/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
index 71f41b7..5d2943a 100644
--- a/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
+++ b/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
@@ -162,6 +162,16 @@
<documentation>Bean Name of a list of bean post processors.</documentation>
</annotation>
</attribute>
+ <attribute name="exportAllAttributes" type="string">
+ <annotation>
+ <documentation>Whether all attributes from this data connected are to be exported by the resolution process.</documentation>
+ </annotation>
+ </attribute>
+ <attribute name="exportAttributes" type="string">
+ <annotation>
+ <documentation>A list of attribute names to be exported by the resolution process from this data connector.</documentation>
+ </annotation>
+ </attribute>
</extension>
</complexContent>
</complexType>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list