[java-identity-provider] branch master updated: IDP-1434 - Redesign Attribute encoding/decoding as a service

Scott Cantor cantor.2 at osu.edu
Wed Jun 5 19:36:50 EDT 2019


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

scantor pushed a commit to branch master
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=86b2ad51db6e184332130bade3d4855b62629163

The following commit(s) were added to refs/heads/master by this push:
       new  86b2ad5   IDP-1434 - Redesign Attribute encoding/decoding as a service
86b2ad5 is described below

commit 86b2ad51db6e184332130bade3d4855b62629163
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Jun 5 19:36:46 2019 -0400

    IDP-1434 - Redesign Attribute encoding/decoding as a service
    
    https://issues.shibboleth.net/jira/browse/IDP-1434
    
    Migrate IdPAttribute display metadata handling entirely to transcoders.
---
 .../transcoding/AttributeTranscoderRegistry.java   |  23 ++++
 .../impl/AttributeTranscoderRegistryImpl.java      |  52 ++++++++-
 .../impl/AttributeTranscoderRegistryImplTest.java  |   5 +
 .../resolver/AbstractAttributeDefinition.java      | 119 ++++++---------------
 .../attribute/resolver/AttributeDefinition.java    |  20 ----
 .../resolver/ResolvedAttributeDefinition.java      |  22 ----
 .../context/AttributeResolutionContext.java        |  35 +++++-
 .../resolver/AbstractAttributeDefinitionTest.java  |  59 ----------
 .../resolver/ResolvedAttributeDefinitionTest.java  |   2 -
 .../spring/ad/BaseAttributeDefinitionParser.java   |  45 --------
 .../spring/enc/BaseAttributeEncoderParser.java     |  51 ++++++++-
 .../resolver/spring/AttributeMapperTest.java       |  28 ++++-
 .../spring/ad/SimpleAttributeParserTest.java       |  21 ----
 .../resolver/spring/mapperTest-attributes.xml      |   4 +
 .../system/flows/cas/cas-abstract-beans.xml        |   4 +-
 .../system/flows/saml/saml-abstract-beans.xml      |   1 +
 .../idp/profile/impl/ResolveAttributes.java        |  20 +++-
 17 files changed, 244 insertions(+), 267 deletions(-)

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 d7c72a7..b36ba98 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
@@ -18,6 +18,8 @@
 package net.shibboleth.idp.attribute.transcoding;
 
 import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
 
 import javax.annotation.Nonnull;
 import javax.annotation.concurrent.ThreadSafe;
@@ -25,6 +27,7 @@ import javax.annotation.concurrent.ThreadSafe;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
 import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
 import net.shibboleth.utilities.java.support.component.IdentifiedComponent;
 
@@ -56,6 +59,26 @@ public interface AttributeTranscoderRegistry extends IdentifiedComponent {
     @Nonnull @NotEmpty static final String PROP_DESCRIPTION = "description";
     
     /**
+     * Get the localized display names for an {@link IdPAttribute}.
+     * 
+     * @param attribute input attribute
+     * 
+     * @return map of locale-based names
+     */
+    @Nonnull @NonnullElements @NotLive @Unmodifiable public Map<Locale,String> getDisplayNames(
+            @Nonnull final IdPAttribute attribute);
+
+    /**
+     * Get the localized descriptions for an {@link IdPAttribute}.
+     * 
+     * @param attribute input attribute
+     * 
+     * @return map of locale-based descriptions
+     */
+    @Nonnull @NonnullElements @NotLive @Unmodifiable public Map<Locale,String> getDescriptions(
+            @Nonnull final IdPAttribute attribute);
+    
+    /**
      * Obtains a set of instructions for encoding an input {@link IdPAttribute} into a target type.
      * 
      * <p>The principal property useful to a caller is {@link #PROP_TRANSCODER} to obtain an instance
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 c537c06..00c7f65 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
@@ -22,6 +22,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -38,6 +39,7 @@ import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
 import net.shibboleth.idp.profile.logic.RelyingPartyIdPredicate;
 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.NotLive;
 import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -62,15 +64,23 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
     @Nonnull private final Logger log = LoggerFactory.getLogger(AttributeTranscoderRegistryImpl.class);
     
     /** Registry of transcoding instructions for a given "name" and type of object. */
-    @Nonnull private final Map<String,Multimap<Class<?>,TranscodingRule>> transcodingRegistry;
+    @Nonnull @NonnullElements private final Map<String,Multimap<Class<?>,TranscodingRule>> transcodingRegistry;
+    
+    /** Registry of display name mappings associated with internal attribute IDs. */
+    @Nonnull @NonnullElements private final Map<String,Map<Locale,String>> displayNameRegistry;
+    
+    /** Registry of description mappings associated with internal attribute IDs. */
+    @Nonnull @NonnullElements private final Map<String,Map<Locale,String>> descriptionRegistry;
     
     /** Registry of naming functions for supported object types. */
-    @Nonnull private final Map<Class<?>,Function<?,String>> namingFunctionRegistry;
+    @Nonnull @NonnullElements private final Map<Class<?>,Function<?,String>> namingFunctionRegistry;
     
     /** Constructor. */
     public AttributeTranscoderRegistryImpl() {
         transcodingRegistry = new HashMap<>();
         namingFunctionRegistry = new HashMap<>();
+        displayNameRegistry = new HashMap<>();
+        descriptionRegistry = new HashMap<>();
     }
     
     /** {@inheritDoc} */
@@ -143,6 +153,32 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
     }
     
     /** {@inheritDoc} */
+    @Nonnull @NonnullElements @NotLive @Unmodifiable public Map<Locale,String> getDisplayNames(
+            @Nonnull final IdPAttribute attribute) {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+        Constraint.isNotNull(attribute, "IdPAttribute cannot be null");
+     
+        if (displayNameRegistry.containsKey(attribute.getId())) {
+            return displayNameRegistry.get(attribute.getId());
+        } else {
+            return Collections.emptyMap();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull @NonnullElements @NotLive @Unmodifiable public Map<Locale,String> getDescriptions(
+            @Nonnull final IdPAttribute attribute) {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+        Constraint.isNotNull(attribute, "IdPAttribute cannot be null");
+        
+        if (descriptionRegistry.containsKey(attribute.getId())) {
+            return descriptionRegistry.get(attribute.getId());
+        } else {
+            return Collections.emptyMap();
+        }
+    }
+    
+    /** {@inheritDoc} */
     @Nonnull @NonnullElements @Unmodifiable public Collection<TranscodingRule> getTranscodingRules(
             @Nonnull final IdPAttribute from, @Nonnull final Class<?> to) {
         ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
@@ -270,6 +306,18 @@ public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponen
             
             rulesetsForEncodedName.put(type, copy);
             
+            if (displayNameRegistry.containsKey(id)) {
+                displayNameRegistry.get(id).putAll(copy.getDisplayNames());
+            } else {
+                displayNameRegistry.put(id, new HashMap<>(copy.getDisplayNames()));
+            }
+
+            if (descriptionRegistry.containsKey(id)) {
+                descriptionRegistry.get(id).putAll(copy.getDescriptions());
+            } else {
+                descriptionRegistry.put(id, new HashMap<>(copy.getDescriptions()));
+            }
+            
         } else {
             log.warn("Transcoding rule for {} into type {} did not produce an encoded name", id, type.getName());
         }
diff --git a/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImplTest.java b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImplTest.java
index e6a419e..07e8f3a 100644
--- a/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImplTest.java
+++ b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImplTest.java
@@ -80,6 +80,8 @@ public class AttributeTranscoderRegistryImplTest {
         ruleset3.put(AttributeTranscoderRegistry.PROP_ID, "foo");
         ruleset3.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
         ruleset3.put(AttributeTranscoderRegistry.PROP_CONDITION, Predicates.alwaysFalse());
+        ruleset3.put(AttributeTranscoderRegistry.PROP_DISPLAY_NAME + '.' +  Locale.CANADA_FRENCH.toLanguageTag(), "Canadian French name2");
+        ruleset3.put(AttributeTranscoderRegistry.PROP_DESCRIPTION + '.' +  Locale.FRENCH.toLanguageTag(), "French desc");
         ruleset3.put("name", "ban");
 
         final Map<String,Object> ruleset4 = new HashMap<>();
@@ -94,6 +96,9 @@ public class AttributeTranscoderRegistryImplTest {
                 new TranscodingRule(ruleset4)));
         
         registry.initialize();
+        
+        assertEquals(registry.getDisplayNames(new IdPAttribute("foo")).size(), 2);
+        assertEquals(registry.getDescriptions(new IdPAttribute("foo")).size(), 3);
     }
     
     @AfterClass public void tearDown() {
diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinition.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinition.java
index 0c2f8c5..96e4c79 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinition.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinition.java
@@ -17,11 +17,6 @@
 
 package net.shibboleth.idp.attribute.resolver;
 
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
 import java.util.regex.Pattern;
 
 import javax.annotation.Nonnull;
@@ -31,19 +26,15 @@ import javax.annotation.concurrent.ThreadSafe;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
 import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.annotation.constraint.NullableElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.ImmutableMap;
-
 /** Base class for attribute definition resolver plugins. */
 @ThreadSafe
 public abstract class AbstractAttributeDefinition extends AbstractResolverPlugin<IdPAttribute> implements
@@ -55,12 +46,6 @@ public abstract class AbstractAttributeDefinition extends AbstractResolverPlugin
     /** Whether this attribute definition is only a dependency and thus its values should never be released. */
     private boolean dependencyOnly;
 
-    /** Localized human intelligible attribute name. */
-    @Nonnull private Map<Locale, String> displayNames = Collections.emptyMap();
-
-    /** Localized human readable description of attribute. */
-    @Nonnull private Map<Locale, String> displayDescriptions = Collections.emptyMap();
-
     /** cache for the log prefix - to save multiple recalculations. */
     @Nullable private String logPrefix;
 
@@ -88,68 +73,6 @@ public abstract class AbstractAttributeDefinition extends AbstractResolverPlugin
         dependencyOnly = isDependencyOnly;
     }
 
-    /**
-     * Gets the localized human readable descriptions of attribute.
-     * 
-     * @return human readable descriptions of attribute
-     */
-    @Override
-    @Nonnull @NonnullElements @Unmodifiable public Map<Locale, String> getDisplayDescriptions() {
-        return displayDescriptions;
-    }
-
-    /**
-     * Sets the localized human readable descriptions of attribute.
-     * 
-     * @param descriptions localized human readable descriptions of attribute
-     */
-    public void setDisplayDescriptions(@Nullable @NullableElements  final Map<Locale, String> descriptions) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
-
-        final HashMap<Locale, String> checkedDescriptions = new HashMap<>();
-        String trimmedDescription;
-        for (final Entry<Locale, String> entry : descriptions.entrySet()) {
-            trimmedDescription = StringSupport.trimOrNull(entry.getValue());
-            if (trimmedDescription != null) {
-                checkedDescriptions.put(entry.getKey(), trimmedDescription);
-            }
-        }
-
-        displayDescriptions = ImmutableMap.copyOf(checkedDescriptions);
-    }
-
-    /**
-     * Gets the localized human readable names of the attribute.
-     * 
-     * @return human readable names of the attribute
-     */
-    @Override
-    @Nonnull @NonnullElements @Unmodifiable public Map<Locale, String> getDisplayNames() {
-        return displayNames;
-    }
-
-    /**
-     * Sets the localized human readable names of the attribute.
-     * 
-     * @param names localized human readable names of the attribute
-     */
-    public void setDisplayNames(@Nullable @NullableElements final Map<Locale, String> names) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
-
-        final HashMap<Locale, String> checkedNames = new HashMap<>();
-        String trimmedName;
-        for (final Entry<Locale, String> entry : names.entrySet()) {
-            trimmedName = StringSupport.trimOrNull(entry.getValue());
-            if (trimmedName != null) {
-                checkedNames.put(entry.getKey(), trimmedName);
-            }
-        }
-
-        displayNames = ImmutableMap.copyOf(checkedNames);
-    }
-
     /** {@inheritDoc} */
     @Override
     protected void doInitialize() throws ComponentInitializationException {
@@ -193,13 +116,37 @@ public abstract class AbstractAttributeDefinition extends AbstractResolverPlugin
                     resolvedAttribute.getValues());
         }
 
-        log.trace("{} associating the following display descriptions with the resolved attribute: {}", getLogPrefix(),
-                getDisplayDescriptions());
-        resolvedAttribute.setDisplayDescriptions(getDisplayDescriptions());
-
-        log.trace("{} associating the following display names with the resolved attribute: {}", getLogPrefix(),
-                getDisplayNames());
-        resolvedAttribute.setDisplayNames(getDisplayNames());
+        if (resolutionContext.getTranscoderRegistry() != null) {
+            ServiceableComponent<AttributeTranscoderRegistry> component = null;
+            try {
+                component = resolutionContext.getTranscoderRegistry().getServiceableComponent();
+                if (component != null) {
+                    
+                    if (resolvedAttribute.getDisplayNames().isEmpty()) {
+                        resolvedAttribute.setDisplayNames(
+                                component.getComponent().getDisplayNames(resolvedAttribute));
+                        log.trace("{} associated display names with the resolved attribute: {}", getLogPrefix(),
+                                resolvedAttribute.getDisplayNames());
+                    }
+
+                    if (resolvedAttribute.getDisplayDescriptions().isEmpty()) {
+                        resolvedAttribute.setDisplayDescriptions(
+                                component.getComponent().getDescriptions(resolvedAttribute));
+                        log.trace("{} associated descriptions with the resolved attribute: {}", getLogPrefix(),
+                                resolvedAttribute.getDisplayDescriptions());
+                    }
+
+                } else {
+                    log.warn("No transcoder registry available, unable to attach displayName/description metadata");
+                }
+            } finally {
+                if (component != null) {
+                    component.unpinComponent();
+                }
+            }
+        } else {
+            log.debug("No transcoder registry supplied, unable to attach displayName/description metadata");
+        }
 
         return resolvedAttribute;
     }
diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AttributeDefinition.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AttributeDefinition.java
index 27a4850..f49e296 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AttributeDefinition.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AttributeDefinition.java
@@ -17,15 +17,9 @@
 
 package net.shibboleth.idp.attribute.resolver;
 
-import java.util.Locale;
-import java.util.Map;
-
 import javax.annotation.concurrent.ThreadSafe;
 
 import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
 
 /** Definition of attribute definition resolver plugins. */
 @ThreadSafe
@@ -39,18 +33,4 @@ public interface AttributeDefinition extends ResolverPlugin<IdPAttribute> {
      */
     boolean isDependencyOnly();
 
-    /**
-     * Gets the localized human readable descriptions of attribute.
-     * 
-     * @return human readable descriptions of attribute
-     */
-    @NonnullAfterInit @NonnullElements @Unmodifiable Map<Locale, String> getDisplayDescriptions();
-
-    /**
-     * Gets the localized human readable names of the attribute.
-     * 
-     * @return human readable names of the attribute
-     */
-    @NonnullAfterInit @NonnullElements @Unmodifiable Map<Locale, String> getDisplayNames();
-
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/ResolvedAttributeDefinition.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/ResolvedAttributeDefinition.java
index a382b7f..2f95da0 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/ResolvedAttributeDefinition.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/ResolvedAttributeDefinition.java
@@ -17,8 +17,6 @@
 
 package net.shibboleth.idp.attribute.resolver;
 
-import java.util.Locale;
-import java.util.Map;
 import java.util.Set;
 import java.util.function.Predicate;
 
@@ -87,16 +85,6 @@ public final class ResolvedAttributeDefinition extends AbstractAttributeDefiniti
     }
 
     /** {@inheritDoc} */
-    @Override @Nonnull @NonnullElements public Map<Locale, String> getDisplayDescriptions() {
-        return resolvedDefinition.getDisplayDescriptions();
-    }
-
-    /** {@inheritDoc} */
-    @Override @Nonnull @NonnullElements public Map<Locale, String> getDisplayNames() {
-        return resolvedDefinition.getDisplayNames();
-    }
-
-    /** {@inheritDoc} */
     @Override @Nullable public Predicate<ProfileRequestContext> getActivationCondition() {
         return null;
     }
@@ -136,16 +124,6 @@ public final class ResolvedAttributeDefinition extends AbstractAttributeDefiniti
     }
 
     /** {@inheritDoc} */
-    @Override public void setDisplayDescriptions(final Map<Locale, String> descriptions) {
-        return;
-    }
-
-    /** {@inheritDoc} */
-    @Override public void setDisplayNames(final Map<Locale, String> names) {
-        return;
-    }
-
-    /** {@inheritDoc} */
     @Override public void setPropagateResolutionExceptions(final boolean propagate) {
         return;
     }
diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/context/AttributeResolutionContext.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/context/AttributeResolutionContext.java
index a2bda1e..b14ad15 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/context/AttributeResolutionContext.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/context/AttributeResolutionContext.java
@@ -30,6 +30,7 @@ import javax.annotation.concurrent.NotThreadSafe;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.resolver.AttributeResolver;
 import net.shibboleth.idp.attribute.resolver.ResolutionException;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
 import net.shibboleth.utilities.java.support.annotation.constraint.Live;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
@@ -51,6 +52,9 @@ import com.google.common.collect.ImmutableMap;
 /** A context supplying input to the {@link net.shibboleth.idp.attribute.resolver.AttributeResolver} interface. */
 @NotThreadSafe
 public final class AttributeResolutionContext extends BaseContext {
+    
+    /** Registry service to use to attach display metadata to resolved attributes. */
+    @Nullable private ReloadableService<AttributeTranscoderRegistry> registryService;
 
     /** (internal) Names of the attributes that have been requested to be resolved. */
     @Nonnull @NonnullElements private Set<String> requestedAttributeNames;
@@ -66,10 +70,6 @@ public final class AttributeResolutionContext extends BaseContext {
 
     /** The attribute recipient's group identity. */
     @Nullable private String attributeRecipientGroupID;
-
-    /** How was the principal Authenticated? */
-    @Deprecated
-    @Nullable private String principalAuthenticationMethod;
     
     /** Whether the resolver should allow for results to come from cache. */
     private boolean allowCachedResults;
@@ -88,6 +88,33 @@ public final class AttributeResolutionContext extends BaseContext {
     }
     
     /**
+     * Gets a transcoder registry service instance.
+     * 
+     * @return registry service
+     * 
+     * @since 4.0.0
+     */
+    @Nullable public ReloadableService<AttributeTranscoderRegistry> getTranscoderRegistry() {
+        return registryService;
+    }
+    
+    /**
+     * Sets a transcoder registry service instance.
+     * 
+     * @param service registry service
+     * 
+     * @return this context
+     * 
+     * @since 4.0.0
+     */
+    @Nonnull public AttributeResolutionContext setTranscoderRegistry(
+            @Nullable final ReloadableService<AttributeTranscoderRegistry> service) {
+        registryService = service;
+        
+        return this;
+    }
+    
+    /**
      * Get whether to allow for results from cache (defaults to true).
      * 
      * @return whether to allow for results from cache
diff --git a/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinitionTest.java b/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinitionTest.java
index 3ddab43..3d17ff8 100644
--- a/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinitionTest.java
+++ b/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinitionTest.java
@@ -18,9 +18,6 @@
 package net.shibboleth.idp.attribute.resolver;
 
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
 import java.util.Set;
 
 import javax.annotation.Nonnull;
@@ -47,10 +44,6 @@ public class AbstractAttributeDefinitionTest {
 
         Assert.assertEquals(definition.getId(), "foo");
         Assert.assertFalse(definition.isDependencyOnly());
-        Assert.assertNotNull(definition.getDisplayDescriptions());
-        Assert.assertTrue(definition.getDisplayDescriptions().isEmpty());
-        Assert.assertNotNull(definition.getDisplayNames());
-        Assert.assertTrue(definition.getDisplayNames().isEmpty());
     }
 
     /** Tests setting and retrieving the dependency only option. */
@@ -72,58 +65,6 @@ public class AbstractAttributeDefinitionTest {
         Assert.assertFalse(definition.isDependencyOnly());
     }
 
-    /** Tests that display descriptions are properly added and modified. */
-    @Test
-    public void displayDescriptions() {
-        Locale en = new Locale("en");
-        Locale enbr = new Locale("en", "br");
-
-        MockAttributeDefinition definition = new MockAttributeDefinition("foo", null);
-        
-        Map<Locale, String> descriptions = new HashMap<>();
-        descriptions.put(en, "english");
-        descriptions.put(enbr, null);
-        definition.setDisplayDescriptions(descriptions);
-        
-        Assert.assertFalse(definition.getDisplayDescriptions().isEmpty());
-        Assert.assertEquals(definition.getDisplayDescriptions().size(), 1);
-        Assert.assertNotNull(definition.getDisplayDescriptions().get(en));
-
-        descriptions = definition.getDisplayDescriptions();
-        try {
-            descriptions.put(enbr, "british");
-            Assert.fail("able to add description to unmodifable map");
-        } catch (UnsupportedOperationException e) {
-            // expected this
-        }
-    }
-
-    /** Tests that display names are properly added and modified. */
-    @Test
-    public void displayNames() {
-        Locale en = new Locale("en");
-        Locale enbr = new Locale("en", "br");
-
-        MockAttributeDefinition definition = new MockAttributeDefinition("foo", null);
-
-        Map<Locale, String> names = new HashMap<>();
-        names.put(en, "english");
-        names.put(enbr, null);
-        definition.setDisplayNames(names);
-        
-        Assert.assertFalse(definition.getDisplayNames().isEmpty());
-        Assert.assertEquals(definition.getDisplayNames().size(), 1);
-        Assert.assertNotNull(definition.getDisplayNames().get(en));
-
-        names = definition.getDisplayNames();
-        try {
-            names.put(enbr, "british");
-            Assert.fail("able to add name to unmodifable map");
-        } catch (UnsupportedOperationException e) {
-            // expected this
-        }
-    }
-
     /** Test resolve an attribute. */
     @Test
     public void resolve() throws Exception {
diff --git a/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/ResolvedAttributeDefinitionTest.java b/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/ResolvedAttributeDefinitionTest.java
index eac6f76..2ad33be 100644
--- a/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/ResolvedAttributeDefinitionTest.java
+++ b/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/ResolvedAttributeDefinitionTest.java
@@ -112,8 +112,6 @@ public class ResolvedAttributeDefinitionTest {
         Assert.assertFalse(resolvedAttributeDefinition.isPropagateResolutionExceptions());
 
         resolvedAttributeDefinition.setDependencyOnly(true);
-        resolvedAttributeDefinition.setDisplayDescriptions(null);
-        resolvedAttributeDefinition.setDisplayNames(null);
 
         resolvedAttributeDefinition.setPropagateResolutionExceptions(true);
         Assert.assertFalse(resolvedAttributeDefinition.isPropagateResolutionExceptions());
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/ad/BaseAttributeDefinitionParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/ad/BaseAttributeDefinitionParser.java
index 0e85bc5..6b7c771 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/ad/BaseAttributeDefinitionParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/ad/BaseAttributeDefinitionParser.java
@@ -17,17 +17,12 @@
 
 package net.shibboleth.idp.attribute.resolver.spring.ad;
 
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
 import javax.annotation.Nonnull;
 import javax.xml.namespace.QName;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.support.ManagedMap;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.w3c.dom.Element;
 
@@ -35,8 +30,6 @@ import net.shibboleth.idp.attribute.resolver.spring.BaseResolverPluginParser;
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
-import net.shibboleth.utilities.java.support.xml.AttributeSupport;
-import net.shibboleth.utilities.java.support.xml.ElementSupport;
 
 /**
  * Base spring bean definition parser for attribute definitions. AttributeDefinition implementations should provide a
@@ -58,54 +51,16 @@ public abstract class BaseAttributeDefinitionParser extends BaseResolverPluginPa
     @Nonnull private final Logger log = LoggerFactory.getLogger(BaseAttributeDefinitionParser.class);
 
     /** {@inheritDoc} */
-    // CheckStyle: CyclomaticComplexity OFF
     @Override protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
             @Nonnull final BeanDefinitionBuilder builder) {
         super.doParse(config, parserContext, builder);
 
-        final List<Element> displayNames =
-                ElementSupport.getChildElements(config, new QName(AttributeResolverNamespaceHandler.NAMESPACE,
-                        "DisplayName"));
-        if (displayNames != null && !displayNames.isEmpty()) {
-            final Map<Locale, String> names = processLocalizedElement(displayNames);
-            log.debug("{} Setting displayNames {}", getLogPrefix(), names);
-            builder.addPropertyValue("displayNames", names);
-        }
-
-        final List<Element> displayDescriptions =
-                ElementSupport.getChildElements(config, new QName(AttributeResolverNamespaceHandler.NAMESPACE,
-                        "DisplayDescription"));
-        if (displayDescriptions != null && !displayDescriptions.isEmpty()) {
-            final Map<Locale, String> names = processLocalizedElement(displayDescriptions);
-            log.debug("{} Setting displayDescriptions {}", getLogPrefix(), names);
-            builder.addPropertyValue("displayDescriptions", names);
-        }
-
         if (config.hasAttributeNS(null, "dependencyOnly")) {
             final String dependencyOnly = StringSupport.trimOrNull(config.getAttributeNS(null, "dependencyOnly"));
             log.debug("{} Setting dependencyOnly {}", getLogPrefix(), dependencyOnly);
             builder.addPropertyValue("dependencyOnly", dependencyOnly);
         }
     }
-    // CheckStyle: CyclomaticComplexity ON
-
-
-    /**
-     * Used to process string elements that contain an xml:lang attribute expressing localization. returns a
-     * {@link ManagedMap} to allow property replacement to work.
-     * 
-     * @param elements list of elements, must not be null, may be empty
-     * 
-     * @return the localized string indexed by locale
-     */
-    protected Map<Locale, String> processLocalizedElement(@Nonnull final List<Element> elements) {
-        final Map<Locale, String> localizedString = new ManagedMap<>(elements.size());
-        for (final Element element : elements) {
-            localizedString.put(AttributeSupport.getXMLLangAsLocale(element), element.getTextContent());
-        }
-
-        return localizedString;
-    }
 
     /**
      * Return a string which is to be prepended to all log messages.
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseAttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseAttributeEncoderParser.java
index 880adfe..b5d3e74 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseAttributeEncoderParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseAttributeEncoderParser.java
@@ -17,9 +17,11 @@
 
 package net.shibboleth.idp.attribute.resolver.spring.enc;
 
+import java.util.List;
 import java.util.Map;
 
 import javax.annotation.Nonnull;
+import javax.xml.namespace.QName;
 
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
 import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
@@ -28,6 +30,7 @@ import net.shibboleth.idp.profile.logic.ScriptedPredicate;
 import net.shibboleth.idp.profile.spring.relyingparty.metadata.ScriptTypeBeanParser;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.xml.AttributeSupport;
 import net.shibboleth.utilities.java.support.xml.ElementSupport;
 
 import org.slf4j.Logger;
@@ -39,6 +42,7 @@ import org.springframework.beans.factory.support.ManagedMap;
 import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 
 /**
  * Base class for Spring bean definition parser for attribute encoders.
@@ -70,6 +74,7 @@ public abstract class BaseAttributeEncoderParser extends AbstractSingleBeanDefin
     }
     
     /** {@inheritDoc} */
+// Checkstyle: CyclomaticComplexity OFF
     @Override
     protected void doParse(final Element config, final ParserContext context, final BeanDefinitionBuilder builder) {
 
@@ -77,9 +82,29 @@ public abstract class BaseAttributeEncoderParser extends AbstractSingleBeanDefin
 
         builder.addConstructorArgValue(rule);
         
-        if (config.getParentNode() instanceof Element && ((Element)config.getParentNode()).hasAttributeNS(null, "id")) {
+        final Node parentAttribute = config.getParentNode();
+        
+        if (parentAttribute instanceof Element && ((Element) parentAttribute).hasAttributeNS(null, "id")) {
+            
+            // Handle id property.
             rule.put(AttributeTranscoderRegistry.PROP_ID,
                     StringSupport.trimOrNull(((Element) config.getParentNode()).getAttributeNS(null, "id")));
+            
+            // Handle display metadata.
+            final List<Element> displayNames =
+                    ElementSupport.getChildElements(parentAttribute,
+                            new QName(AttributeResolverNamespaceHandler.NAMESPACE, "DisplayName"));
+            if (displayNames != null && !displayNames.isEmpty()) {
+                processLocalizedElement(displayNames, rule, AttributeTranscoderRegistry.PROP_DISPLAY_NAME);
+            }
+
+            final List<Element> displayDescriptions =
+                    ElementSupport.getChildElements(parentAttribute,
+                            new QName(AttributeResolverNamespaceHandler.NAMESPACE, "DisplayDescription"));
+            if (displayDescriptions != null && !displayDescriptions.isEmpty()) {
+                processLocalizedElement(displayDescriptions, rule, AttributeTranscoderRegistry.PROP_DESCRIPTION);
+            }
+            
         } else {
             log.warn("Parsing AttributeEncoder with no parent element, resulting rule will be ignored");
         }
@@ -105,6 +130,7 @@ public abstract class BaseAttributeEncoderParser extends AbstractSingleBeanDefin
         
         doParse(config, context, rule);
     }
+// Checkstyle: CyclomaticComplexity ON
     
     /**
      * Inject any necessary elements into the mapping rule based on the specific encoder type.
@@ -124,4 +150,27 @@ public abstract class BaseAttributeEncoderParser extends AbstractSingleBeanDefin
      */
     @Nonnull protected abstract BeanReference buildTranscoder();
     
+    /**
+     * Used to process string elements that contain an xml:lang attribute expressing localization.
+     * 
+     * @param elements list of elements, must not be null, may be empty
+     * @param rule the map of rules to add to
+     * @param propertyPrefix the root property name to install
+     */
+    private void processLocalizedElement(@Nonnull final List<Element> elements, @Nonnull final ManagedMap rule,
+            @Nonnull @NotEmpty final String propertyPrefix) {
+        
+        for (final Element element : elements) {
+            final String value = element.getTextContent();
+            if (value != null) {
+                final String lang = StringSupport.trimOrNull(AttributeSupport.getXMLLang(element));
+                if (lang != null) {
+                    rule.put(propertyPrefix + '.' + lang, value);
+                } else {
+                    rule.put(propertyPrefix, value);
+                }
+            }
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeMapperTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeMapperTest.java
index ab688f0..44c8170 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeMapperTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeMapperTest.java
@@ -22,6 +22,8 @@ import static org.testng.Assert.*;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
 
 import org.opensaml.core.OpenSAMLInitBaseTestCase;
 import org.opensaml.saml.saml2.core.Attribute;
@@ -44,8 +46,7 @@ import net.shibboleth.utilities.java.support.service.ReloadableService;
 import net.shibboleth.utilities.java.support.service.ServiceException;
 import net.shibboleth.utilities.java.support.service.ServiceableComponent;
 
-/** test the Auto generation of the attribute mapper */
-
+/** Test transcoder registry population via attribute resolver. */
 public class AttributeMapperTest extends OpenSAMLInitBaseTestCase {
 
     private GenericApplicationContext pendingTeardownContext = null;
@@ -89,8 +90,10 @@ public class AttributeMapperTest extends OpenSAMLInitBaseTestCase {
         try {
             serviceableComponent = transcoderRegistry.getServiceableComponent();
             
+            final IdPAttribute idpattr = new IdPAttribute("eduPersonScopedAffiliation");
+            
             Collection<TranscodingRule> rulesets = serviceableComponent.getComponent().getTranscodingRules(
-                    new IdPAttribute("eduPersonScopedAffiliation"), Attribute.class);
+                    idpattr, Attribute.class);
             assertEquals(rulesets.size(), 1);
             final TranscodingRule rule = rulesets.iterator().next();
             assertEquals(rule.get(SAML2AttributeTranscoder.PROP_NAME, String.class), "urn:oid:1.3.6.1.4.1.5923.1.1.1.9");
@@ -98,6 +101,25 @@ public class AttributeMapperTest extends OpenSAMLInitBaseTestCase {
             assertEquals(rule.get(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME, String.class), "feduPersonScopedAffiliation");
             assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER, AttributeTranscoder.class) instanceof SAML2ScopedStringAttributeTranscoder);
             assertEquals(rule.get(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_DELIMITER, String.class), "#");
+
+            Map<Locale,String> names = rule.getDisplayNames();
+            assertEquals(names.size(), 2);
+            assertEquals(names.get(Locale.getDefault()), "Color");
+            assertEquals(names.get(Locale.UK), "Colour");
+            
+            Map<Locale,String> descs = rule.getDescriptions();
+            assertEquals(descs.size(), 1);
+            assertEquals(descs.get(Locale.CANADA_FRENCH), "Le Color, eh?");
+            
+            names = serviceableComponent.getComponent().getDisplayNames(idpattr);
+            assertEquals(names.size(), 2);
+            assertEquals(names.get(Locale.getDefault()), "Color");
+            assertEquals(names.get(Locale.UK), "Colour");
+            
+            descs = serviceableComponent.getComponent().getDescriptions(idpattr);
+            assertEquals(descs.size(), 1);
+            assertEquals(descs.get(Locale.CANADA_FRENCH), "Le Color, eh?");
+            
         } finally {
             serviceableComponent.unpinComponent();
         }
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/ad/SimpleAttributeParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/ad/SimpleAttributeParserTest.java
index 4d3c397..3514a1c 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/ad/SimpleAttributeParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/ad/SimpleAttributeParserTest.java
@@ -20,8 +20,6 @@ package net.shibboleth.idp.attribute.resolver.spring.ad;
 import static org.testng.Assert.*;
 
 import java.util.Collection;
-import java.util.Locale;
-import java.util.Map;
 import java.util.Set;
 
 import org.opensaml.profile.context.ProfileRequestContext;
@@ -55,8 +53,6 @@ public class SimpleAttributeParserTest extends BaseAttributeDefinitionParserTest
 
         assertEquals(attrDef.getId(), "simpleUnpopulated");
         assertFalse(attrDef.isDependencyOnly(), "isDependencyOnly");
-        assertTrue(attrDef.getDisplayDescriptions().isEmpty(), "getDisplayDescriptions().isEmpty()");
-        assertTrue(attrDef.getDisplayNames().isEmpty(), "getDisplayNames().isEmpty()");
         assertEquals(attrDef.getAttributeDependencies().size(), 1);
         
         assertTrue(pendingTeardownContext.getBeansOfType(Collection.class).isEmpty());
@@ -71,17 +67,6 @@ public class SimpleAttributeParserTest extends BaseAttributeDefinitionParserTest
         assertEquals(attrDef.getId(), "simplePopulated");
         assertTrue(attrDef.isDependencyOnly(), "isDependencyOnly");
 
-        final Map<Locale, String> descriptions = attrDef.getDisplayDescriptions();
-        assertEquals(descriptions.size(), 3, "getDisplayDescriptions");
-        assertEquals(descriptions.get(new Locale("en")), "DescInEnglish");
-        assertEquals(descriptions.get(new Locale("fr")), "DescEnFrancais");
-        assertEquals(descriptions.get(new Locale("ca")), "DescInCanadian");
-
-        final Map<Locale, String> names = attrDef.getDisplayNames();
-        assertEquals(names.size(), 2, "getDisplayNames");
-        assertEquals(names.get(new Locale("en")), "NameInEnglish");
-        assertEquals(names.get(new Locale("fr")), "NameEnFrancais");
-
         Set<ResolverAttributeDefinitionDependency> adDeps = attrDef.getAttributeDependencies();
         assertEquals(adDeps.size(), 2, "getAttributeDependencies");
         assertTrue(adDeps.contains(TestSources.makeAttributeDefinitionDependency("dep2")));
@@ -103,12 +88,6 @@ public class SimpleAttributeParserTest extends BaseAttributeDefinitionParserTest
         assertEquals(attrDef.getId(), "simplePopulated2");
         assertFalse(attrDef.isDependencyOnly(), "isDependencyOnly");
 
-        assertTrue(attrDef.getDisplayDescriptions().isEmpty(), "getDisplayDescriptions().isEmpty()");
-
-        final Map<Locale, String> names = attrDef.getDisplayNames();
-        assertEquals(names.size(), 1, "getDisplayNames");
-        assertEquals(names.get(new Locale("en")), "NameInAmerican");
-
         final Set<ResolverAttributeDefinitionDependency> attrDeps = attrDef.getAttributeDependencies();
         assertEquals(attrDeps.size(), 1, "getAttributeDependencies");
         assertTrue(attrDeps.contains(TestSources.makeAttributeDefinitionDependency("dep3")));
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/mapperTest-attributes.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/mapperTest-attributes.xml
index 6dacf39..8c52376 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/mapperTest-attributes.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/mapperTest-attributes.xml
@@ -44,6 +44,10 @@
         <AttributeEncoder xsi:type="SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" />
         <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" friendlyName="feduPersonScopedAffiliation" 
                scopeDelimiter='#'/>
+        
+        <DisplayName>Color</DisplayName>
+        <DisplayName xml:lang="en-GB">Colour</DisplayName>
+        <DisplayDescription xml:lang="fr-CA">Le Color, eh?</DisplayDescription>
     </AttributeDefinition>
     
     <AttributeDefinition xsi:type="SAML2NameID" id="eduPersonTID" 
diff --git a/idp-conf/src/main/resources/system/flows/cas/cas-abstract-beans.xml b/idp-conf/src/main/resources/system/flows/cas/cas-abstract-beans.xml
index 38d4aa6..5cca89d 100644
--- a/idp-conf/src/main/resources/system/flows/cas/cas-abstract-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/cas/cas-abstract-beans.xml
@@ -78,7 +78,9 @@
     <bean id="ResolveAttributes"
           class="net.shibboleth.idp.profile.impl.ResolveAttributes"
           scope="prototype"
-          c:resolverService-ref="shibboleth.AttributeResolverService" />
+          c:resolverService-ref="shibboleth.AttributeResolverService"
+          p:transcoderRegistry-ref="shibboleth.AttributeRegistryService"
+          p:maskFailures="%{idp.service.attribute.resolver.maskFailures:true}" />
 
     <bean id="FilterAttributes"
           class="net.shibboleth.idp.profile.impl.FilterAttributes"
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml
index 71bcfd2..4cf6c8b 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml
@@ -232,6 +232,7 @@
 
     <bean id="ResolveAttributes" class="net.shibboleth.idp.profile.impl.ResolveAttributes" scope="prototype"
         c:resolverService-ref="shibboleth.AttributeResolverService"
+        p:transcoderRegistry-ref="shibboleth.AttributeRegistryService"
         p:maskFailures="%{idp.service.attribute.resolver.maskFailures:true}" />
 
     <bean id="FilterAttributes" class="net.shibboleth.idp.profile.impl.FilterAttributes" scope="prototype"
diff --git a/idp-profile-impl/src/main/java/net/shibboleth/idp/profile/impl/ResolveAttributes.java b/idp-profile-impl/src/main/java/net/shibboleth/idp/profile/impl/ResolveAttributes.java
index 837f6ca..922413a 100644
--- a/idp-profile-impl/src/main/java/net/shibboleth/idp/profile/impl/ResolveAttributes.java
+++ b/idp-profile-impl/src/main/java/net/shibboleth/idp/profile/impl/ResolveAttributes.java
@@ -34,6 +34,7 @@ import net.shibboleth.idp.attribute.context.AttributeContext;
 import net.shibboleth.idp.attribute.resolver.AttributeResolver;
 import net.shibboleth.idp.attribute.resolver.ResolutionException;
 import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
 import net.shibboleth.idp.authn.context.SubjectContext;
 import net.shibboleth.idp.authn.context.navigate.SubjectContextPrincipalLookupFunction;
 import net.shibboleth.idp.profile.AbstractProfileAction;
@@ -41,6 +42,7 @@ import net.shibboleth.idp.profile.IdPEventIds;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.idp.profile.context.navigate.RelyingPartyIdLookupFunction;
 import net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -65,6 +67,9 @@ public final class ResolveAttributes extends AbstractProfileAction {
     /** Service used to get the resolver used to fetch attributes. */
     @Nonnull private final ReloadableService<AttributeResolver> attributeResolverService;
 
+    /** Transcoder registry service object. */
+    @NonnullAfterInit private ReloadableService<AttributeTranscoderRegistry> transcoderRegistry;
+    
     /** Strategy used to locate the identity of the issuer associated with the attribute resolution. */
     @Nullable private Function<ProfileRequestContext,String> issuerLookupStrategy;
 
@@ -114,6 +119,17 @@ public final class ResolveAttributes extends AbstractProfileAction {
         maskFailures = true;
         createResolutionContext = true;
     }
+
+    /**
+     * Sets the registry of transcoding rules to apply to supply attribute display metadata.
+     * 
+     * @param registry registry service interface
+     */
+    public void setTranscoderRegistry(@Nullable final ReloadableService<AttributeTranscoderRegistry> registry) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        transcoderRegistry = registry;
+    }
     
     /**
      * Set the strategy used to lookup the issuer for this attribute resolution.
@@ -295,7 +311,9 @@ public final class ResolveAttributes extends AbstractProfileAction {
     private void populateResolutionContext(@Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final AttributeResolutionContext resolutionContext) {
         
-        resolutionContext.setResolutionLabel(resolutionLabel);
+        resolutionContext
+            .setResolutionLabel(resolutionLabel)
+            .setTranscoderRegistry(transcoderRegistry);
         
         // Populate requested attributes, if not already set.
         if (resolutionContext.getRequestedIdPAttributeNames() == null

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


More information about the commits mailing list