[java-identity-provider] branch main updated: IDP-2353 - C14n flow that runs a deployer-defined bean

Scott Cantor cantor.2 at osu.edu
Thu Feb 27 19:14:48 UTC 2025


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

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

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

The following commit(s) were added to refs/heads/main by this push:
     new 7fe2c97c1 IDP-2353 - C14n flow that runs a deployer-defined bean
7fe2c97c1 is described below

commit 7fe2c97c1c721424c7492dd6805ff6bfe2f4a4aa
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu Feb 27 14:14:29 2025 -0500

    IDP-2353 - C14n flow that runs a deployer-defined bean
    
    https://shibboleth.atlassian.net/browse/IDP-2353
    
    Reimplement attribute-sourced c14n inline, leaving orignal in place.
---
 .../AttributeSourcedSubjectCanonicalization.java   | 269 ++++++++++++++++-----
 ...ibuteSourcedSubjectCanonicalizationAction.java} |   9 +-
 ...eSourcedSubjectCanonicalizationActionTest.java} |  12 +-
 ...ttributeSourcedSubjectCanonicalizationTest.java | 136 +++++++----
 .../shibboleth/idp/conf/subject-c14n-system.xml    |  44 +++-
 .../c14n/attribute-sourced-subject-c14n-beans.xml  |   2 +-
 6 files changed, 340 insertions(+), 132 deletions(-)

diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalization.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalization.java
index 54ef3d64d..92e307594 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalization.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalization.java
@@ -15,18 +15,21 @@
 package net.shibboleth.idp.authn.impl;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.security.auth.Subject;
 
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.messaging.context.navigate.RecursiveTypedParentContextLookup;
+import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 
@@ -35,70 +38,114 @@ import net.shibboleth.idp.attribute.IdPAttributeValue;
 import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
 import net.shibboleth.idp.attribute.StringAttributeValue;
 import net.shibboleth.idp.attribute.context.AttributeContext;
-import net.shibboleth.idp.authn.AbstractSubjectCanonicalizationAction;
+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.authn.AbstractSubjectCanonicalizer;
 import net.shibboleth.idp.authn.AuthnEventIds;
-import net.shibboleth.idp.authn.SubjectCanonicalizationException;
+import net.shibboleth.idp.authn.SubjectCanonicalizer;
 import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
+import net.shibboleth.idp.authn.context.SubjectContext;
+import net.shibboleth.idp.authn.context.navigate.SubjectContextPrincipalLookupFunction;
 import net.shibboleth.idp.authn.principal.IdPAttributePrincipal;
+import net.shibboleth.profile.context.navigate.IssuerLookupFunction;
+import net.shibboleth.profile.context.navigate.RelyingPartyIdLookupFunction;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.FunctionSupport;
+import net.shibboleth.shared.logic.PredicateSupport;
 import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.service.ReloadableService;
+import net.shibboleth.shared.service.ServiceException;
+import net.shibboleth.shared.service.ServiceableComponent;
 
 /**
- * An action that extracts a resolved {@link IdPAttribute} value from an {@link AttributeContext} child obtained via
- * lookup function (by default a child of the {@link SubjectCanonicalizationContext}), and uses it as the result
- * of subject canonicalization.
- * 
- * <p>This action operates on a set of previously resolved attributes that are presumed to have been generated based
- * in some fashion on the content of the {@link SubjectCanonicalizationContext}.</p>
+ * A {@link SubjectCanonicalizer} that resolves attributes and then extracts a resolved {@link IdPAttribute} value
+ * from an {@link AttributeContext} child obtained via lookup function (by default a child of the
+ * {@link SubjectCanonicalizationContext}), and uses it as the result of subject canonicalization.
  * 
  * <p>String and scoped attribute values are supported.</p>
  * 
- * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
- * @event {@link AuthnEventIds#INVALID_SUBJECT}
- * @pre <pre>ProfileRequestContext.getSubcontext(SubjectCanonicalizationContext.class) != null</pre>
- * @post <pre>SubjectCanonicalizationContext.getPrincipalName() != null
- *  || SubjectCanonicalizationContext.getException() != null</pre>
+ * @since 5.2.0
  */
-public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCanonicalizationAction {
+public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCanonicalizer {
     
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(AttributeSourcedSubjectCanonicalization.class);
 
+    /** Attribute resolver service. */
+    @NonnullAfterInit private ReloadableService<AttributeResolver> resolverService;
+    
+    /** Condition controlling whether to run the attribute resolver. */
+    @Nonnull private Predicate<ProfileRequestContext> resolutionCondition;
+    
     /** Delimiter to use for scoped attribute serialization. */
     private char delimiter;
     
-    /** Whether to also check the original Subject for {@link IdPAttributePrincipal}s. */
+    /** Whether to also (or instead) check the original Subject for {@link IdPAttributePrincipal}s. */
     private boolean resolveFromSubject;
     
-    /** Indexed attributes pulled from subject. */
-    @Nonnull private Map<String,IdPAttribute> subjectSourcedAttributes;
+    /** Attributes to resolve if explicit. */
+    @Nonnull private Collection<String> attributesToResolve;
     
     /** Ordered list of attributes to look for and read from. */
     @Nonnull private List<String> attributeSourceIds;
         
-    /** Lookup strategy for {@link AttributeContext} to read from. */
-    @Nonnull private Function<ProfileRequestContext,AttributeContext> attributeContextLookupStrategy;
+    /** Strategy used to locate the identity of the issuer associated with the attribute resolution. */
+    @Nullable private Function<ProfileRequestContext,String> issuerLookupStrategy;
+
+    /** Strategy used to locate the identity of the recipient associated with the attribute resolution. */
+    @Nullable private Function<ProfileRequestContext,String> recipientLookupStrategy;
     
-    /** The context to read from. */
-    @Nullable private AttributeContext attributeCtx;
+    /** Strategy used to locate the principal name associated with the attribute resolution. */
+    @Nullable private Function<ProfileRequestContext,String> principalNameLookupStrategy;
     
     /** Constructor. */
     public AttributeSourcedSubjectCanonicalization() {
         delimiter = '@';
+        attributesToResolve = CollectionSupport.emptyList();
         attributeSourceIds = CollectionSupport.emptyList();
-        subjectSourcedAttributes = CollectionSupport.emptyMap();
         
-        attributeContextLookupStrategy =
-                new ChildContextLookup<>(AttributeContext.class).compose(
-                        new ChildContextLookup<>(SubjectCanonicalizationContext.class));
+        resolutionCondition = PredicateSupport.alwaysTrue();
+        
+        issuerLookupStrategy = new IssuerLookupFunction();
+        recipientLookupStrategy = new RelyingPartyIdLookupFunction();
+        
+        principalNameLookupStrategy =
+                new SubjectContextPrincipalLookupFunction().compose(
+                        new ChildContextLookup<>(SubjectContext.class));
+    }
+    
+    /**
+     * Set the {@link AttributeResolver} service instance to use.
+     * 
+     * @param service service to use
+     */
+    public void setAttributeResolver(@Nonnull final ReloadableService<AttributeResolver> service) {
+        checkSetterPreconditions();
+        resolverService = Constraint.isNotNull(service, "AttributeResolver service cannot be null");
+    }
+    
+    /**
+     * Set a condition to evaluate to decide whether to resolve attributes.
+     * 
+     * <p>Defaults to true.</p>
+     * 
+     * @param condition condition to set
+     */
+    public void setResolutionCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
+        checkSetterPreconditions();
+        resolutionCondition = Constraint.isNotNull(condition, "Attribute resolution condition cannot be null");
     }
     
     /**
      * Set the delimiter to use for serializing scoped attribute values.
      * 
+     * <p>Defaults to '@'.</p>
+     * 
      * @param ch delimiter to use
      */
     public void setScopedDelimiter(final char ch) {
@@ -110,15 +157,25 @@ public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCano
      * Whether to include any {@link IdPAttributePrincipal} objects found in the input {@link Subject}
      * when searching for a matching attribute ID.
      * 
-     * @param flag flag to set
+     * <p>Defaults to false.</p>
      * 
-     * @since 4.1.0
+     * @param flag flag to set
      */
     public void setResolveFromSubject(final boolean flag) {
         checkSetterPreconditions();
         resolveFromSubject = flag;
     }
     
+    /**
+     * Set the attribute IDs to resolve.
+     * 
+     * @param ids   attribute IDs to resolve
+     */
+    public void setAttributesToResolve(@Nonnull final Collection<String> ids) {
+        checkSetterPreconditions();
+        attributesToResolve = StringSupport.normalizeStringCollection(ids);
+    }
+    
     /**
      * Set the attribute IDs to read from in order of preference.
      * 
@@ -130,15 +187,33 @@ public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCano
     }
     
     /**
-     * Set the lookup strategy for the {@link AttributeContext} to read from.
+     * Set the strategy used to lookup the issuer for this attribute resolution.
+     * 
+     * @param strategy  lookup strategy
+     */
+    public void setIssuerLookupStrategy(@Nullable final Function<ProfileRequestContext,String> strategy) {
+        checkSetterPreconditions();
+        issuerLookupStrategy = strategy;
+    }
+
+    /**
+     * Set the strategy used to lookup the recipient for this attribute resolution.
      * 
      * @param strategy  lookup strategy
      */
-    public void setAttributeContextLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext,AttributeContext> strategy) {
+    public void setRecipientLookupStrategy(@Nullable final Function<ProfileRequestContext,String> strategy) {
+        checkSetterPreconditions();
+        recipientLookupStrategy = strategy;
+    }
+    
+    /**
+     * Set the strategy used to locate the principal name for this attribute resolution.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setPrincipalNameLookupStrategy(@Nullable final Function<ProfileRequestContext,String> strategy) {
         checkSetterPreconditions();
-        attributeContextLookupStrategy = Constraint.isNotNull(strategy,
-                "AttributeContext lookup strategy cannot be null");
+        principalNameLookupStrategy = strategy;
     }
     
     /** {@inheritDoc} */
@@ -148,44 +223,38 @@ public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCano
         
         if (attributeSourceIds.isEmpty()) {
             throw new ComponentInitializationException("Attribute source ID list cannot be empty");
+        } else if (resolverService == null) {
+            throw new ComponentInitializationException("AttributeResolver service cannot be null");
         }
     }
     
     /** {@inheritDoc} */
     @Override
-    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext, 
-            @Nonnull final SubjectCanonicalizationContext c14nContext) {
+    @Nullable protected String doApply(@Nonnull final SubjectCanonicalizationContext c14nContext) {
 
-        if (!super.doPreExecute(profileRequestContext, c14nContext)) {
-            return false;
+        // Find first PRC walking up from c14n context.
+        final ProfileRequestContext profileRequestContext =
+                new RecursiveTypedParentContextLookup<>(ProfileRequestContext.class).apply(c14nContext);
+        if (profileRequestContext == null) {
+            log.error("{} Unable to locate ProfileRequestContext", getLogPrefix());
+            return AuthnEventIds.INVALID_SUBJECT_C14N_CTX;
         }
-        
+
+        final Map<String,IdPAttribute> subjectSourcedAttributes;
         if (resolveFromSubject) {
-            final Subject subject =
-                    Constraint.isNotNull(c14nContext.getSubject(), "Expected a non-null Subject");
-            final Set<IdPAttributePrincipal> subjectSourced = subject.getPrincipals(IdPAttributePrincipal.class);
-            if (subjectSourced != null && !subjectSourced.isEmpty()) {
-                subjectSourcedAttributes = new HashMap<>(subjectSourced.size());
-                subjectSourced.forEach(a -> subjectSourcedAttributes.put(a.getAttribute().getId(), a.getAttribute()));
-            }
+            subjectSourcedAttributes = extractSubjectSourcedAttributes(c14nContext);
+        } else {
+            subjectSourcedAttributes = CollectionSupport.emptyMap();
         }
         
-        final AttributeContext aCtx = attributeCtx = attributeContextLookupStrategy.apply(profileRequestContext);
-        if (subjectSourcedAttributes.isEmpty() && (aCtx == null || aCtx.getIdPAttributes().isEmpty())) {
-            log.warn("{} No attributes found, canonicalization not possible", getLogPrefix());
-            c14nContext.setException(new SubjectCanonicalizationException("No attributes were found"));
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_SUBJECT);
-            return false;
+        final Map<String,IdPAttribute> resolvedAttributes;
+        if (resolutionCondition.test(profileRequestContext)) {
+            resolvedAttributes = resolveAttributes(profileRequestContext, c14nContext);
+        } else {
+            log.debug("{} Skipping attribute resolution based on configured condition", getLogPrefix());
+            resolvedAttributes = CollectionSupport.emptyMap();
         }
         
-        return true;
-    }
-    
-    /** {@inheritDoc} */
-    @Override
-    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext, 
-            @Nonnull final SubjectCanonicalizationContext c14nContext) {
-        
         for (final String id : attributeSourceIds) {
             
             IdPAttribute attr = subjectSourcedAttributes.get(id);
@@ -193,25 +262,95 @@ public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCano
                 final String result = findValue(attr);
                 if (result != null) {
                     c14nContext.setPrincipalName(result);
-                    return;
+                    return EventIds.PROCEED_EVENT_ID;
                 }
-            } else if (attributeCtx != null) {
-                attr = attributeCtx.getIdPAttributes().get(id);
+            } else {
+                attr = resolvedAttributes.get(id);
                 if (attr != null) {
                     final String result = findValue(attr);
                     if (result != null) {
                         c14nContext.setPrincipalName(result);
-                        return;
+                        return EventIds.PROCEED_EVENT_ID;
                     }
                 }
             }
         }
         
         log.info("{} Attribute sources {} did not produce a usable identifier", getLogPrefix(), attributeSourceIds);
-        c14nContext.setException(new SubjectCanonicalizationException("No usable attribute values were found"));
-        ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_SUBJECT);
+        return AuthnEventIds.INVALID_SUBJECT;
+    }
+    
+    /**
+     * Extract and populate a map of any attributes found in the Subject.
+     * 
+     * @param c14nContext source of Subject
+     * 
+     * @return possibly empty map
+     */
+    @Nonnull private Map<String,IdPAttribute> extractSubjectSourcedAttributes(
+            @Nonnull final SubjectCanonicalizationContext c14nContext) {
+        
+        final Subject subject = Constraint.isNotNull(c14nContext.getSubject(), "Expected a non-null Subject");
+        
+        final Set<IdPAttributePrincipal> subjectSourced = subject.getPrincipals(IdPAttributePrincipal.class);
+        if (subjectSourced != null && !subjectSourced.isEmpty()) {
+            final Map<String,IdPAttribute> subjectSourcedAttributes = new HashMap<>(subjectSourced.size());
+            subjectSourced.forEach(a -> subjectSourcedAttributes.put(a.getAttribute().getId(), a.getAttribute()));
+            return subjectSourcedAttributes;
+        }
+        
+        return CollectionSupport.emptyMap();
     }
+    
+    /**
+     * Perform attribute resolution inline.
+     * 
+     * @param profileRequestContext profile request context
+     * @param c14nContext subject c14n context
+     * 
+     * @return the resolved map of attributes
+     */
+    private Map<String,IdPAttribute> resolveAttributes(@Nonnull final ProfileRequestContext profileRequestContext, 
+            @Nonnull final SubjectCanonicalizationContext c14nContext) {
+        
+        final AttributeResolutionContext resolutionContext =
+                c14nContext.ensureSubcontext(AttributeResolutionContext.class);
+        
+        resolutionContext.setProfileRequestContextLookupStrategy(FunctionSupport.constant(profileRequestContext));
+
+        final var flow = c14nContext.getAttemptedFlow();
+        if (flow != null) {
+            resolutionContext.setResolutionLabel(flow.ensureId());
+        }
+        resolutionContext.setRequestedIdPAttributeNames(attributesToResolve);
+        if (null != principalNameLookupStrategy) {
+            resolutionContext.setPrincipal(principalNameLookupStrategy.apply(profileRequestContext));
+        }
+        if (recipientLookupStrategy != null) {
+            resolutionContext.setAttributeRecipientID(recipientLookupStrategy.apply(profileRequestContext));
+        }
+        if (issuerLookupStrategy != null) {
+            resolutionContext.setAttributeIssuerID(issuerLookupStrategy.apply(profileRequestContext));
+        }
+
+        try (final ServiceableComponent<AttributeResolver> component =
+                resolverService.getServiceableComponent()) {
+            final AttributeResolver attributeResolver = component.getComponent();
+            attributeResolver.resolveAttributes(resolutionContext);
+            c14nContext.removeSubcontext(resolutionContext);
 
+            return resolutionContext.getResolvedIdPAttributes();
+        } catch (final ResolutionException e) {
+            c14nContext.removeSubcontext(resolutionContext);
+            log.error("{} Error resolving attributes", getLogPrefix(), e);
+            return CollectionSupport.emptyMap();
+        } catch (final ServiceException e) {
+            c14nContext.removeSubcontext(resolutionContext);
+            log.error("{} Invalid AttributeResolver configuration", getLogPrefix(), e);
+            return CollectionSupport.emptyMap();
+        }
+    }
+    
     /**
      * Check for a compatible value in the input attribute.
      * 
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalization.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationAction.java
similarity index 97%
copy from idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalization.java
copy to idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationAction.java
index 54ef3d64d..2edd580e4 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalization.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationAction.java
@@ -56,16 +56,19 @@ import net.shibboleth.shared.primitive.StringSupport;
  * 
  * <p>String and scoped attribute values are supported.</p>
  * 
+ * @deprecated
+ * 
  * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
  * @event {@link AuthnEventIds#INVALID_SUBJECT}
  * @pre <pre>ProfileRequestContext.getSubcontext(SubjectCanonicalizationContext.class) != null</pre>
  * @post <pre>SubjectCanonicalizationContext.getPrincipalName() != null
  *  || SubjectCanonicalizationContext.getException() != null</pre>
  */
-public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCanonicalizationAction {
+ at Deprecated(since="5.2.0", forRemoval=true)
+public class AttributeSourcedSubjectCanonicalizationAction extends AbstractSubjectCanonicalizationAction {
     
     /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(AttributeSourcedSubjectCanonicalization.class);
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AttributeSourcedSubjectCanonicalizationAction.class);
 
     /** Delimiter to use for scoped attribute serialization. */
     private char delimiter;
@@ -86,7 +89,7 @@ public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCano
     @Nullable private AttributeContext attributeCtx;
     
     /** Constructor. */
-    public AttributeSourcedSubjectCanonicalization() {
+    public AttributeSourcedSubjectCanonicalizationAction() {
         delimiter = '@';
         attributeSourceIds = CollectionSupport.emptyList();
         subjectSourcedAttributes = CollectionSupport.emptyMap();
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationActionTest.java
similarity index 95%
copy from idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationTest.java
copy to idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationActionTest.java
index 4a1dce700..d9f671cbf 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationActionTest.java
@@ -33,23 +33,23 @@ import net.shibboleth.idp.profile.testing.ActionTestingSupport;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
 
-/** {@link AttributeSourcedSubjectCanonicalization} unit test. */
- at SuppressWarnings("javadoc")
-public class AttributeSourcedSubjectCanonicalizationTest extends BaseAuthenticationContextTest {
+/** {@link AttributeSourcedSubjectCanonicalizationAction} unit test. */
+ at SuppressWarnings({"javadoc", "removal"})
+public class AttributeSourcedSubjectCanonicalizationActionTest extends BaseAuthenticationContextTest {
     
-    private AttributeSourcedSubjectCanonicalization action; 
+    private AttributeSourcedSubjectCanonicalizationAction action; 
     
     @BeforeMethod
     public void setUp() throws ComponentInitializationException {
         super.setUp();
         
-        action = new AttributeSourcedSubjectCanonicalization();
+        action = new AttributeSourcedSubjectCanonicalizationAction();
         action.setAttributeSourceIds(CollectionSupport.listOf("attr1", "attr2"));
     }
     
     @Test(expectedExceptions=ComponentInitializationException.class)
     public void testNoSources() throws ComponentInitializationException {
-        action = new AttributeSourcedSubjectCanonicalization();
+        action = new AttributeSourcedSubjectCanonicalizationAction();
         action.initialize();
     }
     
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationTest.java
index 4a1dce700..22f8b6708 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationTest.java
@@ -14,9 +14,14 @@
 
 package net.shibboleth.idp.authn.impl;
 
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.security.auth.Subject;
 
-import org.springframework.webflow.execution.Event;
+import org.opensaml.profile.action.EventIds;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -25,53 +30,74 @@ import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
 import net.shibboleth.idp.attribute.StringAttributeValue;
 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.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.SubjectCanonicalizationFlowDescriptor;
 import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
 import net.shibboleth.idp.authn.impl.testing.BaseAuthenticationContextTest;
 import net.shibboleth.idp.authn.principal.IdPAttributePrincipal;
-import net.shibboleth.idp.profile.testing.ActionTestingSupport;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.testing.MockReloadableService;
 
 /** {@link AttributeSourcedSubjectCanonicalization} unit test. */
 @SuppressWarnings("javadoc")
 public class AttributeSourcedSubjectCanonicalizationTest extends BaseAuthenticationContextTest {
     
+    private SubjectCanonicalizationFlowDescriptor flow;
     private AttributeSourcedSubjectCanonicalization action; 
+    private Collection<IdPAttribute> mockData;
+
     
     @BeforeMethod
     public void setUp() throws ComponentInitializationException {
         super.setUp();
         
+        flow = new SubjectCanonicalizationFlowDescriptor();
+        flow.setId("c14n/test");
+        flow.initialize();
+        
+        mockData = new ArrayList<>();
+        
         action = new AttributeSourcedSubjectCanonicalization();
+        action.setId("test");
         action.setAttributeSourceIds(CollectionSupport.listOf("attr1", "attr2"));
+        final MockReloadableService<AttributeResolver> mockResolver =
+                new MockReloadableService<AttributeResolver>(new MockAttributeResolver());
+        action.setAttributeResolver(mockResolver);
+        
+        prc.ensureSubcontext(SubjectCanonicalizationContext.class).setAttemptedFlow(flow);
     }
     
     @Test(expectedExceptions=ComponentInitializationException.class)
     public void testNoSources() throws ComponentInitializationException {
         action = new AttributeSourcedSubjectCanonicalization();
+        action.setId("test");
         action.initialize();
     }
     
     @Test public void testNoContext() throws ComponentInitializationException {
         action.initialize();
         
-        final Event event = action.execute(src);
+        prc.removeSubcontext(SubjectCanonicalizationContext.class);
         
-        ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_SUBJECT_C14N_CTX);
+        final String event = action.apply(null);
+        
+        Assert.assertEquals(event, AuthnEventIds.INVALID_SUBJECT_C14N_CTX);
     }
 
     @Test public void testNoAttributes() throws ComponentInitializationException {
         action.initialize();
 
-        Subject subject = new Subject();
-        prc.ensureSubcontext(SubjectCanonicalizationContext.class).setSubject(subject);
+        final Subject subject = new Subject();
+        final var scc = prc.ensureSubcontext(SubjectCanonicalizationContext.class).setSubject(subject);
         
-        final Event event = action.execute(src);
+        final String event = action.apply(scc);
         
-        ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_SUBJECT);
-        SubjectCanonicalizationContext scc = prc.getSubcontext(SubjectCanonicalizationContext.class);
-        assert scc != null && scc.getException()!=null;
+        Assert.assertEquals(event, AuthnEventIds.INVALID_SUBJECT);
+        Assert.assertNull(scc.getPrincipalName());
     }
 
     @Test public void testNoSubjectSourcedAttributes() throws ComponentInitializationException {
@@ -79,13 +105,12 @@ public class AttributeSourcedSubjectCanonicalizationTest extends BaseAuthenticat
         action.initialize();
         
         Subject subject = new Subject();
-        prc.ensureSubcontext(SubjectCanonicalizationContext.class).setSubject(subject);
+        final var scc = prc.ensureSubcontext(SubjectCanonicalizationContext.class).setSubject(subject);
         
-        final Event event = action.execute(src);
+        final String event = action.apply(scc);
         
-        ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_SUBJECT);
-        SubjectCanonicalizationContext scc = prc.getSubcontext(SubjectCanonicalizationContext.class);
-        assert scc != null && scc.getException()!=null;
+        Assert.assertEquals(event, AuthnEventIds.INVALID_SUBJECT);
+        Assert.assertNull(scc.getPrincipalName());
     }
 
     @Test public void testSuccess() throws ComponentInitializationException {
@@ -94,15 +119,15 @@ public class AttributeSourcedSubjectCanonicalizationTest extends BaseAuthenticat
         final IdPAttribute inputAttribute = new IdPAttribute("attr2");
         inputAttribute.setValues(CollectionSupport.singletonList(new StringAttributeValue("foo")));
         
-        final SubjectCanonicalizationContext sc = prc.ensureSubcontext(SubjectCanonicalizationContext.class);
-        sc.setSubject(new Subject());
+        final SubjectCanonicalizationContext scc = prc.ensureSubcontext(SubjectCanonicalizationContext.class);
+        scc.setSubject(new Subject());
         
-        sc.ensureSubcontext(AttributeContext.class).setIdPAttributes(CollectionSupport.singleton(inputAttribute));
+        mockData.add(inputAttribute);
         
-        final Event event = action.execute(src);
+        final String event = action.apply(scc);
         
-        ActionTestingSupport.assertProceedEvent(event);
-        Assert.assertEquals(sc.getPrincipalName(), "foo");
+        Assert.assertEquals(event, EventIds.PROCEED_EVENT_ID);
+        Assert.assertEquals(scc.getPrincipalName(), "foo");
     }
 
     @Test public void testSubjectSourcedSuccess() throws ComponentInitializationException {
@@ -111,15 +136,15 @@ public class AttributeSourcedSubjectCanonicalizationTest extends BaseAuthenticat
         
         final IdPAttribute inputAttribute = new IdPAttribute("attr2");
         inputAttribute.setValues(CollectionSupport.singletonList(new StringAttributeValue("foo")));
-        final SubjectCanonicalizationContext sc = prc.ensureSubcontext(SubjectCanonicalizationContext.class);
         final Subject subject = new Subject();
-        sc.setSubject(subject);
         subject.getPrincipals().add(new IdPAttributePrincipal(inputAttribute));
+        final SubjectCanonicalizationContext scc = prc.ensureSubcontext(SubjectCanonicalizationContext.class);
+        scc.setSubject(subject);
 
-        final Event event = action.execute(src);
+        final String event = action.apply(scc);
         
-        ActionTestingSupport.assertProceedEvent(event);
-        Assert.assertEquals(sc.getPrincipalName(), "foo");
+        Assert.assertEquals(event, EventIds.PROCEED_EVENT_ID);
+        Assert.assertEquals(scc.getPrincipalName(), "foo");
     }
 
     @Test public void testSubjectSourcedScopedSuccess() throws ComponentInitializationException {
@@ -128,15 +153,15 @@ public class AttributeSourcedSubjectCanonicalizationTest extends BaseAuthenticat
         
         final IdPAttribute inputAttribute = new IdPAttribute("attr2");
         inputAttribute.setValues(CollectionSupport.singletonList(new ScopedStringAttributeValue("foo", "scope")));
-        final SubjectCanonicalizationContext sc = prc.ensureSubcontext(SubjectCanonicalizationContext.class);
         final Subject subject = new Subject();
-        sc.setSubject(subject);
         subject.getPrincipals().add(new IdPAttributePrincipal(inputAttribute));
+        final SubjectCanonicalizationContext scc = prc.ensureSubcontext(SubjectCanonicalizationContext.class);
+        scc.setSubject(subject);
 
-        final Event event = action.execute(src);
+        final String event = action.apply(scc);
         
-        ActionTestingSupport.assertProceedEvent(event);
-        Assert.assertEquals(sc.getPrincipalName(), "foo at scope");
+        Assert.assertEquals(event, EventIds.PROCEED_EVENT_ID);
+        Assert.assertEquals(scc.getPrincipalName(), "foo at scope");
     }
     
     @Test public void testDualSubjectSourcedSuccess() throws ComponentInitializationException {
@@ -149,26 +174,26 @@ public class AttributeSourcedSubjectCanonicalizationTest extends BaseAuthenticat
         final IdPAttribute attr2bar = new IdPAttribute("attr2");
         attr2bar.setValues(CollectionSupport.singletonList(new StringAttributeValue("bar")));
 
-        final SubjectCanonicalizationContext sc = prc.ensureSubcontext(SubjectCanonicalizationContext.class);
         final Subject subject = new Subject();
-        sc.setSubject(subject);
         subject.getPrincipals().add(new IdPAttributePrincipal(attr2));
+        final SubjectCanonicalizationContext scc = prc.ensureSubcontext(SubjectCanonicalizationContext.class);
+        scc.setSubject(subject);
         
-        sc.ensureSubcontext(AttributeContext.class).setIdPAttributes(CollectionSupport.singleton(attr2bar));
+        mockData.add(attr2bar);
         
-        Event event = action.execute(src);
+        String event = action.apply(scc);
         
-        ActionTestingSupport.assertProceedEvent(event);
-        Assert.assertEquals(sc.getPrincipalName(), "foo");
+        Assert.assertEquals(event, EventIds.PROCEED_EVENT_ID);
+        Assert.assertEquals(scc.getPrincipalName(), "foo");
 
         subject.getPrincipals().clear();
         subject.getPrincipals().add(new IdPAttributePrincipal(attr2bar));
-        sc.ensureSubcontext(AttributeContext.class).setIdPAttributes(CollectionSupport.singleton(attr2));
+        scc.ensureSubcontext(AttributeContext.class).setIdPAttributes(CollectionSupport.singleton(attr2));
 
-        event = action.execute(src);
+        event = action.apply(scc);
         
-        ActionTestingSupport.assertProceedEvent(event);
-        Assert.assertEquals(sc.getPrincipalName(), "bar");
+        Assert.assertEquals(event, EventIds.PROCEED_EVENT_ID);
+        Assert.assertEquals(scc.getPrincipalName(), "bar");
     }
 
     @Test public void testDualSubjectSourcedSuccess2() throws ComponentInitializationException {
@@ -181,17 +206,34 @@ public class AttributeSourcedSubjectCanonicalizationTest extends BaseAuthenticat
         final IdPAttribute attr1 = new IdPAttribute("attr1");
         attr1.setValues(CollectionSupport.singletonList(new StringAttributeValue("foo")));
 
-        final SubjectCanonicalizationContext sc = prc.ensureSubcontext(SubjectCanonicalizationContext.class);
         final Subject subject = new Subject();
-        sc.setSubject(subject);
         subject.getPrincipals().add(new IdPAttributePrincipal(attr2));
+        final SubjectCanonicalizationContext scc = prc.ensureSubcontext(SubjectCanonicalizationContext.class);
+        scc.setSubject(subject);
         
-        sc.ensureSubcontext(AttributeContext.class).setIdPAttributes(CollectionSupport.singleton(attr1));
+        mockData.add(attr1);
         
-        final Event event = action.execute(src);
+        final String event = action.apply(scc);
         
-        ActionTestingSupport.assertProceedEvent(event);
-        Assert.assertEquals(sc.getPrincipalName(), "foo");
+        Assert.assertEquals(event, EventIds.PROCEED_EVENT_ID);
+        Assert.assertEquals(scc.getPrincipalName(), "foo");
+    }
+    
+    public class MockAttributeResolver implements AttributeResolver {
+                
+        /** {@inheritDoc} */
+        @Nullable public String getId() {
+            return "mockResolver";
+        }
+
+        /** {@inheritDoc} */
+        public void resolveAttributes(@Nonnull final AttributeResolutionContext resolutionContext)
+                throws ResolutionException {
+            
+            Assert.assertEquals(resolutionContext.getResolutionLabel(), "c14n/test");
+            
+            resolutionContext.setResolvedIdPAttributes(mockData);
+        }
     }
     
 }
\ No newline at end of file
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/subject-c14n-system.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/subject-c14n-system.xml
index 3266d9b96..c20c77f3f 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/subject-c14n-system.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/subject-c14n-system.xml
@@ -29,10 +29,15 @@
         c:_0="#{'%{idp.c14n.x500.subjectAltNameTypes:}'.trim()}" />
     <bean id="shibboleth.c14n.x500.ObjectIDs" parent="shibboleth.CommaDelimStringArray"
         c:_0="#{'%{idp.c14n.x500.objectIDs:}'.trim()}" />
+    <bean id="shibboleth.c14n.attribute.AttributesToResolve" parent="shibboleth.CommaDelimStringArray"
+        c:_0="#{'%{idp.c14n.attribute.attributesToResolve:}'.trim()}" />
+    <bean id="shibboleth.c14n.attribute.AttributeSourceIds" parent="shibboleth.CommaDelimStringArray"
+        c:_0="#{'%{idp.c14n.attribute.attributeSourceIds:%{idp.c14n.attribute.attributesToResolve:}}'.trim()}" />
 
     <!-- Legacy compatibility. -->
     <import resource="conditional:%{idp.home}/conf/c14n/simple-subject-c14n-config.xml" />
     <import resource="conditional:%{idp.home}/conf/c14n/x500-subject-c14n-config.xml" />
+    <import resource="conditional:%{idp.home}/conf/c14n/attribute-sourced-subject-c14n-config.xml" />
 
     <!-- Parent beans for defining flows of the three typical types. -->
 
@@ -49,7 +54,7 @@
 
     <bean id="c14n/simple" parent="shibboleth.PostLoginSubjectCanonicalizationFlow">
         <property name="implementation">
-            <bean id="SimpleSubjectCanonicalization" class="net.shibboleth.idp.authn.impl.SimpleSubjectCanonicalization"
+            <bean id="SimpleC14N" class="net.shibboleth.idp.authn.impl.SimpleSubjectCanonicalization"
                 p:lowercase="#{getObject('shibboleth.c14n.simple.Lowercase') ?: %{idp.c14n.simple.lowercase:false}}"
                 p:uppercase="#{getObject('shibboleth.c14n.simple.Uppercase') ?: %{idp.c14n.simple.uppercase:false}}"
                 p:trim="#{getObject('shibboleth.c14n.simple.Trim') ?: %{idp.c14n.simple.trim:true}}"
@@ -59,7 +64,7 @@
 
     <bean id="c14n/x500" parent="shibboleth.PostLoginSubjectCanonicalizationFlow">
         <property name="implementation">
-            <bean id="X500SubjectCanonicalization" class="net.shibboleth.idp.authn.impl.X500SubjectCanonicalization"
+            <bean id="X500C14N" class="net.shibboleth.idp.authn.impl.X500SubjectCanonicalization"
                 p:subjectAltNameTypes-ref="shibboleth.c14n.x500.SubjectAltNameTypes"
                 p:objectIds-ref="shibboleth.c14n.x500.ObjectIDs"
                 p:lowercase="#{getObject('shibboleth.c14n.x500.Lowercase') ?: %{idp.c14n.x500.lowercase:false}}"
@@ -69,12 +74,31 @@
         </property>
     </bean>
 
+    <bean id="c14n/attribute-sourced" parent="shibboleth.PostLoginSubjectCanonicalizationFlow">
+        <property name="implementation">
+            <bean id="AttributeSourcedC14N" parent="shibboleth.AttributeSourcedC14N" />
+        </property>
+    </bean>
+
+    <bean id="shibboleth.AttributeSourcedC14N"
+        class="net.shibboleth.idp.authn.impl.AttributeSourcedSubjectCanonicalization"
+        p:attributeResolver-ref="shibboleth.AttributeResolverService"
+        p:attributesToResolve-ref="shibboleth.c14n.attribute.AttributesToResolve"
+        p:attributeSourceIds-ref="shibboleth.c14n.attribute.AttributeSourceIds"
+        p:resolveFromSubject="%{idp.c14n.attribute.resolveFromSubject:false}"
+        p:resolutionCondition-ref="#{'%{idp.c14n.attribute.resolutionCondition:shibboleth.Conditions.TRUE}'.trim()}"
+        p:principalNameLookupStrategy="#{getObject('shibboleth.c14n.attribute.PrincipalNameLookupStrategy')}"
+        p:lowercase="%{idp.c14n.attribute.lowercase:false}"
+        p:uppercase="%{idp.c14n.attribute.uppercase:false}"
+        p:trim="%{idp.c14n.attribute.trim:true}"
+        p:transforms="#{getObject('shibboleth.c14n.attribute.Transforms')}" />
+
     <!-- The rest are machinery for handling NameIDs, mostly in protocol requests, also for proxying. -->
 
     <bean id="c14n/SAML2Persistent" parent="shibboleth.AbstractSAML2C14NFlowBean"
             p:formats="#{ {'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'} }">
         <property name="implementation">
-            <bean id="NameIDCanonicalization/SAML2Persistent" class="net.shibboleth.idp.saml.nameid.impl.NameIDCanonicalization">
+            <bean id="NameIDC14N/SAML2Persistent" class="net.shibboleth.idp.saml.nameid.impl.NameIDCanonicalization">
                 <property name="decoder">
                     <bean class="net.shibboleth.idp.saml.nameid.impl.StoredPersistentIdDecoder"
                         p:persistentIdStore="#{getObject('%{idp.persistentId.store:undefined}'.trim())}"
@@ -87,7 +111,7 @@
     <bean id="c14n/SAML2Transient" parent="shibboleth.AbstractSAML2C14NFlowBean" 
             p:formats="#{ {'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'} }">
         <property name="implementation">
-            <bean id="NameIDCanonicalization/SAML2Transient" class="net.shibboleth.idp.saml.nameid.impl.NameIDCanonicalization">
+            <bean id="NameIDC14N/SAML2Transient" class="net.shibboleth.idp.saml.nameid.impl.NameIDCanonicalization">
                 <property name="decoder">
                     <bean class="net.shibboleth.idp.saml.nameid.impl.TransientNameIDDecoder"
                         p:idStore-ref="shibboleth.StorageService" />
@@ -99,7 +123,7 @@
     <bean id="c14n/SAML2CryptoTransient" parent="shibboleth.AbstractSAML2C14NFlowBean"
             p:formats="#{ {'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'} }">
         <property name="implementation">
-            <bean id="NameIDCanonicalization/SAML2CryptoTransient" class="net.shibboleth.idp.saml.nameid.impl.NameIDCanonicalization">
+            <bean id="NameIDC14N/SAML2CryptoTransient" class="net.shibboleth.idp.saml.nameid.impl.NameIDCanonicalization">
                 <property name="decoder">
                     <bean class="net.shibboleth.idp.saml.nameid.impl.CryptoTransientNameIDDecoder"
                         p:dataSealer="#{(environment.containsProperty('idp.sealer.storeResource') or environment.containsProperty('idp.sealer.keyStrategy')) ? getObject('shibboleth.DataSealer') : null}" />
@@ -111,7 +135,7 @@
     <bean id="c14n/SAML1Transient" parent="shibboleth.AbstractSAML1C14NFlowBean"
             p:formats="#{ {'urn:mace:shibboleth:1.0:nameIdentifier'} }"> 
         <property name="implementation">
-            <bean id="NameIdentifierCanonicalization/SAML1Transient" class="net.shibboleth.idp.saml.nameid.impl.NameIdentifierCanonicalization">
+            <bean id="NameIDC14N/SAML1Transient" class="net.shibboleth.idp.saml.nameid.impl.NameIdentifierCanonicalization">
                 <property name="decoder">
                     <bean class="net.shibboleth.idp.saml.nameid.impl.TransientNameIdentifierDecoder"
                         p:idStore-ref="shibboleth.StorageService" />
@@ -123,7 +147,7 @@
     <bean id="c14n/SAML1CryptoTransient" parent="shibboleth.AbstractSAML1C14NFlowBean"
             p:formats="#{ {'urn:mace:shibboleth:1.0:nameIdentifier'} }">
         <property name="implementation">
-            <bean id="NameIdentifierCanonicalization/SAML1CryptoTransient" class="net.shibboleth.idp.saml.nameid.impl.NameIdentifierCanonicalization">
+            <bean id="NameIDC14N/SAML1CryptoTransient" class="net.shibboleth.idp.saml.nameid.impl.NameIdentifierCanonicalization">
                 <property name="decoder">
                     <bean class="net.shibboleth.idp.saml.nameid.impl.CryptoTransientNameIdentifierDecoder"
                         p:dataSealer="#{(environment.containsProperty('idp.sealer.storeResource') or environment.containsProperty('idp.sealer.keyStrategy')) ? getObject('shibboleth.DataSealer') : null}" />
@@ -142,7 +166,7 @@
             p:activationCondition-ref="shibboleth.NameTransformPredicate"
             p:formats-ref="shibboleth.NameTransformFormats">
         <property name="implementation">
-            <bean id="NameIDCanonicalization/SAML2Transform" class="net.shibboleth.idp.saml.nameid.impl.NameIDCanonicalization"
+            <bean id="NameIDC14N/SAML2Transform" class="net.shibboleth.idp.saml.nameid.impl.NameIDCanonicalization"
                 p:decoder="#{getObject('shibboleth.SAML2Transform.NameIDDecoder') ?: getObject('TransformingNameIDDecoder')}" />
         </property>
     </bean>
@@ -157,7 +181,7 @@
             p:activationCondition-ref="shibboleth.ProxyNameTransformPredicate"
             p:formats-ref="shibboleth.ProxyNameTransformFormats">
         <property name="implementation">
-            <bean id="NameIDCanonicalization/SAML2ProxyTransform" class="net.shibboleth.idp.saml.nameid.impl.NameIDCanonicalization"
+            <bean id="NameIDC14N/SAML2ProxyTransform" class="net.shibboleth.idp.saml.nameid.impl.NameIDCanonicalization"
                 p:decoder="#{getObject('shibboleth.SAML2ProxyTransform.NameIDDecoder') ?: getObject('ProxyTransformingNameIDDecoder')}" />
         </property>
     </bean>
@@ -172,7 +196,7 @@
             p:activationCondition-ref="shibboleth.NameTransformPredicate"
             p:formats-ref="shibboleth.NameTransformFormats">
         <property name="implementation">
-            <bean id="NameIdentifierCanonicalization/SAML1Transform" class="net.shibboleth.idp.saml.nameid.impl.NameIdentifierCanonicalization"
+            <bean id="NameIDC14N/SAML1Transform" class="net.shibboleth.idp.saml.nameid.impl.NameIdentifierCanonicalization"
                 p:decoder="#{getObject('shibboleth.SAML1Transform.NameIdentifierDecoder') ?: getObject('TransformingNameIdentifierDecoder')}" />
         </property>
     </bean>
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/attribute-sourced-subject-c14n-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/attribute-sourced-subject-c14n-beans.xml
index 538de7961..2fb0e2ab9 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/attribute-sourced-subject-c14n-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/attribute-sourced-subject-c14n-beans.xml
@@ -34,7 +34,7 @@
     </bean>
     
     <bean id="AttributeSourcedSubjectCanonicalization"
-        class="net.shibboleth.idp.authn.impl.AttributeSourcedSubjectCanonicalization" scope="prototype"
+        class="net.shibboleth.idp.authn.impl.AttributeSourcedSubjectCanonicalizationAction" scope="prototype"
         p:attributeSourceIds-ref="shibboleth.c14n.attribute.AttributeSourceIds"
         p:resolveFromSubject="%{idp.c14n.attribute.resolveFromSubject:false}"
         p:lowercase="#{getObject('shibboleth.c14n.attribute.Lowercase') ?: %{idp.c14n.attribute.lowercase:false}}"

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


More information about the commits mailing list