[java-identity-provider] 05/27: Revamp subtype support while working on SAML 2 tests.
Scott Cantor
cantor.2 at osu.edu
Fri May 3 14:31:55 EDT 2019
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch feature/IDP-1434
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=147a0818c1350042aef54517ebce9587b4d3b532
commit 147a0818c1350042aef54517ebce9587b4d3b532
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Apr 10 20:57:10 2019 -0400
Revamp subtype support while working on SAML 2 tests.
---
.../transcoding/AbstractAttributeTranscoder.java | 15 --
.../attribute/transcoding/AttributeTranscoder.java | 4 +-
.../transcoding/AttributeTranscoderRegistry.java | 6 +
.../impl/AttributeTranscoderRegistryImpl.java | 95 ++++----
.../impl/AttributeTranscoderRegistryTest.java | 36 ++--
.../AbstractSAML2AttributeTranscoder.java | 9 +-
.../AbstractSAMLAttributeTranscoder.java | 59 +++--
.../impl/SAML2StringAttributeTranscoder.java | 6 +-
.../impl/SAML2StringAttributeEncoderTest.java | 238 +++++++++++++++++++++
9 files changed, 362 insertions(+), 106 deletions(-)
diff --git a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AbstractAttributeTranscoder.java b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AbstractAttributeTranscoder.java
index a333a1a..4ac568f 100644
--- a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AbstractAttributeTranscoder.java
+++ b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AbstractAttributeTranscoder.java
@@ -21,8 +21,6 @@ import java.util.function.Predicate;
import javax.annotation.Nonnull;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -68,17 +66,4 @@ public abstract class AbstractAttributeTranscoder<T> extends AbstractInitializab
activationCondition = Constraint.isNotNull(condition, "Activation condition cannot be null");
}
- /**
- * Checks if the given value can be handled by the transcoder.
- *
- * <p>In many cases this is simply a check to see if the given object is of the right type.</p>
- *
- * @param idpAttribute the attribute being encoded, never null
- * @param value the value to check, never null
- *
- * @return true if the transcoder can encode this value, false if not
- */
- protected abstract boolean canEncodeValue(@Nonnull final IdPAttribute idpAttribute,
- @Nonnull final IdPAttributeValue value);
-
}
\ No newline at end of file
diff --git a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoder.java b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoder.java
index c50eef1..8ced8a8 100644
--- a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoder.java
+++ b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoder.java
@@ -77,7 +77,7 @@ public interface AttributeTranscoder<T> {
*
* @throws AttributeEncodingException if unable to successfully encode attribute
*/
- @Nullable T encode(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nullable T encode(@Nullable final ProfileRequestContext profileRequestContext,
@Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends T> to,
@Nonnull final Properties properties) throws AttributeEncodingException;
@@ -92,7 +92,7 @@ public interface AttributeTranscoder<T> {
*
* @throws AttributeDecodingException if unable to successfully decode object
*/
- @Nullable IdPAttribute decode(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nullable IdPAttribute decode(@Nullable final ProfileRequestContext profileRequestContext,
@Nonnull final T input, @Nonnull final Properties properties) throws AttributeDecodingException;
}
\ No newline at end of file
diff --git a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoderRegistry.java b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoderRegistry.java
index 07dce7b..d378b06 100644
--- a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoderRegistry.java
+++ b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoderRegistry.java
@@ -44,6 +44,12 @@ public interface AttributeTranscoderRegistry extends IdentifiedComponent {
/** Property name for accessing {@link AttributeTranscoder} object to use. */
@Nonnull @NotEmpty static final String PROP_TRANSCODER = "transcoder";
+ /** Property name for whether a result with no values should be allowed, default false . */
+ @Nonnull @NotEmpty static final String PROP_ENCODE_NO_VALUES = "encodeNoValues";
+
+ /** Property name for whether a result with no values should be allowed, default true. */
+ @Nonnull @NotEmpty static final String PROP_DECODE_NO_VALUES = "decodeNoValues";
+
/**
* Obtains a set of instructions for encoding an input {@link IdPAttribute} into a target type.
*
diff --git a/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImpl.java b/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImpl.java
index 607901d..0fa1ab3 100644
--- a/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImpl.java
+++ b/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImpl.java
@@ -26,6 +26,7 @@ import java.util.Properties;
import java.util.function.Function;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import net.shibboleth.ext.spring.service.AbstractServiceableComponent;
@@ -62,14 +63,10 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
/** Registry of naming functions for supported object types. */
@Nonnull private final Map<Class<?>,Function<?,String>> namingFunctionRegistry;
- /** Maps acceptable subtypes of a given class into the proper base class to use during registry fuctions. */
- @Nonnull private final Map<Class<?>,Class<?>> classEquivalenceRegistry;
-
/** Constructor. */
public AttributeTranscoderRegistryImpl() {
transcodingRegistry = new HashMap<>();
namingFunctionRegistry = new HashMap<>();
- classEquivalenceRegistry = new HashMap<>();
}
/** {@inheritDoc} */
@@ -95,29 +92,6 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
}
});
}
-
- /**
- * Installs registry of mappings from subclasses of the "officially" registered types to their proper
- * official type so that the registry can ignore them.
- *
- * <p>For example, if a transcoder registry for type Foo also handles subtypes of Foo, those subtypes
- * should be registered with mappings to Foo.</p>
- *
- * @param registry mappings from subclass to the canonical parent class
- */
- public void setClassEquivalenceRegistry(@Nonnull @NonnullElements final Map<Class<?>,Class<?>> registry) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- if (registry == null) {
- classEquivalenceRegistry.clear();
- return;
- }
-
- registry.forEach((k,v) -> {
- if (k != null && v != null && v.isAssignableFrom(k)) {
- classEquivalenceRegistry.put(k, v);
- }
- });
- }
/**
* Installs the transcoder mappings en masse.
@@ -153,9 +127,19 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
Constraint.isNotNull(to, "Target type cannot be null");
final Multimap<Class<?>,Properties> propertyCollections = transcodingRegistry.get(from.getId());
+ if (propertyCollections == null) {
+ return Collections.emptyList();
+ }
- return propertyCollections != null ? ImmutableList.copyOf(propertyCollections.get(getEffectiveType(to)))
- : Collections.emptyList();
+ final Class<?> effectiveType = getEffectiveType(to);
+ if (effectiveType == null) {
+ log.warn("Unsupported object type: {}", to.getClass().getName());
+ return Collections.emptyList();
+ }
+
+ log.trace("Using rules for effective type {}", effectiveType.getName());
+
+ return ImmutableList.copyOf(propertyCollections.get(effectiveType));
}
/** {@inheritDoc} */
@@ -165,21 +149,24 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
Constraint.isNotNull(from, "Input object cannot be null");
final Class<?> effectiveType = getEffectiveType(from.getClass());
+ if (effectiveType == null) {
+ log.warn("Unsupported object type: {}", from.getClass().getName());
+ return Collections.emptyList();
+ }
- final Function<?,String> namingFunction = namingFunctionRegistry.get(from.getClass());
- if (namingFunction != null) {
- // Don't know if we can work around this cast or not.
- final String id = ((Function<? super T,String>) namingFunction).apply(from);
- if (id != null) {
- final Multimap<Class<?>,Properties> propertyCollections = transcodingRegistry.get(id);
-
- return propertyCollections != null ? ImmutableList.copyOf(propertyCollections.get(effectiveType))
- : Collections.emptyList();
- } else {
- log.warn("Object of type {} did not have a canonical name", from.getClass().getName());
- }
+ log.trace("Using rules for effective type {}", effectiveType.getName());
+
+ final Function<?,String> namingFunction = namingFunctionRegistry.get(effectiveType);
+
+ // Don't know if we can work around this cast or not.
+ final String id = ((Function<? super T,String>) namingFunction).apply(from);
+ if (id != null) {
+ final Multimap<Class<?>,Properties> propertyCollections = transcodingRegistry.get(id);
+
+ return propertyCollections != null ? ImmutableList.copyOf(propertyCollections.get(effectiveType))
+ : Collections.emptyList();
} else {
- log.warn("Unsupported object type: {}", from.getClass().getName());
+ log.warn("Object of type {} did not have a canonical name", from.getClass().getName());
}
return Collections.emptyList();
@@ -253,12 +240,28 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
*
* @param inputType the type passed into the registry operation
*
- * @return the appropriate type to use subsequently
+ * @return the appropriate type to use subsequently or null if not found
*/
- @Nonnull private Class<?> getEffectiveType(@Nonnull final Class<?> inputType) {
+ @Nullable private Class<?> getEffectiveType(@Nonnull final Class<?> inputType) {
+
+ // Walk the superclass tree.
+ Class<?> returnType = inputType;
+ while (returnType != null && !namingFunctionRegistry.containsKey(returnType)) {
+ returnType = returnType.getSuperclass();
+ }
+
+ if (returnType != null) {
+ return returnType;
+ }
+
+ for (final Class<?> iface : inputType.getInterfaces()) {
+ returnType = getEffectiveType(iface);
+ if (returnType != null) {
+ return returnType;
+ }
+ }
- final Class<?> outputType = classEquivalenceRegistry.get(inputType);
- return outputType != null ? outputType : inputType;
+ return null;
}
}
\ No newline at end of file
diff --git a/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryTest.java b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryTest.java
index 9dad889..edea506 100644
--- a/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryTest.java
+++ b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryTest.java
@@ -27,8 +27,8 @@ import java.util.Map;
import java.util.Properties;
import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import net.shibboleth.idp.attribute.AttributeDecodingException;
@@ -49,15 +49,13 @@ public class AttributeTranscoderRegistryTest {
private AttributeTranscoderRegistryImpl registry;
- @BeforeMethod public void setUp() throws ComponentInitializationException {
+ @BeforeClass public void setUp() throws ComponentInitializationException {
registry = new AttributeTranscoderRegistryImpl();
registry.setId("test");
registry.setNamingRegistry(Collections.singletonMap(
Pair.class, (Pair p) -> "{Pair}" + p.getFirst().toString()));
- registry.setClassEquivalenceRegistry(Collections.singletonMap(MyPair1.class, Pair.class));
-
final PairTranscoder transcoder = new PairTranscoder();
final Map<String,Collection<Properties>> mappings = new HashMap<>();
@@ -78,7 +76,7 @@ public class AttributeTranscoderRegistryTest {
registry.initialize();
}
- @AfterMethod public void tearDown() {
+ @AfterClass public void tearDown() {
registry.destroy();
registry = null;
}
@@ -88,8 +86,7 @@ public class AttributeTranscoderRegistryTest {
@Test public void testEncodeNoMappings() throws AttributeEncodingException {
Assert.assertTrue(registry.getTranscodingProperties(new IdPAttribute("frobnitz"), Pair.class).isEmpty());
- Assert.assertTrue(registry.getTranscodingProperties(new IdPAttribute("frobnitz"), MyPair1.class).isEmpty());
- Assert.assertTrue(registry.getTranscodingProperties(new IdPAttribute("foo"), MyPair2.class).isEmpty());
+ Assert.assertTrue(registry.getTranscodingProperties(new IdPAttribute("frobnitz"), MyPair.class).isEmpty());
Assert.assertTrue(registry.getTranscodingProperties(new IdPAttribute("foo"), String.class).isEmpty());
}
@@ -97,8 +94,7 @@ public class AttributeTranscoderRegistryTest {
@Test public void testDecodeNoMappings() throws AttributeDecodingException {
Assert.assertTrue(registry.getTranscodingProperties(new Pair("foo", "value")).isEmpty());
- Assert.assertTrue(registry.getTranscodingProperties(new MyPair1("foo", "value")).isEmpty());
- Assert.assertTrue(registry.getTranscodingProperties(new MyPair2("bar", "value")).isEmpty());
+ Assert.assertTrue(registry.getTranscodingProperties(new MyPair("foo", "value")).isEmpty());
Assert.assertTrue(registry.getTranscodingProperties(new String("bar")).isEmpty());
}
@@ -186,12 +182,12 @@ public class AttributeTranscoderRegistryTest {
final IdPAttribute foo = new IdPAttribute("foo");
foo.setValues(Collections.singletonList(StringAttributeValue.valueOf("value")));
- final List<MyPair1> pairs = new ArrayList<>();
+ final List<MyPair> pairs = new ArrayList<>();
- for (final Properties ruleset : registry.getTranscodingProperties(foo, MyPair1.class)) {
- final AttributeTranscoder<MyPair1> t =
+ for (final Properties ruleset : registry.getTranscodingProperties(foo, MyPair.class)) {
+ final AttributeTranscoder<MyPair> t =
(AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
- pairs.add(t.encode(null, foo, MyPair1.class, ruleset));
+ pairs.add(t.encode(null, foo, MyPair.class, ruleset));
}
Assert.assertEquals(pairs.size(), 2);
@@ -302,18 +298,12 @@ public class AttributeTranscoderRegistryTest {
Assert.assertTrue(attributes.get(1).getValues().isEmpty());
}
- /** Marker classes to exercise subtype support. */
+ /** Marker class to exercise subtype support. */
- public static class MyPair1 extends Pair {
- public MyPair1(Object one, Object two) {
+ public static class MyPair extends Pair {
+ public MyPair(Object one, Object two) {
super(one, two);
}
}
- public static class MyPair2 extends Pair {
- public MyPair2(Object one, Object two) {
- super(one, two);
- }
- }
-
}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAML2AttributeTranscoder.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAML2AttributeTranscoder.java
index acd3114..44e444d 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAML2AttributeTranscoder.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAML2AttributeTranscoder.java
@@ -92,8 +92,8 @@ public abstract class AbstractSAML2AttributeTranscoder<EncodedType extends IdPAt
/** {@inheritDoc} */
@Override
- @Nonnull protected Attribute buildAttribute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends Attribute> to,
+ @Nonnull protected Attribute buildAttribute(@Nullable final ProfileRequestContext profileRequestContext,
+ @Nullable final IdPAttribute attribute, @Nonnull final Class<? extends Attribute> to,
@Nonnull final Properties properties, @Nonnull @NonnullElements final List<XMLObject> attributeValues)
throws AttributeEncodingException {
@@ -119,7 +119,8 @@ public abstract class AbstractSAML2AttributeTranscoder<EncodedType extends IdPAt
samlAttribute.setNameFormat(properties.getProperty(PROP_NAME_FORMAT, Attribute.URI_REFERENCE));
samlAttribute.getAttributeValues().addAll(attributeValues);
- final String friendlyName = properties.getProperty(PROP_FRIENDLY_NAME, attribute.getId());
+ final String friendlyName = properties.getProperty(PROP_FRIENDLY_NAME,
+ attribute != null ? attribute.getId() : "");
if (!friendlyName.isBlank()) {
samlAttribute.setFriendlyName(friendlyName);
}
@@ -130,7 +131,7 @@ public abstract class AbstractSAML2AttributeTranscoder<EncodedType extends IdPAt
/** {@inheritDoc} */
@Override
@Nonnull protected IdPAttribute buildIdPAttribute(
- @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
+ @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
@Nonnull final Properties properties,
@Nonnull @NonnullElements final List<IdPAttributeValue<?>> attributeValues)
throws AttributeDecodingException {
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAMLAttributeTranscoder.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAMLAttributeTranscoder.java
index 464ef9f..c9ce061 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAMLAttributeTranscoder.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAMLAttributeTranscoder.java
@@ -30,6 +30,7 @@ import net.shibboleth.idp.attribute.AttributeEncodingException;
import net.shibboleth.idp.attribute.IdPAttribute;
import net.shibboleth.idp.attribute.IdPAttributeValue;
import net.shibboleth.idp.attribute.transcoding.AbstractAttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
@@ -68,7 +69,7 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
@Nonnull private final Logger log = LoggerFactory.getLogger(AbstractSAMLAttributeTranscoder.class);
/** {@inheritDoc} */
- @Nullable public AttributeType encode(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nullable public AttributeType encode(@Nullable final ProfileRequestContext profileRequestContext,
@Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends AttributeType> to,
@Nonnull final Properties properties) throws AttributeEncodingException {
ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
@@ -94,7 +95,7 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
if (!canEncodeValue(attribute, o)) {
log.warn("Skipping value of attribute '{}'; Type {} cannot be encoded by this encoder ({}).",
- attributeId, o.getClass().getName(), this.getClass().getName());
+ attributeId, o.getClass().getSimpleName(), this.getClass().getSimpleName());
continue;
}
@@ -102,18 +103,28 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
final XMLObject samlAttributeValue =
encodeValue(profileRequestContext, attribute, properties, attributeValue);
if (samlAttributeValue == null) {
- log.debug("Skipping empty value for attribute {}", attributeId);
+ log.debug("Skipping null value for attribute {}", attributeId);
} else {
samlAttributeValues.add(samlAttributeValue);
}
}
+
+ if (samlAttributeValues.isEmpty()) {
+ Object allowNoValues = properties.get(AttributeTranscoderRegistry.PROP_ENCODE_NO_VALUES);
+ if (!(allowNoValues instanceof Boolean)) {
+ allowNoValues = false;
+ }
+ if (! (Boolean) allowNoValues) {
+ throw new AttributeEncodingException("No values encoded for attribute " + attributeId);
+ }
+ }
log.debug("Completed encoding {} values for attribute {}", samlAttributeValues.size(), attributeId);
return buildAttribute(profileRequestContext, attribute, to, properties, samlAttributeValues);
}
/** {@inheritDoc} */
- @Nullable public IdPAttribute decode(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nullable public IdPAttribute decode(@Nullable final ProfileRequestContext profileRequestContext,
@Nonnull final AttributeType input, @Nonnull final Properties properties)
throws AttributeDecodingException {
ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
@@ -145,6 +156,16 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
idpAttributeValues.add(idpAttributeValue);
}
}
+
+ if (idpAttributeValues.isEmpty()) {
+ Object allowNoValues = properties.get(AttributeTranscoderRegistry.PROP_DECODE_NO_VALUES);
+ if (!(allowNoValues instanceof Boolean)) {
+ allowNoValues = true;
+ }
+ if (! (Boolean) allowNoValues) {
+ throw new AttributeDecodingException("No values decoded for attribute " + attributeName);
+ }
+ }
log.debug("Completed decoding {} values for attribute {}", idpAttributeValues.size(), attributeName);
return buildIdPAttribute(profileRequestContext, input, properties, idpAttributeValues);
@@ -201,11 +222,24 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
}
if (null == retVal) {
- log.info("Value of type {} could not be converted", object.getClass().toString());
+ log.info("Value of type {} could not be converted", object.getClass().getSimpleName());
}
return retVal;
}
// Checkstyle: CyclomaticComplexity ON
+
+ /**
+ * Checks if the given value can be handled by the transcoder.
+ *
+ * <p>In many cases this is simply a check to see if the given object is of the right type.</p>
+ *
+ * @param idpAttribute the attribute being encoded, never null
+ * @param value the value to check, never null
+ *
+ * @return true if the transcoder can encode this value, false if not
+ */
+ protected abstract boolean canEncodeValue(@Nonnull final IdPAttribute idpAttribute,
+ @Nonnull final IdPAttributeValue value);
/**
* Builds a SAML attribute element from the given attribute values.
@@ -220,10 +254,10 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
*
* @throws AttributeEncodingException thrown if there is a problem constructing the SAML attribute
*/
- @Nonnull protected abstract AttributeType buildAttribute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends AttributeType> to,
- @Nonnull final Properties properties, @Nonnull @NonnullElements final List<XMLObject> attributeValues)
- throws AttributeEncodingException;
+ @Nonnull protected abstract AttributeType buildAttribute(
+ @Nullable final ProfileRequestContext profileRequestContext, @Nullable final IdPAttribute attribute,
+ @Nonnull final Class<? extends AttributeType> to, @Nonnull final Properties properties,
+ @Nonnull @NonnullElements final List<XMLObject> attributeValues) throws AttributeEncodingException;
/**
* Encodes an attribute value into a SAML AttributeValue element.
@@ -237,7 +271,7 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
*
* @throws AttributeEncodingException thrown if there is a problem encoding the attribute value
*/
- @Nullable protected abstract XMLObject encodeValue(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nullable protected abstract XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
@Nonnull final IdPAttribute attribute, @Nonnull final Properties properties,
@Nonnull final EncodedType value) throws AttributeEncodingException;
@@ -254,7 +288,7 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
* @throws AttributeDecodingException thrown if there is a problem constructing the IdPAttribute
*/
@Nonnull protected abstract IdPAttribute buildIdPAttribute(
- @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final AttributeType attribute,
+ @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final AttributeType attribute,
@Nonnull final Properties properties,
@Nonnull @NonnullElements final List<IdPAttributeValue<?>> attributeValues)
throws AttributeDecodingException;
@@ -268,7 +302,6 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
*/
@Nonnull protected abstract Iterable<XMLObject> getValues(@Nonnull final AttributeType input);
-
/**
* Function to decode a single {@link XMLObject} into an {@link IdPAttributeValue}.
*
@@ -280,7 +313,7 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
* @return the returned final {@link IdPAttributeValue} or null if decoding failed
*/
@Nullable protected abstract IdPAttributeValue<?> decodeValue(
- @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final AttributeType attribute,
+ @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final AttributeType attribute,
@Nonnull final Properties properties, @Nullable final XMLObject value);
}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoder.java
index 420c308..dd6cfbd 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoder.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoder.java
@@ -54,13 +54,13 @@ public class SAML2StringAttributeTranscoder extends AbstractSAML2AttributeTransc
}
/** {@inheritDoc} */
- @Override @Nullable protected XMLObject encodeValue(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Override @Nullable protected XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
@Nonnull final IdPAttribute attribute, @Nonnull final Properties properties,
@Nonnull final StringAttributeValue value) throws AttributeEncodingException {
if (value instanceof LocalizedStringAttributeValue || value instanceof ScopedStringAttributeValue) {
log.warn("Attribute '{}': Lossy encoding of attribute value of type {} to SAML2 String Attribute",
- attribute.getId(), value.getClass().getName());
+ attribute.getId(), value.getClass().getSimpleName());
}
final Object encodeType = properties.getOrDefault(PROP_ENCODE_TYPE, Boolean.TRUE);
@@ -72,7 +72,7 @@ public class SAML2StringAttributeTranscoder extends AbstractSAML2AttributeTransc
/** {@inheritDoc} */
@Override @Nullable protected IdPAttributeValue<?> decodeValue(
- @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
+ @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
@Nonnull final Properties properties, @Nullable final XMLObject value) {
return value != null ? StringAttributeValue.valueOf(getStringValue(value)) : null;
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeEncoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeEncoderTest.java
new file mode 100644
index 0000000..b41146a
--- /dev/null
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeEncoderTest.java
@@ -0,0 +1,238 @@
+/*
+ * 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.idp.saml.attribute.transcoding.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.ByteAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAMLAttributeTranscoder;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.opensaml.core.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeValue;
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/** {@link SAML2StringAttributeTranscoder} unit test. */
+public class SAML2StringAttributeEncoderTest extends OpenSAMLInitBaseTestCase {
+
+ private AttributeTranscoderRegistryImpl registry;
+
+ @Nonnull private SAMLObjectBuilder<Attribute> attributeBuilder;
+
+ @Nonnull private SAMLObjectBuilder<RequestedAttribute> reqAttributeBuilder;
+
+ private final static String ATTR_NAME = "foo";
+ private final static String ATTR_NAMEFORMAT = "Namespace";
+ private final static String ATTR_FRIENDLYNAME = "friendly";
+ private final static String STRING_1 = "Value The First";
+ private final static String STRING_2 = "Second string the value is";
+
+ @BeforeClass public void setUp() throws ComponentInitializationException {
+
+ attributeBuilder = (SAMLObjectBuilder<Attribute>)
+ XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
+ Attribute.TYPE_NAME);
+ reqAttributeBuilder = (SAMLObjectBuilder<RequestedAttribute>)
+ XMLObjectProviderRegistrySupport.getBuilderFactory().<RequestedAttribute>getBuilderOrThrow(
+ RequestedAttribute.TYPE_NAME);
+
+ registry = new AttributeTranscoderRegistryImpl();
+ registry.setId("test");
+
+ registry.setNamingRegistry(Collections.singletonMap(
+ Attribute.class, new AbstractSAML2AttributeTranscoder.NamingFunction()));
+
+ final SAML2StringAttributeTranscoder transcoder = new SAML2StringAttributeTranscoder();
+ transcoder.initialize();
+
+ final Map<String,Collection<Properties>> mappings = new HashMap<>();
+
+ final Properties ruleset1 = new Properties();
+ ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+ ruleset1.put(AbstractSAMLAttributeTranscoder.PROP_ENCODE_TYPE, true);
+ ruleset1.setProperty(AbstractSAMLAttributeTranscoder.PROP_NAME, ATTR_NAME);
+ ruleset1.setProperty(AbstractSAML2AttributeTranscoder.PROP_NAME_FORMAT, ATTR_NAMEFORMAT);
+ ruleset1.setProperty(AbstractSAML2AttributeTranscoder.PROP_FRIENDLY_NAME, ATTR_FRIENDLYNAME);
+
+ mappings.put(ATTR_NAME, Collections.singletonList(ruleset1));
+
+ registry.setTranscoderRegistry(mappings);
+
+ registry.initialize();
+ }
+
+ @AfterClass public void tearDown() {
+ registry.destroy();
+ registry = null;
+ }
+
+ @Test(expectedExceptions = {AttributeEncodingException.class,}) public void emptyEncode() throws Exception {
+ final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+
+ final Collection<Properties> rulesets = registry.getTranscodingProperties(inputAttribute, Attribute.class);
+ Assert.assertEquals(rulesets.size(), 1);
+ final Properties ruleset = rulesets.iterator().next();
+
+ final Attribute attr =
+ ((AttributeTranscoder<Attribute>) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER)).encode(
+ null, inputAttribute, Attribute.class, ruleset);
+
+ Assert.assertNotNull(attr);
+ Assert.assertEquals(attr.getName(), ATTR_NAME);
+ Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+ Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+ Assert.assertTrue(attr.getAttributeValues().isEmpty());
+ }
+
+ @Test public void emptyDecode() throws Exception {
+
+ final Attribute samlAttribute = attributeBuilder.buildObject();
+ samlAttribute.setName(ATTR_NAME);
+ samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+
+ final Collection<Properties> rulesets = registry.getTranscodingProperties(samlAttribute);
+ Assert.assertEquals(rulesets.size(), 1);
+ final Properties ruleset = rulesets.iterator().next();
+
+ final IdPAttribute attr =
+ ((AttributeTranscoder<Attribute>) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER)).decode(
+ null, samlAttribute, ruleset);
+
+ Assert.assertNotNull(attr);
+ Assert.assertEquals(attr.getId(), ATTR_NAME);
+ Assert.assertTrue(attr.getValues().isEmpty());
+ }
+
+ @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
+ final int[] intArray = {1, 2, 3, 4};
+ final Collection<? extends IdPAttributeValue<?>> values =
+ Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new IdPAttributeValue<Object>() {
+ @Override
+ public Object getValue() {
+ return intArray;
+ }
+ @Override
+ public String getDisplayValue() {
+ return intArray.toString();
+ }
+ });
+
+ final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+ inputAttribute.setValues(values);
+
+ final Collection<Properties> rulesets = registry.getTranscodingProperties(inputAttribute, Attribute.class);
+ Assert.assertEquals(rulesets.size(), 1);
+ final Properties ruleset = rulesets.iterator().next();
+
+ ((AttributeTranscoder<Attribute>) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER)).encode(
+ null, inputAttribute, Attribute.class, ruleset);
+ }
+
+ @Test public void single() throws Exception {
+ final Collection<? extends IdPAttributeValue<?>> values =
+ Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue(STRING_1));
+
+ final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+ inputAttribute.setValues(values);
+
+ final Collection<Properties> rulesets = registry.getTranscodingProperties(inputAttribute, Attribute.class);
+ Assert.assertEquals(rulesets.size(), 1);
+ final Properties ruleset = rulesets.iterator().next();
+
+ final Attribute attr =
+ ((AttributeTranscoder<Attribute>) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER)).encode(
+ null, inputAttribute, Attribute.class, ruleset);
+
+ Assert.assertNotNull(attr);
+ Assert.assertEquals(attr.getName(), ATTR_NAME);
+ Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+ Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+
+ final List<XMLObject> children = attr.getOrderedChildren();
+
+ Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+ final XMLObject child = children.get(0);
+
+ Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+ "Attribute Value not inside <AttributeValue/>");
+
+ Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
+
+ final XSString childAsString = (XSString) child;
+
+ Assert.assertEquals(childAsString.getValue(), STRING_1);
+ }
+
+ @Test public void multi() throws Exception {
+ final Collection<? extends IdPAttributeValue<?>> values =
+ Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
+ new StringAttributeValue(STRING_1),
+ new StringAttributeValue(STRING_2),
+ new ScopedStringAttributeValue(STRING_1, STRING_2));
+
+ final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+ inputAttribute.setValues(values);
+
+ final Collection<Properties> rulesets = registry.getTranscodingProperties(inputAttribute, Attribute.class);
+ Assert.assertEquals(rulesets.size(), 1);
+ final Properties ruleset = rulesets.iterator().next();
+
+ final Attribute attr =
+ ((AttributeTranscoder<Attribute>) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER)).encode(
+ null, inputAttribute, Attribute.class, ruleset);
+
+ Assert.assertNotNull(attr);
+
+ final List<XMLObject> children = attr.getOrderedChildren();
+ Assert.assertEquals(children.size(), 3, "Encoding three entries");
+
+ for (final XMLObject child: children) {
+ Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
+ final String childAsString = ((XSString) children.get(0)).getValue();
+ Assert.assertTrue(STRING_1.equals(childAsString)||STRING_2.equals(childAsString));
+ }
+ }
+
+}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list