[java-identity-provider] 04/27: Next checkpoint.
Scott Cantor
cantor.2 at osu.edu
Fri May 3 14:31:54 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=733ec43a182bcb573f5766f1eca3eebdc884793e
commit 733ec43a182bcb573f5766f1eca3eebdc884793e
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Apr 10 17:51:55 2019 -0400
Next checkpoint.
---
.../attribute/transcoding/AttributeTranscoder.java | 12 +-
.../transcoding/AttributeTranscoderRegistry.java | 3 -
.../impl/AttributeTranscoderRegistryImpl.java | 74 +++++++----
.../impl/AttributeTranscoderRegistryTest.java | 136 +++++++++------------
.../attribute/transcoding/impl/PairTranscoder.java | 27 ++--
.../src/main/resources/conf/attribute-registry.xml | 1 -
.../AbstractSAML2AttributeTranscoder.java | 80 ++++++++++--
.../AbstractSAMLAttributeTranscoder.java | 116 +++++++++++++-----
.../impl/SAML2StringAttributeTranscoder.java | 8 +-
9 files changed, 300 insertions(+), 157 deletions(-)
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 2572616..c50eef1 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
@@ -48,6 +48,13 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
public interface AttributeTranscoder<T> {
/**
+ * Get the class representing the type of object supported by this transcoder.
+ *
+ * @return object type supported
+ */
+ @Nonnull Class<T> getEncodedType();
+
+ /**
* Get the name of the encoded object that would be created by a given set of
* instructions.
*
@@ -63,6 +70,7 @@ public interface AttributeTranscoder<T> {
*
* @param profileRequestContext current profile request context
* @param attribute the attribute to encode
+ * @param to specific type of object to encode
* @param properties properties governing the encoding process, principally the resulting object's naming
*
* @return the Object the attribute was encoded into
@@ -70,8 +78,8 @@ public interface AttributeTranscoder<T> {
* @throws AttributeEncodingException if unable to successfully encode attribute
*/
@Nullable T encode(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final IdPAttribute attribute, @Nonnull final Properties properties)
- throws AttributeEncodingException;
+ @Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends T> to,
+ @Nonnull final Properties properties) throws AttributeEncodingException;
/**
* Decode the supplied object into a protocol-neutral representation.
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 756c0ab..07dce7b 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
@@ -41,9 +41,6 @@ public interface AttributeTranscoderRegistry extends IdentifiedComponent {
/** Property name for accessing the name of the {@link IdPAttribute} to decode into. */
@Nonnull @NotEmpty static final String PROP_ID = "id";
- /** Property name for accessing the class of object supported by a given ruleset. */
- @Nonnull @NotEmpty static final String PROP_TYPE = "type";
-
/** Property name for accessing {@link AttributeTranscoder} object to use. */
@Nonnull @NotEmpty static final String PROP_TRANSCODER = "transcoder";
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 24d8e1f..607901d 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
@@ -61,11 +61,15 @@ 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} */
@@ -91,9 +95,32 @@ 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);
+ }
+ });
+ }
/**
- * Install the transcoder mappings en masse.
+ * Installs the transcoder mappings en masse.
*
* <p>Each map entry connects an {@link IdPAttribute} name to the rules for transcoding to/from it.</p>
*
@@ -127,7 +154,7 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
final Multimap<Class<?>,Properties> propertyCollections = transcodingRegistry.get(from.getId());
- return propertyCollections != null ? ImmutableList.copyOf(propertyCollections.get(to))
+ return propertyCollections != null ? ImmutableList.copyOf(propertyCollections.get(getEffectiveType(to)))
: Collections.emptyList();
}
@@ -137,14 +164,16 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
Constraint.isNotNull(from, "Input object cannot be null");
+ final Class<?> effectiveType = getEffectiveType(from.getClass());
+
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<T,String>) namingFunction).apply(from);
+ 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(from.getClass()))
+ 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());
@@ -169,20 +198,8 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
* @param ruleset transcoding rules
*/
private void addMapping(@Nonnull @NotEmpty final String id, @Nonnull final Properties ruleset) {
- Object type = ruleset.get(PROP_TYPE);
+
Object transcoder = ruleset.get(PROP_TRANSCODER);
-
- if (type instanceof String) {
- try {
- type = Class.forName((String) type);
- } catch (final ClassNotFoundException e) {
- log.warn("Target class type {} not found in transcoding rule for {}", type, id);
- return;
- }
- } else if (type == null) {
- log.warn("Transcoding rule for {} missing {} property", id, PROP_TYPE);
- }
-
if (transcoder instanceof String) {
try {
transcoder = Class.forName((String) transcoder).getDeclaredConstructor().newInstance();
@@ -197,6 +214,7 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
log.warn("Transcoding rule for {} missing {} property", id, PROP_TRANSCODER);
}
+ final Class<?> type = ((AttributeTranscoder) transcoder).getEncodedType();
final String targetName = ((AttributeTranscoder) transcoder).getEncodedName(ruleset);
if (targetName != null) {
@@ -204,7 +222,6 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
copy.putAll(ruleset);
copy.put(PROP_TRANSCODER, transcoder);
- copy.put(PROP_TYPE, type);
// Install mapping back to IdPAttribute's name.
copy.setProperty(PROP_ID, id);
@@ -215,7 +232,7 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
transcodingRegistry.put(id, rulesetsForIdPName);
}
- rulesetsForIdPName.put((Class) type, copy);
+ rulesetsForIdPName.put(type, copy);
Multimap<Class<?>,Properties> rulesetsForEncodedName = transcodingRegistry.get(targetName);
if (rulesetsForEncodedName == null) {
@@ -223,12 +240,25 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
transcodingRegistry.put(targetName, rulesetsForEncodedName);
}
- rulesetsForEncodedName.put((Class) type, copy);
+ rulesetsForEncodedName.put(type, copy);
} else {
- log.warn("Transcoding rule for {} into type {} did not produce an encoded name",
- id, ((Class) type).getName());
+ log.warn("Transcoding rule for {} into type {} did not produce an encoded name", id, type.getName());
}
}
+ /**
+ * Convert an input type into the appropriate type (possibly itself) to use in looking up
+ * rules in the registry.
+ *
+ * @param inputType the type passed into the registry operation
+ *
+ * @return the appropriate type to use subsequently
+ */
+ @Nonnull private Class<?> getEffectiveType(@Nonnull final Class<?> inputType) {
+
+ final Class<?> outputType = classEquivalenceRegistry.get(inputType);
+ return outputType != null ? outputType : inputType;
+ }
+
}
\ 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 689c036..9dad889 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
@@ -26,7 +26,6 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
-import org.opensaml.profile.context.ProfileRequestContext;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
@@ -36,15 +35,12 @@ import net.shibboleth.idp.attribute.AttributeDecodingException;
import net.shibboleth.idp.attribute.AttributeEncodingException;
import net.shibboleth.idp.attribute.EmptyAttributeValue;
import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.attribute.transcoding.AbstractAttributeTranscoder;
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.utilities.java.support.collection.Pair;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
/**
* Test for {@link AttributeTranscoderRegistry}.
@@ -60,17 +56,17 @@ public class AttributeTranscoderRegistryTest {
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<>();
final Properties ruleset1 = new Properties();
- ruleset1.put(AttributeTranscoderRegistry.PROP_TYPE, Pair.class);
ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
ruleset1.setProperty("name", "bar");
final Properties ruleset2 = new Properties();
- ruleset2.put(AttributeTranscoderRegistry.PROP_TYPE, "net.shibboleth.utilities.java.support.collection.Pair");
ruleset2.put(AttributeTranscoderRegistry.PROP_TRANSCODER, "net.shibboleth.idp.attribute.transcoding.impl.PairTranscoder");
ruleset2.setProperty("name", "baz");
@@ -92,6 +88,8 @@ 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("foo"), String.class).isEmpty());
}
@@ -99,18 +97,20 @@ public class AttributeTranscoderRegistryTest {
@Test public void testDecodeNoMappings() throws AttributeDecodingException {
Assert.assertTrue(registry.getTranscodingProperties(new Pair("foo", "value")).isEmpty());
- Assert.assertTrue(registry.getTranscodingProperties(new String("foo")).isEmpty());
+ Assert.assertTrue(registry.getTranscodingProperties(new MyPair1("foo", "value")).isEmpty());
+ Assert.assertTrue(registry.getTranscodingProperties(new MyPair2("bar", "value")).isEmpty());
+ Assert.assertTrue(registry.getTranscodingProperties(new String("bar")).isEmpty());
}
@Test public void testEncodeNoValues() throws AttributeEncodingException {
final IdPAttribute foo = new IdPAttribute("foo");
- final List<Pair<String,Object>> pairs = new ArrayList<>();
+ final List<Pair> pairs = new ArrayList<>();
for (final Properties ruleset : registry.getTranscodingProperties(foo, Pair.class)) {
- final AttributeTranscoder<Pair<String,Object>> t =
+ final AttributeTranscoder<Pair> t =
(AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
- pairs.add(t.encode(null, foo, ruleset));
+ pairs.add(t.encode(null, foo, Pair.class, ruleset));
}
Assert.assertEquals(pairs.size(), 2);
@@ -124,12 +124,12 @@ public class AttributeTranscoderRegistryTest {
@Test public void testDecodeOneNoValues() throws AttributeDecodingException {
- final Pair<String,Object> bar = new Pair("bar", null);
+ final Pair bar = new Pair("bar", null);
final List<IdPAttribute> attributes = new ArrayList<>();
for (final Properties ruleset : registry.getTranscodingProperties(bar)) {
- final AttributeTranscoder<Pair<String,Object>> t =
+ final AttributeTranscoder<Pair> t =
(AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
attributes.add(t.decode(null, bar, ruleset));
}
@@ -142,12 +142,12 @@ public class AttributeTranscoderRegistryTest {
@Test public void testDecodeTwoNoValues() throws AttributeDecodingException {
- final Pair<String,Object> baz = new Pair("baz", null);
+ final Pair baz = new Pair("baz", null);
final List<IdPAttribute> attributes = new ArrayList<>();
for (final Properties ruleset : registry.getTranscodingProperties(baz)) {
- final AttributeTranscoder<Pair<String,Object>> t =
+ final AttributeTranscoder<Pair> t =
(AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
attributes.add(t.decode(null, baz, ruleset));
}
@@ -165,12 +165,33 @@ public class AttributeTranscoderRegistryTest {
final IdPAttribute foo = new IdPAttribute("foo");
foo.setValues(Collections.singletonList(StringAttributeValue.valueOf("value")));
- final List<Pair<String,Object>> pairs = new ArrayList<>();
+ final List<Pair> pairs = new ArrayList<>();
for (final Properties ruleset : registry.getTranscodingProperties(foo, Pair.class)) {
- final AttributeTranscoder<Pair<String,Object>> t =
+ final AttributeTranscoder<Pair> t =
(AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
- pairs.add(t.encode(null, foo, ruleset));
+ pairs.add(t.encode(null, foo, Pair.class, ruleset));
+ }
+
+ Assert.assertEquals(pairs.size(), 2);
+
+ Assert.assertEquals(pairs.get(0).getFirst(), "bar");
+ Assert.assertEquals(pairs.get(0).getSecond(), "value");
+
+ Assert.assertEquals(pairs.get(1).getFirst(), "baz");
+ Assert.assertEquals(pairs.get(1).getSecond(), "value");
+ }
+
+ @Test public void testEncodeSubtypeStringValues() throws AttributeEncodingException {
+ final IdPAttribute foo = new IdPAttribute("foo");
+ foo.setValues(Collections.singletonList(StringAttributeValue.valueOf("value")));
+
+ final List<MyPair1> pairs = new ArrayList<>();
+
+ for (final Properties ruleset : registry.getTranscodingProperties(foo, MyPair1.class)) {
+ final AttributeTranscoder<MyPair1> t =
+ (AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
+ pairs.add(t.encode(null, foo, MyPair1.class, ruleset));
}
Assert.assertEquals(pairs.size(), 2);
@@ -184,12 +205,12 @@ public class AttributeTranscoderRegistryTest {
@Test public void testDecodeOneStringValues() throws AttributeDecodingException {
- final Pair<String,Object> bar = new Pair("bar", "value");
+ final Pair bar = new Pair("bar", "value");
final List<IdPAttribute> attributes = new ArrayList<>();
for (final Properties ruleset : registry.getTranscodingProperties(bar)) {
- final AttributeTranscoder<Pair<String,Object>> t =
+ final AttributeTranscoder<Pair> t =
(AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
attributes.add(t.decode(null, bar, ruleset));
}
@@ -202,12 +223,12 @@ public class AttributeTranscoderRegistryTest {
@Test public void testDecodeTwoStringValues() throws AttributeDecodingException {
- final Pair<String,Object> baz = new Pair("baz", "value");
+ final Pair baz = new Pair("baz", "value");
final List<IdPAttribute> attributes = new ArrayList<>();
for (final Properties ruleset : registry.getTranscodingProperties(baz)) {
- final AttributeTranscoder<Pair<String,Object>> t =
+ final AttributeTranscoder<Pair> t =
(AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
attributes.add(t.decode(null, baz, ruleset));
}
@@ -225,12 +246,12 @@ public class AttributeTranscoderRegistryTest {
final IdPAttribute foo = new IdPAttribute("foo");
foo.setValues(Collections.singletonList(EmptyAttributeValue.ZERO_LENGTH));
- final List<Pair<String,Object>> pairs = new ArrayList<>();
+ final List<Pair> pairs = new ArrayList<>();
for (final Properties ruleset : registry.getTranscodingProperties(foo, Pair.class)) {
- final AttributeTranscoder<Pair<String,Object>> t =
+ final AttributeTranscoder<Pair> t =
(AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
- pairs.add(t.encode(null, foo, ruleset));
+ pairs.add(t.encode(null, foo, Pair.class, ruleset));
}
Assert.assertEquals(pairs.size(), 2);
@@ -244,12 +265,12 @@ public class AttributeTranscoderRegistryTest {
@Test public void testDecodeOneUnsupportedValues() throws AttributeDecodingException {
- final Pair<String,Object> bar = new Pair("bar", 0L);
+ final Pair bar = new Pair("bar", 0L);
final List<IdPAttribute> attributes = new ArrayList<>();
for (final Properties ruleset : registry.getTranscodingProperties(bar)) {
- final AttributeTranscoder<Pair<String,Object>> t =
+ final AttributeTranscoder<Pair> t =
(AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
attributes.add(t.decode(null, bar, ruleset));
}
@@ -262,12 +283,12 @@ public class AttributeTranscoderRegistryTest {
@Test public void testDecodeTwoUnsupportedValues() throws AttributeDecodingException {
- final Pair<String,Object> baz = new Pair("baz", 0L);
+ final Pair baz = new Pair("baz", 0L);
final List<IdPAttribute> attributes = new ArrayList<>();
for (final Properties ruleset : registry.getTranscodingProperties(baz)) {
- final AttributeTranscoder<Pair<String,Object>> t =
+ final AttributeTranscoder<Pair> t =
(AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
attributes.add(t.decode(null, baz, ruleset));
}
@@ -281,55 +302,18 @@ public class AttributeTranscoderRegistryTest {
Assert.assertTrue(attributes.get(1).getValues().isEmpty());
}
- public class PairTranscoder extends AbstractAttributeTranscoder<Pair<String,Object>> {
-
- /** {@inheritDoc} */
- public String getEncodedName(Properties properties) {
- if (properties.containsKey("name")) {
- return "{Pair}" + properties.getProperty("name");
- } else {
- return null;
- }
- }
-
- /** {@inheritDoc} */
- public Pair<String,Object> encode(ProfileRequestContext profileRequestContext, IdPAttribute attribute, Properties properties)
- throws AttributeEncodingException {
-
- final String name = StringSupport.trimOrNull(properties.getProperty("name"));
- if (name == null) {
- throw new AttributeEncodingException("No name property");
- }
-
- if (attribute.getValues().isEmpty() || !canEncodeValue(attribute, attribute.getValues().get(0))) {
- return new Pair<>(name, null);
- } else {
- return new Pair<>(name, attribute.getValues().get(0).getValue());
- }
- }
-
- /** {@inheritDoc} */
- public IdPAttribute decode(ProfileRequestContext profileRequestContext, Pair<String,Object> input, Properties properties)
- throws AttributeDecodingException {
-
- final String id = StringSupport.trimOrNull(properties.getProperty(AttributeTranscoderRegistry.PROP_ID));
- if (id == null) {
- throw new AttributeDecodingException("No id property");
- }
-
- final IdPAttribute idattr = new IdPAttribute(id);
-
- if (input.getSecond() instanceof String) {
- idattr.setValues(Collections.singletonList(StringAttributeValue.valueOf((String) input.getSecond())));
- }
-
- return idattr;
+ /** Marker classes to exercise subtype support. */
+
+ public static class MyPair1 extends Pair {
+ public MyPair1(Object one, Object two) {
+ super(one, two);
}
+ }
- /** {@inheritDoc} */
- protected boolean canEncodeValue(IdPAttribute idpAttribute, IdPAttributeValue value) {
- return value instanceof StringAttributeValue;
- }
+ 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-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/PairTranscoder.java b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/PairTranscoder.java
index 9cb0384..e38287a 100644
--- a/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/PairTranscoder.java
+++ b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/PairTranscoder.java
@@ -17,6 +17,7 @@
package net.shibboleth.idp.attribute.transcoding.impl;
+import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.Properties;
@@ -35,7 +36,12 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
/**
* Sample transcoder for tests.
*/
-public class PairTranscoder extends AbstractAttributeTranscoder<Pair<String,Object>> {
+public class PairTranscoder extends AbstractAttributeTranscoder<Pair> {
+
+ /** {@inheritDoc} */
+ public Class<Pair> getEncodedType() {
+ return Pair.class;
+ }
/** {@inheritDoc} */
public String getEncodedName(Properties properties) {
@@ -47,23 +53,28 @@ public class PairTranscoder extends AbstractAttributeTranscoder<Pair<String,Obje
}
/** {@inheritDoc} */
- public Pair<String,Object> encode(ProfileRequestContext profileRequestContext, IdPAttribute attribute, Properties properties)
+ public Pair encode(ProfileRequestContext profileRequestContext, IdPAttribute attribute, Class<? extends Pair> to, Properties properties)
throws AttributeEncodingException {
final String name = StringSupport.trimOrNull(properties.getProperty("name"));
if (name == null) {
throw new AttributeEncodingException("No name property");
}
-
- if (attribute.getValues().isEmpty() || !canEncodeValue(attribute, attribute.getValues().get(0))) {
- return new Pair<>(name, null);
- } else {
- return new Pair<>(name, attribute.getValues().get(0).getValue());
+
+ try {
+ if (attribute.getValues().isEmpty() || !canEncodeValue(attribute, attribute.getValues().get(0))) {
+ return to.getDeclaredConstructor(Object.class, Object.class).newInstance(name, null);
+ } else {
+ return to.getDeclaredConstructor(Object.class, Object.class).newInstance(name, attribute.getValues().get(0).getValue());
+ }
+ } catch (final InstantiationException | IllegalAccessException | IllegalArgumentException
+ | InvocationTargetException | NoSuchMethodException | SecurityException e) {
+ throw new AttributeEncodingException(e);
}
}
/** {@inheritDoc} */
- public IdPAttribute decode(ProfileRequestContext profileRequestContext, Pair<String,Object> input, Properties properties)
+ public IdPAttribute decode(ProfileRequestContext profileRequestContext, Pair input, Properties properties)
throws AttributeDecodingException {
final String id = StringSupport.trimOrNull(properties.getProperty(AttributeTranscoderRegistry.PROP_ID));
diff --git a/idp-conf/src/main/resources/conf/attribute-registry.xml b/idp-conf/src/main/resources/conf/attribute-registry.xml
index e1eafb1..2b3b203 100644
--- a/idp-conf/src/main/resources/conf/attribute-registry.xml
+++ b/idp-conf/src/main/resources/conf/attribute-registry.xml
@@ -25,7 +25,6 @@
<entry key="uid">
<map>
- <entry key="type" value-ref="SAML2.Attribute" />
<entry key="transcoder" value-ref="SAML2StringTranscoder" />
<entry key="name" value="urn:oid:0.9.2342.19200300.100.1.1" />
</map>
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 b5f1f2a..acd3114 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
@@ -25,18 +25,21 @@ import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import net.shibboleth.idp.attribute.AttributeDecodingException;
import net.shibboleth.idp.attribute.AttributeEncodingException;
import net.shibboleth.idp.attribute.IdPAttribute;
import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
+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.logic.ConstraintViolationException;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.saml.common.SAMLObjectBuilder;
import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
import com.google.common.base.Strings;
@@ -57,23 +60,31 @@ public abstract class AbstractSAML2AttributeTranscoder<EncodedType extends IdPAt
/** Builder used to construct {@link Attribute} objects. */
@Nonnull private final SAMLObjectBuilder<Attribute> attributeBuilder;
+ /** Builder used to construct {@link RequestedAttribute} objects. */
+ @Nonnull private final SAMLObjectBuilder<RequestedAttribute> reqAttributeBuilder;
+
/** Constructor. */
public AbstractSAML2AttributeTranscoder() {
- attributeBuilder =
- (SAMLObjectBuilder<Attribute>) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(
+ attributeBuilder = (SAMLObjectBuilder<Attribute>)
+ XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
Attribute.TYPE_NAME);
- if (attributeBuilder == null) {
- throw new ConstraintViolationException("SAML 2 Attribute builder is unavailable");
- }
+ reqAttributeBuilder = (SAMLObjectBuilder<RequestedAttribute>)
+ XMLObjectProviderRegistrySupport.getBuilderFactory().<RequestedAttribute>getBuilderOrThrow(
+ RequestedAttribute.TYPE_NAME);
}
+ /** {@inheritDoc} */
+ @Nonnull public Class<Attribute> getEncodedType() {
+ return Attribute.class;
+ }
/** {@inheritDoc} */
@Nullable public String getEncodedName(@Nonnull final Properties properties) {
try {
// SAML 2 naming should be based on only what needs to be available from the properties alone.
- return new NamingFunction().apply(buildAttribute(null, null, properties, Collections.emptyList()));
+ return new NamingFunction().apply(buildAttribute(null, null, Attribute.class, properties,
+ Collections.emptyList()));
} catch (final AttributeEncodingException e) {
return null;
}
@@ -82,15 +93,28 @@ public abstract class AbstractSAML2AttributeTranscoder<EncodedType extends IdPAt
/** {@inheritDoc} */
@Override
@Nonnull protected Attribute buildAttribute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final IdPAttribute attribute, @Nonnull final Properties properties,
- @Nonnull @NonnullElements final List<XMLObject> attributeValues) throws AttributeEncodingException {
+ @Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends Attribute> to,
+ @Nonnull final Properties properties, @Nonnull @NonnullElements final List<XMLObject> attributeValues)
+ throws AttributeEncodingException {
final String name = properties.getProperty(PROP_NAME);
if (Strings.isNullOrEmpty(name)) {
throw new AttributeEncodingException("Required transcoder property 'name' not found");
}
-
- final Attribute samlAttribute = attributeBuilder.buildObject();
+
+ final Attribute samlAttribute;
+
+ if (to.equals(Attribute.class)) {
+ samlAttribute = attributeBuilder.buildObject();
+ } else if (to.equals(RequestedAttribute.class)) {
+ samlAttribute = reqAttributeBuilder.buildObject();
+ if (attribute instanceof IdPRequestedAttribute) {
+ ((RequestedAttribute) samlAttribute).setIsRequired(((IdPRequestedAttribute) attribute).getIsRequired());
+ }
+ } else {
+ throw new AttributeEncodingException("Unsupported target object type: " + to.getName());
+ }
+
samlAttribute.setName(name);
samlAttribute.setNameFormat(properties.getProperty(PROP_NAME_FORMAT, Attribute.URI_REFERENCE));
samlAttribute.getAttributeValues().addAll(attributeValues);
@@ -102,6 +126,38 @@ public abstract class AbstractSAML2AttributeTranscoder<EncodedType extends IdPAt
return samlAttribute;
}
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull protected IdPAttribute buildIdPAttribute(
+ @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
+ @Nonnull final Properties properties,
+ @Nonnull @NonnullElements final List<IdPAttributeValue<?>> attributeValues)
+ throws AttributeDecodingException {
+
+ final String id = properties.getProperty(AttributeTranscoderRegistry.PROP_ID);
+ if (Strings.isNullOrEmpty(id)) {
+ throw new AttributeDecodingException("Required transcoder property 'id' not found");
+ }
+
+ final IdPAttribute idpAttribute;
+ if (attribute instanceof RequestedAttribute) {
+ idpAttribute = new IdPRequestedAttribute(id);
+ ((IdPRequestedAttribute) idpAttribute).setRequired(((RequestedAttribute) attribute).isRequired());
+ } else {
+ idpAttribute = new IdPAttribute(id);
+ }
+
+ idpAttribute.setValues(attributeValues);
+
+ return idpAttribute;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull protected Iterable<XMLObject> getValues(@Nonnull final Attribute input) {
+ return input.getAttributeValues();
+ }
/**
* A function to produce a "canonical" name for a SAML 2.0 {@link Attribute} for transcoding rules.
@@ -121,7 +177,7 @@ public abstract class AbstractSAML2AttributeTranscoder<EncodedType extends IdPAt
}
final StringBuilder builder = new StringBuilder();
- builder.append('{').append(format).append('}').append(input.getName());
+ builder.append("SAML2:{").append(format).append('}').append(input.getName());
return builder.toString();
}
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 eeb12c1..464ef9f 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
@@ -69,8 +69,8 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
/** {@inheritDoc} */
@Nullable public AttributeType encode(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final IdPAttribute attribute, @Nonnull final Properties properties)
- throws AttributeEncodingException {
+ @Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends AttributeType> to,
+ @Nonnull final Properties properties) throws AttributeEncodingException {
ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
Constraint.isNotNull(attribute, "Attribute to encode cannot be null");
@@ -83,15 +83,8 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
log.debug("Beginning to encode attribute {}", attributeId);
- if (attribute.getValues().isEmpty()) {
- log.warn("Unable to encode {} attribute, contains no values", attributeId);
- return null;
- }
-
final List<XMLObject> samlAttributeValues = new ArrayList<>();
- EncodedType attributeValue;
- XMLObject samlAttributeValue;
for (final IdPAttributeValue o : attribute.getValues()) {
if (o == null) {
// filtered out upstream leave in test for sanity
@@ -105,8 +98,9 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
continue;
}
- attributeValue = (EncodedType) o;
- samlAttributeValue = encodeValue(profileRequestContext, attribute, properties, attributeValue);
+ final EncodedType attributeValue = (EncodedType) o;
+ final XMLObject samlAttributeValue =
+ encodeValue(profileRequestContext, attribute, properties, attributeValue);
if (samlAttributeValue == null) {
log.debug("Skipping empty value for attribute {}", attributeId);
} else {
@@ -114,21 +108,46 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
}
}
- if (samlAttributeValues.isEmpty()) {
- log.warn("Attribute {} did not contain any encodable values", attributeId);
- return null;
- }
-
log.debug("Completed encoding {} values for attribute {}", samlAttributeValues.size(), attributeId);
- return buildAttribute(profileRequestContext, attribute, properties, samlAttributeValues);
+ return buildAttribute(profileRequestContext, attribute, to, properties, samlAttributeValues);
}
/** {@inheritDoc} */
@Nullable public IdPAttribute decode(@Nonnull final ProfileRequestContext profileRequestContext,
@Nonnull final AttributeType input, @Nonnull final Properties properties)
throws AttributeDecodingException {
+ ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+ Constraint.isNotNull(input, "Attribute to decode cannot be null");
+
+ final String attributeName = getEncodedName(properties);
+
+ if (!getActivationCondition().test(profileRequestContext)) {
+ log.debug("Decoder for attribute {} inactive", attributeName);
+ return null;
+ }
- return null;
+ log.debug("Beginning to decode attribute {}", attributeName);
+
+ final List<IdPAttributeValue<?>> idpAttributeValues = new ArrayList<>();
+ final Iterable<XMLObject> samlAttributeValues = getValues(input);
+
+ for (final XMLObject o : samlAttributeValues) {
+ if (o == null) {
+ // filtered out upstream leave in test for sanity
+ log.debug("Skipping null value of attribute {}", attributeName);
+ continue;
+ }
+
+ final IdPAttributeValue<?> idpAttributeValue = decodeValue(profileRequestContext, input, properties, o);
+ if (idpAttributeValue == null) {
+ log.debug("Unable to decode value of attribute {}", attributeName);
+ } else {
+ idpAttributeValues.add(idpAttributeValue);
+ }
+ }
+
+ log.debug("Completed decoding {} values for attribute {}", idpAttributeValues.size(), attributeName);
+ return buildIdPAttribute(profileRequestContext, input, properties, idpAttributeValues);
}
/**
@@ -189,12 +208,30 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
// Checkstyle: CyclomaticComplexity ON
/**
- * Encodes an attribute value in to a SAML attribute value element.
+ * Builds a SAML attribute element from the given attribute values.
+ *
+ * @param profileRequestContext current profile request
+ * @param attribute the attribute being encoded
+ * @param to target type to create
+ * @param properties properties to control encoding
+ * @param attributeValues the encoded values for the attribute
+ *
+ * @return the SAML attribute object
+ *
+ * @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;
+
+ /**
+ * Encodes an attribute value into a SAML AttributeValue element.
*
* @param profileRequestContext current profile request
* @param attribute the attribute being encoded
* @param properties properties to control encoding
- * @param value the value to encoder
+ * @param value the value to encode
*
* @return the attribute value or null if the resulting attribute value would be empty
*
@@ -205,28 +242,45 @@ public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAML
@Nonnull final EncodedType value) throws AttributeEncodingException;
/**
- * Builds a SAML attribute element from the given attribute values.
+ * Builds an {@link IdPAttribute} from the given values.
*
* @param profileRequestContext current profile request
- * @param attribute the attribute being encoded
- * @param properties properties to control encoding
- * @param attributeValues the encoded values for the attribute, never null or containing null elements
+ * @param attribute the attribute being decoded
+ * @param properties properties to control decoding
+ * @param attributeValues the decoded values for the attribute
*
- * @return the SAML attribute element
+ * @return the IdPAttribute object
*
- * @throws AttributeEncodingException thrown if there is a problem constructing the SAML attribute
+ * @throws AttributeDecodingException thrown if there is a problem constructing the IdPAttribute
*/
- @Nonnull protected abstract AttributeType buildAttribute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final IdPAttribute attribute, @Nonnull final Properties properties,
- @Nonnull @NonnullElements final List<XMLObject> attributeValues) throws AttributeEncodingException;
+ @Nonnull protected abstract IdPAttribute buildIdPAttribute(
+ @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final AttributeType attribute,
+ @Nonnull final Properties properties,
+ @Nonnull @NonnullElements final List<IdPAttributeValue<?>> attributeValues)
+ throws AttributeDecodingException;
+
+ /**
+ * Returns the values to decode from the concrete input object.
+ *
+ * @param input input object
+ *
+ * @return values to decode
+ */
+ @Nonnull protected abstract Iterable<XMLObject> getValues(@Nonnull final AttributeType input);
+
/**
* Function to decode a single {@link XMLObject} into an {@link IdPAttributeValue}.
*
- * @param object the object to decode
+ * @param profileRequestContext current profile request
+ * @param attribute the attribute being decoded
+ * @param properties properties to control decoding
+ * @param value the value to decode
*
* @return the returned final {@link IdPAttributeValue} or null if decoding failed
*/
- @Nullable protected abstract IdPAttributeValue<?> decodeValue(@Nullable final XMLObject object);
+ @Nullable protected abstract IdPAttributeValue<?> decodeValue(
+ @Nonnull 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 a63c08d..420c308 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
@@ -33,6 +33,7 @@ import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTrans
import org.opensaml.core.xml.XMLObject;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
import org.opensaml.saml.saml2.core.AttributeValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -70,8 +71,11 @@ public class SAML2StringAttributeTranscoder extends AbstractSAML2AttributeTransc
}
/** {@inheritDoc} */
- @Nullable protected IdPAttributeValue<?> decodeValue(@Nullable final XMLObject object) {
- return StringAttributeValue.valueOf(getStringValue(object));
+ @Override @Nullable protected IdPAttributeValue<?> decodeValue(
+ @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
+ @Nonnull final Properties properties, @Nullable final XMLObject value) {
+
+ return value != null ? StringAttributeValue.valueOf(getStringValue(value)) : null;
}
}
\ 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