[java-identity-provider] branch main updated: IDP-2353 - C14n flow that runs a deployer-defined bean
Scott Cantor
cantor.2 at osu.edu
Tue Feb 25 20:51:39 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=0c0581726d269851795d3c32ec6bbaa6096fef04
The following commit(s) were added to refs/heads/main by this push:
new 0c0581726 IDP-2353 - C14n flow that runs a deployer-defined bean
0c0581726 is described below
commit 0c0581726d269851795d3c32ec6bbaa6096fef04
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Feb 25 15:51:32 2025 -0500
IDP-2353 - C14n flow that runs a deployer-defined bean
https://shibboleth.atlassian.net/browse/IDP-2353
Revamp c14n to support direct bean execution in place of subflows.
Reimplement "c14n/simple" as a bean and adjust wiring for compatibility.
---
.../idp/authn/AbstractSubjectCanonicalizer.java | 209 +++++++++++++++++++++
.../SubjectCanonicalizationFlowDescriptor.java | 36 +++-
.../shibboleth/idp/authn/SubjectCanonicalizer.java | 38 ++++
.../impl/SelectSubjectCanonicalizationFlow.java | 41 +++-
.../authn/impl/SimpleSubjectCanonicalization.java | 104 ++++------
.../impl/SimpleSubjectCanonicalizationTest.java | 37 ++--
.../shibboleth/idp/conf/subject-c14n-system.xml | 10 +
.../net/shibboleth/idp/conf/webflow-config.xml | 1 -
.../idp/flows/c14n/simple-subject-c14n-beans.xml | 24 ---
.../idp/flows/c14n/simple-subject-c14n-flow.xml | 17 --
.../idp/flows/c14n/subject-c14n-flow.xml | 6 +
11 files changed, 381 insertions(+), 142 deletions(-)
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AbstractSubjectCanonicalizer.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AbstractSubjectCanonicalizer.java
new file mode 100644
index 000000000..0e1e36f15
--- /dev/null
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AbstractSubjectCanonicalizer.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.authn;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.slf4j.Logger;
+
+import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.collection.Pair;
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * A base class for "stand-alone" subject c14n implementations.
+ */
+public abstract class AbstractSubjectCanonicalizer extends AbstractIdentifiableInitializableComponent
+ implements SubjectCanonicalizer {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractSubjectCanonicalizer.class);
+
+ /** Cached log prefix. */
+ @NonnullAfterInit private String logPrefix;
+
+ /** Match patterns and replacement strings to apply. */
+ @Nonnull private List<Pair<Pattern,String>> transforms;
+
+ /** Convert to uppercase prior to transforms? */
+ private boolean uppercase;
+
+ /** Convert to lowercase prior to transforms? */
+ private boolean lowercase;
+
+ /** Trim prior to transforms? */
+ private boolean trim;
+
+ /** Constructor. */
+ public AbstractSubjectCanonicalizer() {
+ transforms = CollectionSupport.emptyList();
+
+ uppercase = false;
+ lowercase = false;
+ trim = false;
+ }
+
+ /**
+ * A collection of regular expression and replacement pairs.
+ *
+ * @param newTransforms collection of replacement transforms
+ */
+ public void setTransforms(@Nullable final Collection<Pair<String, String>> newTransforms) {
+ checkSetterPreconditions();
+ if (newTransforms != null) {
+ transforms = new ArrayList<>();
+ for (final Pair<String,String> p : newTransforms) {
+ final Pattern pattern = Pattern.compile(StringSupport.trimOrNull(p.getFirst()));
+ transforms.add(new Pair<>(pattern, Constraint.isNotNull(
+ StringSupport.trimOrNull(p.getSecond()), "Replacement expression cannot be null")));
+ }
+ } else {
+ transforms = CollectionSupport.emptyList();
+ }
+ }
+
+ /**
+ * Controls conversion to uppercase prior to applying any transforms.
+ *
+ * @param flag uppercase flag
+ */
+ public void setUppercase(final boolean flag) {
+ checkSetterPreconditions();
+ uppercase = flag;
+ }
+
+ /**
+ * Controls conversion to lowercase prior to applying any transforms.
+ *
+ * @param flag lowercase flag
+ */
+ public void setLowercase(final boolean flag) {
+ checkSetterPreconditions();
+ lowercase = flag;
+ }
+
+ /**
+ * Controls whitespace trimming prior to applying any transforms.
+ *
+ * @param flag trim flag
+ */
+ public void setTrim(final boolean flag) {
+ checkSetterPreconditions();
+ trim = flag;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ logPrefix = "SubjectCanonicalizer " + ensureId() + ":";
+ }
+
+ /**
+ * Performs this c14n action's pre-execute step. Default implementation just returns true iff a subject
+ * is set.
+ *
+ * @param c14nContext the current subject canonicalization context
+ *
+ * @return event indicating result of function
+ */
+ @Nullable public String apply(@Nullable final SubjectCanonicalizationContext c14nContext) {
+ checkComponentActive();
+
+ if (c14nContext == null) {
+ return AuthnEventIds.INVALID_SUBJECT_C14N_CTX;
+ }
+
+ if (c14nContext.getSubject() == null) {
+ c14nContext.setException(new SubjectCanonicalizationException("No Subject found in context"));
+ return AuthnEventIds.INVALID_SUBJECT;
+ }
+
+ return doApply(c14nContext);
+ }
+
+ /**
+ * Performs c14n if possible.
+ *
+ * @param c14nContext the current subject canonicalization context
+ *
+ * @return event indicating result of function
+ */
+ @Nonnull protected abstract String doApply(@Nonnull final SubjectCanonicalizationContext c14nContext);
+
+
+ /**
+ * Apply any configured regular expression replacements to an input value and return the result.
+ *
+ * @param input the input string
+ *
+ * @return the result of applying the expressions
+ */
+ @Nonnull @NotEmpty protected String applyTransforms(@Nonnull @NotEmpty final String input) {
+
+ String s = input;
+
+ if (trim) {
+ log.debug("{} trimming whitespace of input string '{}'", getLogPrefix(), s);
+ s = s.trim();
+ }
+
+ if (lowercase) {
+ log.debug("{} converting input string '{}' to lowercase", getLogPrefix(), s);
+ s = s.toLowerCase();
+ } else if (uppercase) {
+ log.debug("{} converting input string '{}' to uppercase", getLogPrefix(), s);
+ s = s.toUpperCase();
+ }
+
+ for (final Pair<Pattern,String> p : transforms) {
+ final Pattern pattern = p.getFirst();
+ if (pattern != null) {
+ final Matcher m = pattern.matcher(s);
+ log.debug("{} applying replacement expression '{}' against input '{}'", getLogPrefix(),
+ pattern.pattern(), s);
+ s = m.replaceAll(p.getSecond());
+ log.debug("{} result of replacement is '{}'", getLogPrefix(), s);
+ }
+ }
+
+ assert s != null;
+ return s;
+ }
+
+ /**
+ * Return a prefix for logging messages for this component.
+ *
+ * @return a string for insertion at the beginning of any log messages
+ */
+ @NonnullAfterInit protected String getLogPrefix() {
+ return logPrefix;
+ }
+
+}
\ No newline at end of file
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/SubjectCanonicalizationFlowDescriptor.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/SubjectCanonicalizationFlowDescriptor.java
index 0478fe962..0927800df 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/SubjectCanonicalizationFlowDescriptor.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/SubjectCanonicalizationFlowDescriptor.java
@@ -51,6 +51,9 @@ public class SubjectCanonicalizationFlowDescriptor extends AbstractIdentifiableI
/** Predicate that must be true for this flow to be usable for a given request. */
@Nonnull private Predicate<ProfileRequestContext> activationCondition;
+ /** Optional "inline" c14n implementation to execute without running a subflow. */
+ @Nullable private SubjectCanonicalizer subjectCanonicalizer;
+
/** Constructor. */
public SubjectCanonicalizationFlowDescriptor() {
activationCondition = PredicateSupport.alwaysTrue();
@@ -61,7 +64,7 @@ public class SubjectCanonicalizationFlowDescriptor extends AbstractIdentifiableI
*
* @return flow ID
*/
- @NonnullAfterInit public String getFlowId() {
+ @Nullable public String getFlowId() {
return flowId;
}
@@ -94,13 +97,40 @@ public class SubjectCanonicalizationFlowDescriptor extends AbstractIdentifiableI
activationCondition = Constraint.isNotNull(condition, "Activation condition predicate cannot be null");
}
+ /**
+ * Get an "inline" implementation of subject c14n that does not require use of a Spring WebFlow.
+ *
+ * @return implementation of {@link SubjectCanonicalizer} interface to use
+ *
+ * @since 5.2.0
+ */
+ @Nullable public SubjectCanonicalizer getImplementation() {
+ return subjectCanonicalizer;
+ }
+
+ /**
+ * Set an "inline" implementation of subject c14n that does not require use of a Spring WebFlow.
+ *
+ * @param impl implementation of {@link SubjectCanonicalizer} interface to use
+ *
+ * @since 5.2.0
+ */
+ public void setImplementation(@Nullable final SubjectCanonicalizer impl) {
+ checkSetterPreconditions();
+ subjectCanonicalizer = impl;
+ }
+
/** {@inheritDoc} */
@Override
protected void doInitialize() throws ComponentInitializationException {
super.doInitialize();
- // Backfill flow ID with component ID.
- if (flowId == null) {
+ if (subjectCanonicalizer != null) {
+ if (flowId != null) {
+ throw new ComponentInitializationException("Implementation object and flow ID are mutually exclusive.");
+ }
+ } else if (flowId == null) {
+ // Backfill flow ID with component ID.
flowId = ensureId();
if (!flowId.startsWith("c14n/")) {
throw new ComponentInitializationException("Defaulted flow ID must begin with c14n/ prefix.");
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/SubjectCanonicalizer.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/SubjectCanonicalizer.java
new file mode 100644
index 000000000..d03089810
--- /dev/null
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/SubjectCanonicalizer.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.authn;
+
+import java.util.function.Function;
+
+import org.opensaml.profile.action.EventIds;
+
+import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
+
+/**
+ * Marker interface for the ability to perform subject canonicalization "inline" without
+ * leveraging the Spring Webflow layer.
+ *
+ * <p>Most c14n implementations are functional and don't involve a user interface or views
+ * and do not need the full capability of SWF. Some implementations may choose to leverage
+ * it for code modularity or reuse reasons.</p>
+ *
+ * <p>The output of this function is a SWF event string ({@link EventIds#PROCEED_EVENT_ID}
+ * on success).</p>
+ *
+ * @since 5.2.0
+ */
+public interface SubjectCanonicalizer extends Function<SubjectCanonicalizationContext,String> {
+
+}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SelectSubjectCanonicalizationFlow.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SelectSubjectCanonicalizationFlow.java
index 3183e4922..c8c2b054e 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SelectSubjectCanonicalizationFlow.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SelectSubjectCanonicalizationFlow.java
@@ -20,9 +20,11 @@ import javax.annotation.Nullable;
import net.shibboleth.idp.authn.AbstractSubjectCanonicalizationAction;
import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.authn.SubjectCanonicalizationFlowDescriptor;
+import net.shibboleth.idp.authn.SubjectCanonicalizer;
import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -35,11 +37,17 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* {@link SubjectCanonicalizationContext} has been fully populated. It uses the potential flows,
* and their associated activation conditions to decide how to proceed.</p>
*
- * <p>This is a rare case in that the standard default event,
- * {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}, cannot be returned,
- * because the action must either dispatch to a flow by name, or signal an error.</p>
- *
+ * <p>An enhancement added allows use of "inline" functional c14n implementations that
+ * don't require the sophistication of a webflow. When an inline bean is executed,
+ * its resulting events are relayed back as the result of this action in the case
+ * of {@link EventIds#PROCEED_EVENT_ID} or {@link AuthnEventIds#INVALID_SUBJECT},
+ * or transformed into {@link AuthnEventIds#RESELECT_FLOW} for any other event.</p>
+ *
+ * <p>After exhausting all options, {@link AuthnEventIds#NO_POTENTIAL_FLOW} is returned.</p>
+ *
+ * @event {@link EventIds#PROCEED_EVENT_ID}
* @event {@link AuthnEventIds#NO_POTENTIAL_FLOW}
+ * @event {@link AuthnEventIds#RESELECT_FLOW}
* @event Selected flow ID to execute
* @pre <pre>ProfileRequestContext.getSubcontext(SubjectCanonicalizationContext.class) != null</pre>
*/
@@ -80,9 +88,26 @@ public class SelectSubjectCanonicalizationFlow extends AbstractSubjectCanonicali
ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_POTENTIAL_FLOW);
return;
}
- log.debug("{} Selecting c14n descriptor {} (WebFlow ID: {})", getLogPrefix(), flow.ensureId(),
- flow.getFlowId());
- ActionSupport.buildEvent(profileRequestContext, flow.getFlowId());
+
+ final SubjectCanonicalizer inline = flow.getImplementation();
+ if (inline != null) {
+ log.debug("{} Selecting c14n descriptor {} (Inline implementation)", getLogPrefix(), flow.ensureId());
+ final String event = inline.apply(c14nContext);
+ if (event == EventIds.PROCEED_EVENT_ID || event == AuthnEventIds.INVALID_SUBJECT) {
+ ActionSupport.buildEvent(profileRequestContext, event);
+ } else {
+ log.warn("{} Inline c14n implementation {} failed with event: {}", getLogPrefix(), flow.ensureId(),
+ event);
+ // As with flow events, we remap anything else to a reselect signal to iterate the loop
+ ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.RESELECT_FLOW);
+ }
+ } else {
+ final String flowId = flow.getFlowId();
+ assert flowId != null;
+ log.debug("{} Selecting c14n descriptor {} (WebFlow ID: {})", getLogPrefix(), flow.ensureId(),
+ flow.getFlowId());
+ ActionSupport.buildEvent(profileRequestContext, flowId);
+ }
}
/**
@@ -106,7 +131,7 @@ public class SelectSubjectCanonicalizationFlow extends AbstractSubjectCanonicali
return flow;
}
final Exception ctxException = c14nContext.getException();
- log.debug("{} C14N flow descriptor {} was not applicable: {}", getLogPrefix(), flow.getId(),
+ log.debug("{} C14N flow descriptor {} was not applicable: {}", getLogPrefix(), flow.ensureId(),
ctxException!= null ? ctxException.getMessage() : "reason unknown");
c14nContext.setException(null);
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SimpleSubjectCanonicalization.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SimpleSubjectCanonicalization.java
index 35b7425e7..5d6927333 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SimpleSubjectCanonicalization.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SimpleSubjectCanonicalization.java
@@ -21,10 +21,10 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.security.auth.Subject;
-import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
-import net.shibboleth.idp.authn.AbstractSubjectCanonicalizationAction;
+import net.shibboleth.idp.authn.AbstractSubjectCanonicalizer;
import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.authn.SubjectCanonicalizationException;
import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
@@ -41,40 +41,45 @@ import net.shibboleth.idp.authn.principal.UsernamePrincipal;
* @post <pre>SubjectCanonicalizationContext.getPrincipalName() != null
* || SubjectCanonicalizationContext.getException() != null</pre>
*/
-public class SimpleSubjectCanonicalization extends AbstractSubjectCanonicalizationAction {
-
- /** Supplies logic for pre-execute test. */
- @Nonnull private final ActivationCondition embeddedPredicate;
-
- /** The custom Principal to operate on. */
- @Nullable private UsernamePrincipal usernamePrincipal;
-
- /** Constructor. */
- public SimpleSubjectCanonicalization() {
- embeddedPredicate = new ActivationCondition();
- }
+public class SimpleSubjectCanonicalization extends AbstractSubjectCanonicalizer {
/** {@inheritDoc} */
- @Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final SubjectCanonicalizationContext c14nContext) {
+ @Nonnull public String doApply(@Nonnull final SubjectCanonicalizationContext c14nContext) {
- if (embeddedPredicate.apply(profileRequestContext, c14nContext, true)) {
- final Subject c14CtxSubject = c14nContext.getSubject();
- assert c14CtxSubject != null;
- usernamePrincipal = c14CtxSubject.getPrincipals(UsernamePrincipal.class).iterator().next();
- return super.doPreExecute(profileRequestContext, c14nContext);
+ final UsernamePrincipal usernamePrincipal = getUsernamePrincipal(c14nContext);
+ if (usernamePrincipal == null) {
+ return AuthnEventIds.INVALID_SUBJECT;
}
- return false;
+ c14nContext.setPrincipalName(applyTransforms(usernamePrincipal.getName()));
+ return EventIds.PROCEED_EVENT_ID;
}
- /** {@inheritDoc} */
- @Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+ /**
+ * Helper method that returns the first and only {@link UsernamePrincipal}, returning null
+ * otherwise.
+ *
+ * @param c14nContext input context
+ *
+ * @return the only matching principal or null
+ */
+ @Nullable private static UsernamePrincipal getUsernamePrincipal(
@Nonnull final SubjectCanonicalizationContext c14nContext) {
- assert usernamePrincipal != null;
- c14nContext.setPrincipalName(applyTransforms(usernamePrincipal.getName()));
+
+ final Subject subject = c14nContext.getSubject();
+ if (subject != null) {
+ final Set<UsernamePrincipal> prins = subject.getPrincipals(UsernamePrincipal.class);
+ if (prins == null || prins.isEmpty()) {
+ c14nContext.setException(new SubjectCanonicalizationException("No UsernamePrincipals were found"));
+ } else if (prins.size() > 1) {
+ c14nContext.setException(
+ new SubjectCanonicalizationException("Multiple UsernamePrincipals were found"));
+ } else {
+ return prins.iterator().next();
+ }
+ }
+
+ return null;
}
/** A predicate that determines if this action can run or not. */
@@ -87,53 +92,12 @@ public class SimpleSubjectCanonicalization extends AbstractSubjectCanonicalizati
final SubjectCanonicalizationContext c14nContext =
input.getSubcontext(SubjectCanonicalizationContext.class);
if (c14nContext != null) {
- return apply(input, c14nContext, false);
+ return getUsernamePrincipal(c14nContext) != null;
}
}
return false;
}
-
- /**
- * Helper method that runs either as part of the {@link Predicate} or directly from
- * the {@link SimpleSubjectCanonicalization#doPreExecute(ProfileRequestContext, SubjectCanonicalizationContext)}
- * method above.
- *
- * @param profileRequestContext the current profile request context
- * @param c14nContext the current c14n context
- * @param duringAction true iff the method is run from the action above
- * @return true iff the action can operate successfully on the candidate contexts
- */
- public boolean apply(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final SubjectCanonicalizationContext c14nContext, final boolean duringAction) {
-
- final Set<UsernamePrincipal> usernames;
- final Subject c14CtxSubject = c14nContext.getSubject();
- if (c14CtxSubject != null) {
- usernames = c14CtxSubject .getPrincipals(UsernamePrincipal.class);
- } else {
- usernames = null;
- }
-
- if (usernames == null || usernames.isEmpty()) {
- c14nContext.setException(
- new SubjectCanonicalizationException("No UsernamePrincipals were found"));
- if (duringAction) {
- ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_SUBJECT);
- }
- return false;
- } else if (usernames.size() > 1) {
- c14nContext.setException(
- new SubjectCanonicalizationException("Multiple UsernamePrincipals were found"));
- if (duringAction) {
- ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_SUBJECT);
- }
- return false;
- }
-
- return true;
- }
-
}
}
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/SimpleSubjectCanonicalizationTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/SimpleSubjectCanonicalizationTest.java
index 3742ff045..d5fbeb398 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/SimpleSubjectCanonicalizationTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/SimpleSubjectCanonicalizationTest.java
@@ -22,11 +22,10 @@ import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
import net.shibboleth.idp.authn.impl.testing.BaseAuthenticationContextTest;
import net.shibboleth.idp.authn.principal.UsernamePrincipal;
-import net.shibboleth.idp.profile.testing.ActionTestingSupport;
import net.shibboleth.shared.collection.Pair;
import net.shibboleth.shared.component.ComponentInitializationException;
-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;
@@ -41,37 +40,37 @@ public class SimpleSubjectCanonicalizationTest extends BaseAuthenticationContext
super.setUp();
action = new SimpleSubjectCanonicalization();
+ action.setId("c14n/simple");
action.setTransforms(Arrays.asList(new Pair<>("^(.+)@osu\\.edu$", "$1")));
action.initialize();
}
@Test public void testNoContext() {
- final Event event = action.execute(src);
-
- 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 testNoPrincipal() {
- Subject subject = new Subject();
- prc.ensureSubcontext(SubjectCanonicalizationContext.class).setSubject(subject);
+ final Subject subject = new Subject();
+ final var c14n = prc.ensureSubcontext(SubjectCanonicalizationContext.class).setSubject(subject);
- final Event event = action.execute(src);
+ final String event = action.apply(c14n);
- ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_SUBJECT);
+ Assert.assertEquals(event, AuthnEventIds.INVALID_SUBJECT);
final SubjectCanonicalizationContext scc = prc.getSubcontext(SubjectCanonicalizationContext.class);
assert scc != null;
Assert.assertNotNull(scc.getException());
}
@Test public void testMultiPrincipals() {
- Subject subject = new Subject();
+ final Subject subject = new Subject();
subject.getPrincipals().add(new UsernamePrincipal("foo"));
subject.getPrincipals().add(new UsernamePrincipal("bar"));
- prc.ensureSubcontext(SubjectCanonicalizationContext.class).setSubject(subject);
+ final var c14n = prc.ensureSubcontext(SubjectCanonicalizationContext.class).setSubject(subject);
- final Event event = action.execute(src);
+ final String event = action.apply(c14n);
- ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_SUBJECT);
+ Assert.assertEquals(event, AuthnEventIds.INVALID_SUBJECT);
final SubjectCanonicalizationContext scc = prc.getSubcontext(SubjectCanonicalizationContext.class);
assert scc != null;
Assert.assertNotNull(scc.getException());
@@ -80,11 +79,11 @@ public class SimpleSubjectCanonicalizationTest extends BaseAuthenticationContext
@Test public void testSuccess() {
Subject subject = new Subject();
subject.getPrincipals().add(new UsernamePrincipal("foo"));
- prc.ensureSubcontext(SubjectCanonicalizationContext.class).setSubject(subject);
+ final var c14n = prc.ensureSubcontext(SubjectCanonicalizationContext.class).setSubject(subject);
- final Event event = action.execute(src);
+ final String event = action.apply(c14n);
- ActionTestingSupport.assertProceedEvent(event);
+ Assert.assertEquals(event, EventIds.PROCEED_EVENT_ID);
SubjectCanonicalizationContext sc = prc.getSubcontext(SubjectCanonicalizationContext.class);
assert sc != null;
Assert.assertEquals(sc.getPrincipalName(), "foo");
@@ -93,11 +92,11 @@ public class SimpleSubjectCanonicalizationTest extends BaseAuthenticationContext
@Test public void testTransform() {
Subject subject = new Subject();
subject.getPrincipals().add(new UsernamePrincipal("foo at osu.edu"));
- prc.ensureSubcontext(SubjectCanonicalizationContext.class).setSubject(subject);
+ final var c14n = prc.ensureSubcontext(SubjectCanonicalizationContext.class).setSubject(subject);
- final Event event = action.execute(src);
+ final String event = action.apply(c14n);
- ActionTestingSupport.assertProceedEvent(event);
+ Assert.assertEquals(event, EventIds.PROCEED_EVENT_ID);
SubjectCanonicalizationContext sc = prc.getSubcontext(SubjectCanonicalizationContext.class);
assert sc != null;
Assert.assertEquals(sc.getPrincipalName(), "foo");
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 d6276c3fb..c9542820e 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
@@ -23,11 +23,21 @@
<util:list id="shibboleth.ProxyNameTransformFormats" />
<import resource="${idp.home}/conf/c14n/subject-c14n.xml" />
+
+ <!-- Legacy compatibility. -->
+ <import resource="conditional:%{idp.home}/conf/c14n/simple-subject-c14n-config.xml" />
<bean id="shibboleth.PostLoginSubjectCanonicalizationFlow" abstract="true"
class="net.shibboleth.idp.authn.PostLoginSubjectCanonicalizationFlowDescriptor" />
<bean id="c14n/simple" parent="shibboleth.PostLoginSubjectCanonicalizationFlow">
+ <property name="implementation">
+ <bean 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}}"
+ p:transforms="#{getObject('shibboleth.c14n.simple.Transforms')}" />
+ </property>
<property name="activationCondition">
<bean class="net.shibboleth.idp.authn.impl.SimpleSubjectCanonicalization.ActivationCondition" />
</property>
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/webflow-config.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/webflow-config.xml
index 925ddd429..0fc0c2aa2 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/webflow-config.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/webflow-config.xml
@@ -99,7 +99,6 @@
<entry key="c14n" value="classpath:/net/shibboleth/idp/flows/c14n/subject-c14n-flow.xml" />
<!-- Standard post-login C14N methods. -->
- <entry key="c14n/simple" value="classpath:/net/shibboleth/idp/flows/c14n/simple-subject-c14n-flow.xml" />
<entry key="c14n/x500" value="classpath:/net/shibboleth/idp/flows/c14n/x500-subject-c14n-flow.xml" />
<entry key="c14n/attribute" value="classpath:/net/shibboleth/idp/flows/c14n/attribute-sourced-subject-c14n-flow.xml" />
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/simple-subject-c14n-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/simple-subject-c14n-beans.xml
deleted file mode 100644
index db9ad348f..000000000
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/simple-subject-c14n-beans.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:util="http://www.springframework.org/schema/util"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:c="http://www.springframework.org/schema/c"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
-
- default-init-method="initialize"
- default-destroy-method="destroy">
-
- <import resource="conditional:%{idp.home}/conf/c14n/simple-subject-c14n-config.xml" />
-
- <bean id="SimpleSubjectCanonicalization"
- class="net.shibboleth.idp.authn.impl.SimpleSubjectCanonicalization" scope="prototype"
- 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}}"
- p:transforms="#{getObject('shibboleth.c14n.simple.Transforms')}" />
-
-</beans>
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/simple-subject-c14n-flow.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/simple-subject-c14n-flow.xml
deleted file mode 100644
index e28a1c2e2..000000000
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/simple-subject-c14n-flow.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<flow xmlns="http://www.springframework.org/schema/webflow"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
- parent="c14n.abstract">
-
- <!-- This is a one-step subflow that does Subject Canonicalization using the simplest built-in action. -->
-
- <action-state id="SimpleSubjectCanonicalization">
- <evaluate expression="SimpleSubjectCanonicalization" />
- <evaluate expression="'proceed'" />
-
- <transition on="proceed" to="proceed" />
- </action-state>
-
- <bean-import resource="simple-subject-c14n-beans.xml" />
-
-</flow>
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/subject-c14n-flow.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/subject-c14n-flow.xml
index 029c62e66..d7e0bb0ab 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/subject-c14n-flow.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/subject-c14n-flow.xml
@@ -15,11 +15,17 @@
<action-state id="SelectSubjectCanonicalizationFlow">
<evaluate expression="SelectSubjectCanonicalizationFlow" />
+ <!-- Indicates an inline implementation succeeded. -->
+ <transition on="proceed" to="proceed" />
+
<!-- Call a subflow with the same ID as the event. -->
<transition on="#{currentEvent.id.startsWith('c14n/')}" to="CallSubjectCanonicalizationFlow" />
<!-- Remap the case of no flows to run into a more recognizeable fatal error. -->
<transition on="NoPotentialFlow" to="SubjectCanonicalizationError" />
+
+ <!-- Signals us to loop back for another flow. -->
+ <transition on="ReselectFlow" to="SelectSubjectCanonicalizationFlow" />
</action-state>
<!--
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list