[java-identity-provider] 02/27: Some initial working code.
Scott Cantor
cantor.2 at osu.edu
Fri May 3 14:31:52 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=5aa3440a28b6a48f8b9db6d06641f4f5a38b9731
commit 5aa3440a28b6a48f8b9db6d06641f4f5a38b9731
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Apr 8 22:14:51 2019 -0400
Some initial working code.
---
.../attribute/transcoding/AttributeTranscoder.java | 12 +
.../transcoding/AttributeTranscoderRegistry.java | 7 +-
.../impl/AttributeTranscoderRegistryImpl.java | 227 +++++++-------
.../impl/AttributeTranscoderRegistryTest.java | 60 ----
.../impl/AttributeTranscoderRegistryTest.java | 335 +++++++++++++++++++++
.../src/main/resources/conf/attribute-registry.xml | 25 +-
.../AbstractSAML2AttributeTranscoder.java | 39 +++
7 files changed, 525 insertions(+), 180 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 fed32e9..2572616 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
@@ -29,6 +29,7 @@ import org.opensaml.profile.context.ProfileRequestContext;
import net.shibboleth.idp.attribute.AttributeDecodingException;
import net.shibboleth.idp.attribute.AttributeEncodingException;
import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
/**
* Transcoders are objects that support both attribute encoding and decoding for bidirectional
@@ -47,6 +48,17 @@ import net.shibboleth.idp.attribute.IdPAttribute;
public interface AttributeTranscoder<T> {
/**
+ * Get the name of the encoded object that would be created by a given set of
+ * instructions.
+ *
+ * @param properties properties governing the encoding process
+ *
+ * @return a canonical name for objects produced by this transcoder for the
+ * given instructions
+ */
+ @Nullable @NotEmpty String getEncodedName(@Nonnull final Properties properties);
+
+ /**
* Encode the supplied attribute into a protocol specific representation.
*
* @param profileRequestContext current profile request context
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 6e8728d..756c0ab 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
@@ -38,6 +38,12 @@ import net.shibboleth.utilities.java.support.component.IdentifiedComponent;
@ThreadSafe
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";
@@ -55,7 +61,6 @@ public interface AttributeTranscoderRegistry extends IdentifiedComponent {
*/
@Nonnull @NonnullElements @Unmodifiable
Collection<Properties> getTranscodingProperties(@Nonnull final IdPAttribute from, @Nonnull final Class<?> to);
-
/**
* Obtains a set of instructions for decoding an input object into an {@link IdPAttribute}.
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 a5391f7..6216fab 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
@@ -21,21 +21,18 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Properties;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.ThreadSafe;
import net.shibboleth.ext.spring.service.AbstractServiceableComponent;
-import net.shibboleth.idp.attribute.AttributeDecoder;
-import net.shibboleth.idp.attribute.AttributeEncoder;
import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
-import net.shibboleth.utilities.java.support.annotation.ParameterName;
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.collection.ClassToInstanceMultiMap;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.primitive.StringSupport;
@@ -43,6 +40,12 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+
/** Service implementation of the {@link AttributeTranscoderRegistry} interface. */
@ThreadSafe
public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponent<AttributeTranscoderRegistry>
@@ -51,25 +54,16 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(AttributeTranscoderRegistryImpl.class);
- /** Registry of encoders for a given attribute ID and type of encoder. */
- @Nonnull private final Map<String,ClassToInstanceMultiMap<AttributeEncoder>> attributeEncoders;
-
- /** Registry of decoders for a given object "name" and type of decoder. */
- @Nonnull private final Map<String,ClassToInstanceMultiMap<AttributeDecoder>> attributeDecoders;
+ /** Registry of transcoding instructions for a given "name" and type of object. */
+ @Nonnull private final Map<String,Multimap<Class<?>,Properties>> transcodingRegistry;
- /** Registry of transcoder types and naming for supported object types. */
- @Nonnull private final Map<Class<?>,TypeInfo> typeInfoRegistry;
+ /** Registry of naming functions for supported object types. */
+ @Nonnull private final Map<Class<?>,Function<?,String>> namingFunctionRegistry;
- /**
- * Constructor.
- *
- * @param id ID of this service
- */
- public AttributeTranscoderRegistryImpl(@Nonnull @NotEmpty final String id) {
- setId(id);
- attributeEncoders = new HashMap<>();
- attributeDecoders = new HashMap<>();
- typeInfoRegistry = new HashMap<>();
+ /** Constructor. */
+ public AttributeTranscoderRegistryImpl() {
+ transcodingRegistry = new HashMap<>();
+ namingFunctionRegistry = new HashMap<>();
}
/** {@inheritDoc} */
@@ -77,111 +71,124 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
return this;
}
- public void setTypeRegistry(@Nonnull @NonnullElements Map<Class<?>,TypeInfo<?>> registry) {
+ /**
+ * Installs registry of naming functions mapped against the types of objects they support.
+ *
+ * @param registry map of types to naming functions
+ */
+ public void setNamingRegistry(@Nonnull @NonnullElements final Map<Class<?>,Function<?,String>> registry) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ if (registry == null) {
+ namingFunctionRegistry.clear();
+ return;
+ }
+ registry.forEach((k,v) -> {
+ if (k != null && v != null) {
+ namingFunctionRegistry.put(k, v);
+ }
+ });
}
- public void setTranscoderRegistry(@Nonnull @NonnullElements Map<String,Collection<?>> registry) {
+ /**
+ * Install the transcoder mappings en masse.
+ *
+ * <p>Each map entry connects an {@link IdPAttribute} name to the rules for transcoding to/from it.</p>
+ *
+ * @param registry mappings from internal name to transcoding rules
+ */
+ public void setTranscoderRegistry(@Nonnull @NonnullElements final Map<String,Collection<Properties>> registry) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ if (registry == null) {
+ transcodingRegistry.clear();
+ return;
+ }
+ for (final Map.Entry<String,Collection<Properties>> entry : registry.entrySet()) {
+
+ final String internalId = StringSupport.trimOrNull(entry.getKey());
+ if (internalId != null && entry.getValue() != null && !entry.getValue().isEmpty()) {
+
+ for (final Properties props : Collections2.filter(entry.getValue(), Predicates.notNull())) {
+
+ final Object type = props.get(PROP_TYPE);
+ final Object transcoder = props.get(PROP_TRANSCODER);
+
+ if (type instanceof Class && transcoder instanceof AttributeTranscoder) {
+ final String targetName = ((AttributeTranscoder) transcoder).getEncodedName(props);
+ if (targetName != null) {
+
+ final Properties copy = new Properties();
+ copy.putAll(props);
+
+ // Install mapping back to IdPAttribute's name.
+ copy.setProperty(PROP_ID, internalId);
+
+ Multimap<Class<?>,Properties> rulesetsForIdPName = transcodingRegistry.get(internalId);
+ if (rulesetsForIdPName == null) {
+ rulesetsForIdPName = ArrayListMultimap.create();
+ transcodingRegistry.put(internalId, rulesetsForIdPName);
+ }
+
+ rulesetsForIdPName.put((Class) type, copy);
+
+ Multimap<Class<?>,Properties> rulesetsForEncodedName = transcodingRegistry.get(targetName);
+ if (rulesetsForEncodedName == null) {
+ rulesetsForEncodedName = ArrayListMultimap.create();
+ transcodingRegistry.put(targetName, rulesetsForEncodedName);
+ }
+
+ rulesetsForEncodedName.put((Class) type, copy);
+
+ } else {
+ log.warn("Transcoding rule for {} into type {} did not produce an encoded name",
+ internalId, ((Class) type).getName());
+ }
+ } else {
+ log.warn("Transcoding rule for {} missing or invalid {} or {} properties", internalId,
+ PROP_TYPE, PROP_TRANSCODER);
+ }
+ }
+ }
+ }
}
/** {@inheritDoc} */
- @Nonnull @NonnullElements @Unmodifiable
- public <T> Collection<AttributeEncoder<T>> getEncoders(@Nonnull final IdPAttribute from,
- @Nonnull final Class<T> to) {
+ @Nonnull @NonnullElements @Unmodifiable public Collection<Properties> getTranscodingProperties(
+ @Nonnull final IdPAttribute from, @Nonnull final Class<?> to) {
ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+ Constraint.isNotNull(from, "IdPAttribute cannot be null");
+ Constraint.isNotNull(to, "Target type cannot be null");
- final TypeInfo<T> typeInfo = typeInfoRegistry.get(to);
- if (typeInfo == null) {
- log.warn("Unsupported object type: {}", to.getName());
- return Collections.emptyList();
- }
+ final Multimap<Class<?>,Properties> propertyCollections = transcodingRegistry.get(from.getId());
- final ClassToInstanceMultiMap<AttributeEncoder> encoders = attributeEncoders.get(from.getId());
-
- return encoders != null ? encoders.get(typeInfo.getEncoderType()) : Collections.emptyList();
+ return propertyCollections != null ? ImmutableList.copyOf(propertyCollections.get(to))
+ : Collections.emptyList();
}
/** {@inheritDoc} */
- @Nonnull @NonnullElements @Unmodifiable
- public <T> Collection<AttributeDecoder<T>> getDecoders(@Nonnull final T from) {
+ @Nonnull @NonnullElements @Unmodifiable public <T> Collection<Properties> getTranscodingProperties(
+ @Nonnull final T from) {
ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+ Constraint.isNotNull(from, "Input object cannot be null");
- final TypeInfo<T> typeInfo = typeInfoRegistry.get(from.getClass());
- if (typeInfo == null) {
+ 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);
+ if (id != null) {
+ final Multimap<Class<?>,Properties> propertyCollections = transcodingRegistry.get(id);
+
+ return propertyCollections != null ? ImmutableList.copyOf(propertyCollections.get(from.getClass()))
+ : Collections.emptyList();
+ } else {
+ log.warn("Object of type {} did not have a canonical name", from.getClass().getName());
+ }
+ } else {
log.warn("Unsupported object type: {}", from.getClass().getName());
- return Collections.emptyList();
- }
-
- final String id = StringSupport.trimOrNull(typeInfo.getNamingFunction().apply(from));
- if (id == null) {
- log.warn("Object of type {} did not have a canonical name", from.getClass().getName());
- return Collections.emptyList();
}
- final ClassToInstanceMultiMap<AttributeDecoder> decoders = attributeDecoders.get(id);
-
- return decoders != null ? decoders.get(typeInfo.getDecoderType()) : Collections.emptyList();
- }
-
- /**
- * Metadata connecting data types to naming functions and codec types.
- *
- * @param <T> object type
- */
- public static class TypeInfo<T> {
-
- /** Function to derive a canonical name. */
- @Nonnull private final Function<T,String> namingFunction;
-
- /** Type of encoder. */
- @Nonnull private final Class<AttributeEncoder<T>> encoderType;
-
- /** Type of decoder. */
- @Nonnull private final Class<AttributeDecoder<T>> decoderType;
-
- /**
- * Constructor.
- *
- * @param naming canonical naming function
- * @param encoder encoder type
- * @param decoder decoder type
- */
- public TypeInfo(@Nonnull @ParameterName(name="naming") final Function<T,String> naming,
- @Nonnull @ParameterName(name="encoder") final Class<AttributeEncoder<T>> encoder,
- @Nonnull @ParameterName(name="decoder") final Class<AttributeDecoder<T>> decoder) {
-
- namingFunction = Constraint.isNotNull(naming, "Naming function cannot be null");
- encoderType = Constraint.isNotNull(encoder, "Encoder type cannot be null");
- decoderType = Constraint.isNotNull(decoder, "Decoder type cannot be null");
- }
-
- /**
- * Gets the function deriving a canonical name for an object.
- *
- * @return function deriving a canonical name for an object
- */
- @Nonnull public Function<T,String> getNamingFunction() {
- return namingFunction;
- }
-
- /**
- * Gets the type of encoder supporting an object.
- *
- * @return type of encoder supporting an object
- */
- @Nonnull public Class<AttributeEncoder<T>> getEncoderType() {
- return encoderType;
- }
-
- /**
- * Gets the type of decoder supporting an object.
- *
- * @return type of decoder supporting an object
- */
- @Nonnull public Class<AttributeDecoder<T>> getDecoderType() {
- return decoderType;
- }
+ return Collections.emptyList();
}
-
+
}
\ No newline at end of file
diff --git a/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/impl/AttributeTranscoderRegistryTest.java b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/impl/AttributeTranscoderRegistryTest.java
deleted file mode 100644
index 6429bc8..0000000
--- a/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/impl/AttributeTranscoderRegistryTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.attribute.impl;
-
-import java.util.Collection;
-
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import net.shibboleth.idp.attribute.AttributeDecoder;
-import net.shibboleth.idp.attribute.AttributeDecodingException;
-import net.shibboleth.idp.attribute.AttributeEncoder;
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
-import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
-
-/**
- * Test for {@link AttributeTranscoderRegistry}.
- */
-public class AttributeTranscoderRegistryTest {
-
-
- @Test void testEncode() throws AttributeEncodingException {
-
- final AttributeTranscoderRegistry registry = new AttributeTranscoderRegistryImpl("test");
-
- final IdPAttribute foo = new IdPAttribute("foo");
-
- final Collection<AttributeEncoder<String>> encoders = registry.getEncoders(foo, String.class);
-
- final String s = encoders.iterator().next().encode(foo);
- }
-
- @Test void testDecode() throws AttributeDecodingException {
-
- final AttributeTranscoderRegistry registry = new AttributeTranscoderRegistryImpl("test");
-
- final String foo = new String("foo");
-
- final Collection<AttributeDecoder<String>> decoders = registry.getDecoders(foo);
-
- final IdPAttribute a = decoders.iterator().next().decode(foo);
- }
-}
\ 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
new file mode 100644
index 0000000..02b58a9
--- /dev/null
+++ b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryTest.java
@@ -0,0 +1,335 @@
+/*
+ * 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.attribute.transcoding.impl;
+
+import java.util.ArrayList;
+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 org.opensaml.profile.context.ProfileRequestContext;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+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}.
+ */
+public class AttributeTranscoderRegistryTest {
+
+ private AttributeTranscoderRegistryImpl registry;
+
+ @BeforeMethod public void setUp() throws ComponentInitializationException {
+ registry = new AttributeTranscoderRegistryImpl();
+ registry.setId("test");
+
+ registry.setNamingRegistry(Collections.singletonMap(
+ Pair.class, (Pair p) -> "{Pair}" + p.getFirst().toString()));
+
+ 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, Pair.class);
+ ruleset2.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+ ruleset2.setProperty("name", "baz");
+
+ mappings.put("foo", Arrays.asList(ruleset1, ruleset2));
+ mappings.put("foo2", Collections.singletonList(ruleset2));
+
+ registry.setTranscoderRegistry(mappings);
+
+ registry.initialize();
+ }
+
+ @AfterMethod public void tearDown() {
+ registry.destroy();
+ registry = null;
+ }
+
+
+ // Test no mappings to encode IdPAttribute.
+ @Test public void testEncodeNoMappings() throws AttributeEncodingException {
+
+ Assert.assertTrue(registry.getTranscodingProperties(new IdPAttribute("frobnitz"), Pair.class).isEmpty());
+ Assert.assertTrue(registry.getTranscodingProperties(new IdPAttribute("foo"), String.class).isEmpty());
+}
+
+ // Test no reverse mappings from a Pair/String to an IdPAttribute
+ @Test public void testDecodeNoMappings() throws AttributeDecodingException {
+
+ Assert.assertTrue(registry.getTranscodingProperties(new Pair("foo", "value")).isEmpty());
+ Assert.assertTrue(registry.getTranscodingProperties(new String("foo")).isEmpty());
+ }
+
+ @Test public void testEncodeNoValues() throws AttributeEncodingException {
+ final IdPAttribute foo = new IdPAttribute("foo");
+
+ final List<Pair<String,Object>> pairs = new ArrayList<>();
+
+ for (final Properties ruleset : registry.getTranscodingProperties(foo, Pair.class)) {
+ final AttributeTranscoder<Pair<String,Object>> t =
+ (AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
+ pairs.add(t.encode(null, foo, ruleset));
+ }
+
+ Assert.assertEquals(pairs.size(), 2);
+
+ Assert.assertEquals(pairs.get(0).getFirst(), "bar");
+ Assert.assertNull(pairs.get(0).getSecond());
+
+ Assert.assertEquals(pairs.get(1).getFirst(), "baz");
+ Assert.assertNull(pairs.get(1).getSecond());
+ }
+
+ @Test public void testDecodeOneNoValues() throws AttributeDecodingException {
+
+ final Pair<String,Object> bar = new Pair("bar", null);
+
+ final List<IdPAttribute> attributes = new ArrayList<>();
+
+ for (final Properties ruleset : registry.getTranscodingProperties(bar)) {
+ final AttributeTranscoder<Pair<String,Object>> t =
+ (AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
+ attributes.add(t.decode(null, bar, ruleset));
+ }
+
+ Assert.assertEquals(attributes.size(), 1);
+
+ Assert.assertEquals(attributes.get(0).getId(), "foo");
+ Assert.assertTrue(attributes.get(0).getValues().isEmpty());
+ }
+
+ @Test public void testDecodeTwoNoValues() throws AttributeDecodingException {
+
+ final Pair<String,Object> baz = new Pair("baz", null);
+
+ final List<IdPAttribute> attributes = new ArrayList<>();
+
+ for (final Properties ruleset : registry.getTranscodingProperties(baz)) {
+ final AttributeTranscoder<Pair<String,Object>> t =
+ (AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
+ attributes.add(t.decode(null, baz, ruleset));
+ }
+
+ Assert.assertEquals(attributes.size(), 2);
+
+ Assert.assertEquals(attributes.get(0).getId(), "foo");
+ Assert.assertTrue(attributes.get(0).getValues().isEmpty());
+
+ Assert.assertEquals(attributes.get(1).getId(), "foo2");
+ Assert.assertTrue(attributes.get(1).getValues().isEmpty());
+ }
+
+ @Test public void testEncodeStringValues() throws AttributeEncodingException {
+ final IdPAttribute foo = new IdPAttribute("foo");
+ foo.setValues(Collections.singletonList(StringAttributeValue.valueOf("value")));
+
+ final List<Pair<String,Object>> pairs = new ArrayList<>();
+
+ for (final Properties ruleset : registry.getTranscodingProperties(foo, Pair.class)) {
+ final AttributeTranscoder<Pair<String,Object>> t =
+ (AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
+ pairs.add(t.encode(null, foo, 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 testDecodeOneStringValues() throws AttributeDecodingException {
+
+ final Pair<String,Object> bar = new Pair("bar", "value");
+
+ final List<IdPAttribute> attributes = new ArrayList<>();
+
+ for (final Properties ruleset : registry.getTranscodingProperties(bar)) {
+ final AttributeTranscoder<Pair<String,Object>> t =
+ (AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
+ attributes.add(t.decode(null, bar, ruleset));
+ }
+
+ Assert.assertEquals(attributes.size(), 1);
+
+ Assert.assertEquals(attributes.get(0).getId(), "foo");
+ Assert.assertEquals(attributes.get(0).getValues().get(0).getValue(), "value");
+ }
+
+ @Test public void testDecodeTwoStringValues() throws AttributeDecodingException {
+
+ final Pair<String,Object> baz = new Pair("baz", "value");
+
+ final List<IdPAttribute> attributes = new ArrayList<>();
+
+ for (final Properties ruleset : registry.getTranscodingProperties(baz)) {
+ final AttributeTranscoder<Pair<String,Object>> t =
+ (AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
+ attributes.add(t.decode(null, baz, ruleset));
+ }
+
+ Assert.assertEquals(attributes.size(), 2);
+
+ Assert.assertEquals(attributes.get(0).getId(), "foo");
+ Assert.assertEquals(attributes.get(0).getValues().get(0).getValue(), "value");
+
+ Assert.assertEquals(attributes.get(1).getId(), "foo2");
+ Assert.assertEquals(attributes.get(1).getValues().get(0).getValue(), "value");
+ }
+
+ @Test public void testEncodeUnsupportedValues() throws AttributeEncodingException {
+ final IdPAttribute foo = new IdPAttribute("foo");
+ foo.setValues(Collections.singletonList(EmptyAttributeValue.ZERO_LENGTH));
+
+ final List<Pair<String,Object>> pairs = new ArrayList<>();
+
+ for (final Properties ruleset : registry.getTranscodingProperties(foo, Pair.class)) {
+ final AttributeTranscoder<Pair<String,Object>> t =
+ (AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
+ pairs.add(t.encode(null, foo, ruleset));
+ }
+
+ Assert.assertEquals(pairs.size(), 2);
+
+ Assert.assertEquals(pairs.get(0).getFirst(), "bar");
+ Assert.assertNull(pairs.get(0).getSecond());
+
+ Assert.assertEquals(pairs.get(1).getFirst(), "baz");
+ Assert.assertNull(pairs.get(0).getSecond());
+ }
+
+ @Test public void testDecodeOneUnsupportedValues() throws AttributeDecodingException {
+
+ final Pair<String,Object> bar = new Pair("bar", 0L);
+
+ final List<IdPAttribute> attributes = new ArrayList<>();
+
+ for (final Properties ruleset : registry.getTranscodingProperties(bar)) {
+ final AttributeTranscoder<Pair<String,Object>> t =
+ (AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
+ attributes.add(t.decode(null, bar, ruleset));
+ }
+
+ Assert.assertEquals(attributes.size(), 1);
+
+ Assert.assertEquals(attributes.get(0).getId(), "foo");
+ Assert.assertTrue(attributes.get(0).getValues().isEmpty());
+ }
+
+ @Test public void testDecodeTwoUnsupportedValues() throws AttributeDecodingException {
+
+ final Pair<String,Object> baz = new Pair("baz", 0L);
+
+ final List<IdPAttribute> attributes = new ArrayList<>();
+
+ for (final Properties ruleset : registry.getTranscodingProperties(baz)) {
+ final AttributeTranscoder<Pair<String,Object>> t =
+ (AttributeTranscoder) ruleset.get(AttributeTranscoderRegistry.PROP_TRANSCODER);
+ attributes.add(t.decode(null, baz, ruleset));
+ }
+
+ Assert.assertEquals(attributes.size(), 2);
+
+ Assert.assertEquals(attributes.get(0).getId(), "foo");
+ Assert.assertTrue(attributes.get(0).getValues().isEmpty());
+
+ Assert.assertEquals(attributes.get(1).getId(), "foo2");
+ Assert.assertTrue(attributes.get(1).getValues().isEmpty());
+ }
+
+ private static 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;
+ }
+
+ /** {@inheritDoc} */
+ protected boolean canEncodeValue(IdPAttribute idpAttribute, IdPAttributeValue value) {
+ return value instanceof StringAttributeValue;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/idp-conf/src/main/resources/conf/attribute-registry.xml b/idp-conf/src/main/resources/conf/attribute-registry.xml
index e1e8eaf..e1eafb1 100644
--- a/idp-conf/src/main/resources/conf/attribute-registry.xml
+++ b/idp-conf/src/main/resources/conf/attribute-registry.xml
@@ -25,18 +25,25 @@
<entry key="uid">
<map>
- <entry>
- <key>
- <ref>SAML2.Attribute</ref>
- </key>
- <map>
- <entry key="transcoder" ref="SAML2StringTranscoder" />
- <entry key="name" value="urn:oid:0.9.2342.19200300.100.1.1" />
- </map>
- </entry>
+ <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>
</entry>
</util:map>
+
+ <util:map id="shibboleth.DefaultNamingRegistry">
+ <entry>
+ <key>
+ <ref bean="SAML2.Attribute" />
+ </key>
+ <bean class="net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder.NamingFunction" />
+ </entry>
+ </util:map>
+ <bean class="net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl"
+ p:namingRegistry-ref="shibboleth.DefaultNamingRegistry"
+ p:transcoderRegistry-ref="shibboleth.DefaultAttributeRegistry" />
+
</beans>
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 d32b635..b5f1f2a 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
@@ -17,10 +17,13 @@
package net.shibboleth.idp.saml.attribute.transcoding;
+import java.util.Collections;
import java.util.List;
import java.util.Properties;
+import java.util.function.Function;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import net.shibboleth.idp.attribute.AttributeEncodingException;
import net.shibboleth.idp.attribute.IdPAttribute;
@@ -64,6 +67,18 @@ public abstract class AbstractSAML2AttributeTranscoder<EncodedType extends IdPAt
}
}
+
+ /** {@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()));
+ } catch (final AttributeEncodingException e) {
+ return null;
+ }
+ }
+
/** {@inheritDoc} */
@Override
@Nonnull protected Attribute buildAttribute(@Nonnull final ProfileRequestContext profileRequestContext,
@@ -88,4 +103,28 @@ public abstract class AbstractSAML2AttributeTranscoder<EncodedType extends IdPAt
return samlAttribute;
}
+ /**
+ * A function to produce a "canonical" name for a SAML 2.0 {@link Attribute} for transcoding rules.
+ */
+ public static class NamingFunction implements Function<Attribute,String> {
+
+ /** {@inheritDoc} */
+ @Nullable public String apply(@Nullable final Attribute input) {
+
+ if (input == null || input.getName() == null) {
+ return null;
+ }
+
+ String format = input.getNameFormat();
+ if (format == null) {
+ format = Attribute.UNSPECIFIED;
+ }
+
+ final StringBuilder builder = new StringBuilder();
+ builder.append('{').append(format).append('}').append(input.getName());
+ return builder.toString();
+ }
+
+ }
+
}
\ 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