[java-identity-provider] 02/02: IDP-2365 - Support multiple definitions of a given login flow
Scott Cantor
cantor.2 at osu.edu
Fri Mar 21 17:17:31 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=628da5f00b79390fd68e55c6218054238d4be0f2
commit 628da5f00b79390fd68e55c6218054238d4be0f2
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Fri Mar 21 13:16:26 2025 -0400
IDP-2365 - Support multiple definitions of a given login flow
https://shibboleth.atlassian.net/browse/IDP-2365
Also some cleanup in some method calls inside flows.
---
.../idp/authn/AuthenticationFlowDescriptor.java | 53 +++++++++++++++++++++-
.../context/MultiFactorAuthenticationContext.java | 37 ++++++++++++++-
.../authn/AuthenticationFlowDescriptorTest.java | 2 +
.../PopulateMultiFactorAuthenticationContext.java | 2 +-
.../idp/authn/impl/SelectAuthenticationFlow.java | 6 +--
.../impl/TransitionMultiFactorAuthentication.java | 26 +++++------
.../DefaultAuthenticationResultSerializerTest.java | 2 +
...pulateMultiFactorAuthenticationContextTest.java | 4 ++
.../authn/impl/SelectAuthenticationFlowTest.java | 26 ++++++-----
.../testing/BaseAuthenticationContextTest.java | 3 ++
.../net/shibboleth/idp/conf/authn-system.xml | 33 +++++++++-----
.../net/shibboleth/idp/flows/authn/authn-beans.xml | 2 +-
.../net/shibboleth/idp/flows/authn/authn-flow.xml | 4 +-
.../shibboleth/idp/flows/authn/mfa-authn-flow.xml | 4 +-
.../impl/StorageBackedSessionManagerTest.java | 2 +
.../UpdateSessionWithAuthenticationResultTest.java | 1 +
16 files changed, 157 insertions(+), 50 deletions(-)
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthenticationFlowDescriptor.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthenticationFlowDescriptor.java
index 90576c3fe..dc496ea3e 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthenticationFlowDescriptor.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthenticationFlowDescriptor.java
@@ -43,6 +43,7 @@ import net.shibboleth.idp.authn.principal.PrincipalService;
import net.shibboleth.idp.authn.principal.PrincipalServiceManager;
import net.shibboleth.idp.authn.principal.PrincipalSupportingComponent;
import net.shibboleth.idp.profile.FlowDescriptor;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.annotation.constraint.Unmodifiable;
import net.shibboleth.shared.collection.CollectionSupport;
@@ -72,6 +73,9 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
/** Additional allowance for storage of result records to avoid race conditions during use. */
@Nonnull public static final Duration STORAGE_EXPIRATION_OFFSET;
+ /** Optional flow ID that may differ from component ID. */
+ @NonnullAfterInit private String flowId;
+
/** Spring auto-wiring order. */
private int order;
@@ -155,6 +159,38 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
stringBasedPrincipals = CollectionSupport.emptySet();
}
+
+ /**
+ * Gets the webflow ID for this descriptor (inclusive of the authn/ prefix).
+ *
+ * @return webflow ID
+ *
+ * @since 5.2.0
+ */
+ @NonnullAfterInit public String getFlowId() {
+ return flowId;
+ }
+
+ /**
+ * Sets the flow ID for this descriptor.
+ *
+ * <p>This defaults to the component ID, but can be overridden to allow multiple
+ * components to be defined for a given webflow.</p>
+ *
+ * @param id
+ *
+ * @since 5.2.0
+ */
+ public void setFlowId(@Nullable final String id) {
+ checkSetterPreconditions();
+
+ flowId = StringSupport.trimOrNull(id);
+ if (id != null) {
+ Constraint.isTrue(flowId.startsWith("authn/"), "Flow ID must begin with authn/ prefix.");
+ }
+ }
+
+
/** {@inheritDoc} */
public int getOrder() {
return order;
@@ -629,6 +665,14 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
throw new ComponentInitializationException("AuthenticationResult serializer cannot be null");
}
+ if (flowId == null) {
+ // Backfill flow ID with component ID.
+ flowId = ensureId();
+ if (!flowId.startsWith("authn/")) {
+ throw new ComponentInitializationException("Defaulted flow ID must begin with authn/ prefix.");
+ }
+ }
+
if (!stringBasedPrincipals.isEmpty()) {
if (principalServiceManager == null) {
throw new ComponentInitializationException("PrincipalServiceManager cannot be null");
@@ -754,9 +798,14 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
/** {@inheritDoc} */
@Override public String toString() {
- return MoreObjects.toStringHelper(this).add("flowId", getId()).add("supportsPassive", supportsPassive)
+ return MoreObjects.toStringHelper(this)
+ .add("id", getId())
+ .add("flowId", getFlowId())
+ .add("supportsPassive", supportsPassive)
.add("supportsForcedAuthentication", supportsForced)
- .add("lifetime", lifetime).add("inactivityTimeout", inactivityTimeout).toString();
+ .add("lifetime", lifetime)
+ .add("inactivityTimeout", inactivityTimeout)
+ .toString();
}
/**
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/MultiFactorAuthenticationContext.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/MultiFactorAuthenticationContext.java
index 16479a5f5..d6a3274e9 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/MultiFactorAuthenticationContext.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/MultiFactorAuthenticationContext.java
@@ -52,9 +52,12 @@ public final class MultiFactorAuthenticationContext extends BaseContext {
/** Login flow descriptor for the MFA flow. */
@Nullable private AuthenticationFlowDescriptor mfaFlowDescriptor;
- /** The next flow due to execute (or the currently executing flow during subflow execution). */
+ /** The next logical flow due to execute (or the currently executing flow during subflow execution). */
@Nullable @NotEmpty private String nextFlowId;
+ /** The next webflow due to execute (or the currently executing web flow during subflow execution). */
+ @Nullable @NotEmpty private String nextWebFlowId;
+
/** A SWF event to signal as the completion of the MFA flow. */
@Nullable @NotEmpty private String event;
@@ -139,16 +142,46 @@ public final class MultiFactorAuthenticationContext extends BaseContext {
/**
* Set the next flow due to execute.
*
+ * <p>This also calls {@link #setNextWebFlowId(String)} automatically.</p>
+ *
* @param id flow ID
*
* @return this context
*/
@Nonnull public MultiFactorAuthenticationContext setNextFlowId(@Nullable @NotEmpty final String id) {
nextFlowId = StringSupport.trimOrNull(id);
+ return setNextWebFlowId(nextFlowId);
+ }
+
+ /**
+ * Get the next webflow due to execute (or that is currently executing).
+ *
+ * <p>This is distinct from the next "flow" in the case of login flows, to allow for the
+ * indirection between logical flow as a component designation and physical webflow implementation.</p>
+ *
+ * @return the ID of the next webflow to execute
+ *
+ * @since 5.2.0
+ */
+ @Nullable @NotEmpty public String getNextWebFlowId() {
+ return nextWebFlowId;
+ }
+
+ /**
+ * Set the next webflow due to execute.
+ *
+ * @param id webflow ID
+ *
+ * @return this context
+ *
+ * @since 5.2.0
+ */
+ @Nonnull public MultiFactorAuthenticationContext setNextWebFlowId(@Nullable @NotEmpty final String id) {
+ nextWebFlowId = StringSupport.trimOrNull(id);
return this;
}
-
+
/**
* Get an event that should be signaled as the result of the MFA flow.
*
diff --git a/idp-authn-api/src/test/java/net/shibboleth/idp/authn/AuthenticationFlowDescriptorTest.java b/idp-authn-api/src/test/java/net/shibboleth/idp/authn/AuthenticationFlowDescriptorTest.java
index 8bf70c5d0..77acc03a0 100644
--- a/idp-authn-api/src/test/java/net/shibboleth/idp/authn/AuthenticationFlowDescriptorTest.java
+++ b/idp-authn-api/src/test/java/net/shibboleth/idp/authn/AuthenticationFlowDescriptorTest.java
@@ -32,12 +32,14 @@ public class AuthenticationFlowDescriptorTest {
@BeforeMethod public void setUp() {
descriptor = new AuthenticationFlowDescriptor();
descriptor.setId("test");
+ descriptor.setFlowId("authn/test");
}
/** Tests that everything is properly initialized during object construction. */
@Test public void testInstantation() {
Assert.assertEquals(descriptor.getId(), "test");
+ Assert.assertEquals(descriptor.getFlowId(), "authn/test");
Assert.assertFalse(descriptor.isForcedAuthenticationSupported());
Assert.assertFalse(descriptor.isPassiveAuthenticationSupported());
}
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PopulateMultiFactorAuthenticationContext.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PopulateMultiFactorAuthenticationContext.java
index f95336fb5..1990dc7dc 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PopulateMultiFactorAuthenticationContext.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PopulateMultiFactorAuthenticationContext.java
@@ -171,7 +171,7 @@ public class PopulateMultiFactorAuthenticationContext extends AbstractAuthentica
* the {@link AuthenticationResultPrincipal} collection of an active {@link AuthenticationResult}
* of the currently executing flow.
*/
- private class DefaultResultLookupStrategy
+ private final class DefaultResultLookupStrategy
implements Function<ProfileRequestContext,Collection<AuthenticationResult>> {
/** {@inheritDoc} */
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SelectAuthenticationFlow.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SelectAuthenticationFlow.java
index 9767098a2..7037e0953 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SelectAuthenticationFlow.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SelectAuthenticationFlow.java
@@ -364,10 +364,10 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
@Nonnull final AuthenticationContext authenticationContext,
@Nonnull final AuthenticationFlowDescriptor descriptor) {
- final String id = descriptor.ensureId();
- log.debug("{} Selecting inactive authentication flow {}", getLogPrefix(), id);
+ log.debug("{} Selecting inactive authentication flow {} using webflow {}", getLogPrefix(),
+ descriptor.getId(), descriptor.getFlowId());
authenticationContext.setAttemptedFlow(descriptor);
- ActionSupport.buildEvent(profileRequestContext, id);
+ ActionSupport.buildEvent(profileRequestContext, descriptor.getFlowId());
}
/**
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/TransitionMultiFactorAuthentication.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/TransitionMultiFactorAuthentication.java
index 3cd8840fb..e3fc8e402 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/TransitionMultiFactorAuthentication.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/TransitionMultiFactorAuthentication.java
@@ -63,14 +63,15 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* {@link MultiFactorAuthenticationContext#getEvent()} (if set), or the current WebFlow event.</p>
*
* <p>If a flow is returned, it is populated into the {@link MultiFactorAuthenticationContext}.
- * The flow is checked for the "authn/" prefix, and a login flow is checked against the
- * active result map to determine if it can be reused, in which case the action recurses itself.
- * Otherwise {@link EventIds#PROCEED_EVENT_ID}is signaled to run that flow.</p>
+ * The flow is checked for a matching {@link AuthenticationFlowDescriptor}, and any login flow
+ * is checked against the active result map to determine if it can be reused, in which case the
+ * action recurses itself. Otherwise {@link EventIds#PROCEED_EVENT_ID}is signaled to run that
+ * flow, whether a login flow or not.</p>
*
- * <p>By default, login flow transitions are validated against the request's requirements
+ * <p>By default, login flow transitions are also validated against the request's requirements
* in terms of passive, forced re-authn, and non-browser compatibility.</p>
*
- * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class).getSubcontext(
+ * @pre <pre>ProfileRequestContext.ensureSubcontext(AuthenticationContext.class).getSubcontext(
* MultiFactorAuthenticationContext.class) != null</pre>
* @post See above.
* @event {@link EventIds#PROCEED_EVENT_ID}
@@ -262,18 +263,12 @@ public class TransitionMultiFactorAuthentication extends AbstractAuthenticationA
assert isPreExecuteCalled();
// Non-authentication flows can just be executed (via a "proceed" event).
+ // We identify them by the absence of a matching AuthenticationFlowDescriptor.
final String flowId = Constraint.isNotNull(mfaContext.getNextFlowId(), "No previous flow");
- if (!flowId.startsWith("authn/")) {
- ActionSupport.buildProceedEvent(profileRequestContext);
- return;
- }
-
final AuthenticationFlowDescriptor flow = authenticationContext.getAvailableFlows().get(flowId);
if (flow == null) {
- log.error("{} Targeted login flow '{}' is not configured, check available flow descriptors",
- getLogPrefix(), flowId);
- ActionSupport.buildEvent(profileRequestContext, authenticationContext.isPassive() ?
- AuthnEventIds.NO_PASSIVE : AuthnEventIds.NO_POTENTIAL_FLOW);
+ log.debug("{} Calling non-login subflow: {}", getLogPrefix(), flowId);
+ ActionSupport.buildProceedEvent(profileRequestContext);
return;
}
@@ -324,6 +319,9 @@ public class TransitionMultiFactorAuthentication extends AbstractAuthenticationA
// Set for compatibility with more standard runs of a login flow at the top level.
authenticationContext.setAttemptedFlow(flow);
+ // For login flows, we need to override the webflow ID in the MFA context with the physical flow ID to run.
+ mfaContext.setNextWebFlowId(flow.getFlowId());
+
ActionSupport.buildProceedEvent(profileRequestContext);
}
// Checkstyle: CyclomaticComplexity ON
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/DefaultAuthenticationResultSerializerTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/DefaultAuthenticationResultSerializerTest.java
index 0ff45e119..e53036c6e 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/DefaultAuthenticationResultSerializerTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/DefaultAuthenticationResultSerializerTest.java
@@ -177,6 +177,7 @@ public class DefaultAuthenticationResultSerializerTest {
serializer = new DefaultAuthenticationResultSerializer(manager, generic);
flowDescriptor = new AuthenticationFlowDescriptor();
flowDescriptor.setId("test");
+ flowDescriptor.setFlowId("authn/test");
assert serializer != null;
flowDescriptor.setResultSerializer(serializer);
flowDescriptor.setReuseCondition(PredicateSupport.alwaysTrue());
@@ -498,6 +499,7 @@ public class DefaultAuthenticationResultSerializerTest {
final AuthenticationFlowDescriptor nestedDescriptor = new AuthenticationFlowDescriptor();
nestedDescriptor.setId("nested");
+ nestedDescriptor.setFlowId("authn/nested");
assert serializer != null;
nestedDescriptor.setResultSerializer(serializer);
nestedDescriptor.initialize();
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/PopulateMultiFactorAuthenticationContextTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/PopulateMultiFactorAuthenticationContextTest.java
index fc7cb1863..4de3e965e 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/PopulateMultiFactorAuthenticationContextTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/PopulateMultiFactorAuthenticationContextTest.java
@@ -105,6 +105,7 @@ public class PopulateMultiFactorAuthenticationContextTest {
AuthenticationFlowDescriptor desc = new AuthenticationFlowDescriptor();
desc.setId("foo");
+ desc.setFlowId("authn/foo");
desc.setResultSerializer(new DefaultAuthenticationResultSerializer());
desc.setLifetime(Duration.ofHours(1));
desc.initialize();
@@ -112,6 +113,7 @@ public class PopulateMultiFactorAuthenticationContextTest {
desc = new AuthenticationFlowDescriptor();
desc.setId("bar");
+ desc.setFlowId("authn/bar");
desc.setResultSerializer(new DefaultAuthenticationResultSerializer());
desc.setLifetime(Duration.ofHours(1));
desc.initialize();
@@ -119,6 +121,7 @@ public class PopulateMultiFactorAuthenticationContextTest {
desc = new AuthenticationFlowDescriptor();
desc.setId("bav");
+ desc.setFlowId("authn/bav");
desc.setResultSerializer(new DefaultAuthenticationResultSerializer());
desc.setLifetime(Duration.ofHours(1));
desc.initialize();
@@ -126,6 +129,7 @@ public class PopulateMultiFactorAuthenticationContextTest {
desc = new AuthenticationFlowDescriptor();
desc.setId("bag");
+ desc.setFlowId("authn/bag");
desc.setResultSerializer(new DefaultAuthenticationResultSerializer());
desc.setLifetime(Duration.ofHours(1));
desc.initialize();
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/SelectAuthenticationFlowTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/SelectAuthenticationFlowTest.java
index 8555c855b..bc050f9a5 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/SelectAuthenticationFlowTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/SelectAuthenticationFlowTest.java
@@ -54,14 +54,14 @@ public class SelectAuthenticationFlowTest extends BaseAuthenticationContextTest
final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
assert authCtx != null;
final Event event = action.execute(src);
- ActionTestingSupport.assertEvent(event, "test1");
+ ActionTestingSupport.assertEvent(event, "authn/test1");
Assert.assertNull(authCtx.getAuthenticationResult());
final AuthenticationFlowDescriptor attemptedFlow = authCtx.getAttemptedFlow();
assert attemptedFlow != null && event != null;
- Assert.assertEquals(attemptedFlow, authCtx.getPotentialFlows().get(event.getId()));
Assert.assertEquals(attemptedFlow.getId(), "test1");
+ Assert.assertEquals(attemptedFlow, authCtx.getPotentialFlows().get("test1"));
}
@Test public void testNoRequestNoneActivePassive() {
@@ -70,13 +70,13 @@ public class SelectAuthenticationFlowTest extends BaseAuthenticationContextTest
authCtx.setIsPassive(true);
final Event event = action.execute(src);
- ActionTestingSupport.assertEvent(event, "test2");
+ ActionTestingSupport.assertEvent(event, "authn/test2");
Assert.assertNull(authCtx.getAuthenticationResult());
final AuthenticationFlowDescriptor attemptedFlow = authCtx.getAttemptedFlow();
assert attemptedFlow != null && event != null;
- Assert.assertEquals(attemptedFlow, authCtx.getPotentialFlows().get(event.getId()));
Assert.assertEquals(attemptedFlow.getId(), "test2");
+ Assert.assertEquals(attemptedFlow, authCtx.getPotentialFlows().get("test2"));
}
@Test public void testNoRequestNoneActiveIntermediate() {
@@ -85,13 +85,13 @@ public class SelectAuthenticationFlowTest extends BaseAuthenticationContextTest
authCtx.getIntermediateFlows().put("test1", authCtx.getPotentialFlows().get("test1"));
final Event event = action.execute(src);
- ActionTestingSupport.assertEvent(event, "test2");
+ ActionTestingSupport.assertEvent(event, "authn/test2");
Assert.assertNull(authCtx.getAuthenticationResult());
final AuthenticationFlowDescriptor attemptedFlow = authCtx.getAttemptedFlow();
assert attemptedFlow != null && event != null;
- Assert.assertEquals(attemptedFlow, authCtx.getPotentialFlows().get(event.getId()));
Assert.assertEquals(attemptedFlow.getId(), "test2");
+ Assert.assertEquals(attemptedFlow, authCtx.getPotentialFlows().get("test2"));
}
@Test public void testNoRequestActive() {
@@ -117,7 +117,9 @@ public class SelectAuthenticationFlowTest extends BaseAuthenticationContextTest
assert event != null;
Assert.assertNull(authCtx.getAuthenticationResult());
- Assert.assertEquals(authCtx.getAttemptedFlow(), authCtx.getPotentialFlows().get(event.getId()));
+ // Skip the authn/ prefix in the event to find the flow ID to check for.
+ Assert.assertEquals(authCtx.getAttemptedFlow(),
+ authCtx.getPotentialFlows().get(event.getId().substring(event.getId().indexOf('/') + 1)));
}
@Test public void testRequestNoMatch() {
@@ -142,7 +144,7 @@ public class SelectAuthenticationFlowTest extends BaseAuthenticationContextTest
authCtx.addSubcontext(ppc, true);
final Event event = action.execute(src);
- ActionTestingSupport.assertEvent(event, "test3");
+ ActionTestingSupport.assertEvent(event, "authn/test3");
Assert.assertNull(authCtx.getAuthenticationResult());
final AuthenticationFlowDescriptor attemptedFlow = authCtx.getAttemptedFlow();
@@ -161,7 +163,7 @@ public class SelectAuthenticationFlowTest extends BaseAuthenticationContextTest
authCtx.getPotentialFlows().get("test3").setSupportedPrincipals(principals);
final Event event = action.execute(src);
- ActionTestingSupport.assertEvent(event, "test3");
+ ActionTestingSupport.assertEvent(event, "authn/test3");
Assert.assertNull(authCtx.getAuthenticationResult());
final AuthenticationFlowDescriptor attemptedFlow = authCtx.getAttemptedFlow();
@@ -223,7 +225,7 @@ public class SelectAuthenticationFlowTest extends BaseAuthenticationContextTest
authCtx.getPotentialFlows().get("test3").setSupportedPrincipals(principals);
final Event event = action.execute(src);
- ActionTestingSupport.assertEvent(event, "test3");
+ ActionTestingSupport.assertEvent(event, "authn/test3");
Assert.assertNull(authCtx.getAuthenticationResult());
final AuthenticationFlowDescriptor attemptedFlow = authCtx.getAttemptedFlow();
@@ -247,7 +249,7 @@ public class SelectAuthenticationFlowTest extends BaseAuthenticationContextTest
authCtx.getPotentialFlows().get("test3").setSupportedPrincipals(principals);
final Event event = action.execute(src);
- ActionTestingSupport.assertEvent(event, "test3");
+ ActionTestingSupport.assertEvent(event, "authn/test3");
Assert.assertNull(authCtx.getAuthenticationResult());
final AuthenticationFlowDescriptor flow = authCtx.getAttemptedFlow();
@@ -273,7 +275,7 @@ public class SelectAuthenticationFlowTest extends BaseAuthenticationContextTest
authCtx.getPotentialFlows().get("test3").setSupportedPrincipals(CollectionSupport.singletonList(principals.get(0)));
final Event event = action.execute(src);
- ActionTestingSupport.assertEvent(event, "test3");
+ ActionTestingSupport.assertEvent(event, "authn/test3");
Assert.assertNull(authCtx.getAuthenticationResult());
Assert.assertEquals(authCtx.getAttemptedFlow(), authCtx.getPotentialFlows().get("test3"));
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/testing/BaseAuthenticationContextTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/testing/BaseAuthenticationContextTest.java
index 6e5d99313..f04a9fb65 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/testing/BaseAuthenticationContextTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/testing/BaseAuthenticationContextTest.java
@@ -49,9 +49,12 @@ public class BaseAuthenticationContextTest extends OpenSAMLInitBaseTestCase {
authenticationFlows = List.of(new AuthenticationFlowDescriptor(),
new AuthenticationFlowDescriptor(), new AuthenticationFlowDescriptor());
authenticationFlows.get(0).setId("test1");
+ authenticationFlows.get(0).setFlowId("authn/test1");
authenticationFlows.get(1).setId("test2");
+ authenticationFlows.get(1).setFlowId("authn/test2");
authenticationFlows.get(1).setPassiveAuthenticationSupported(true);
authenticationFlows.get(2).setId("test3");
+ authenticationFlows.get(2).setFlowId("authn/test3");
}
protected void setUp() throws ComponentInitializationException {
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/authn-system.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/authn-system.xml
index ddfa70a4a..722e0f2c5 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/authn-system.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/authn-system.xml
@@ -49,7 +49,8 @@
<!-- Defaulted flows. -->
- <bean p:id="authn/Password" parent="shibboleth.AuthenticationFlow"
+ <bean id="authn/Password" parent="shibboleth.AuthenticationFlow"
+ p:flowId="authn/Password"
p:order="%{idp.authn.Password.order:1000}"
p:nonBrowserSupported="%{idp.authn.Password.nonBrowserSupported:true}"
p:passiveAuthenticationSupported="%{idp.authn.Password.passiveAuthenticationSupported:true}"
@@ -70,7 +71,8 @@
</property>
</bean>
- <bean p:id="authn/IPAddress" parent="shibboleth.AuthenticationFlow"
+ <bean id="authn/IPAddress" parent="shibboleth.AuthenticationFlow"
+ p:flowId="authn/IPAddress"
p:order="%{idp.authn.IPAddress.order:1000}"
p:nonBrowserSupported="%{idp.authn.IPAddress.nonBrowserSupported:true}"
p:passiveAuthenticationSupported="%{idp.authn.IPAddress.passiveAuthenticationSupported:true}"
@@ -91,7 +93,8 @@
</property>
</bean>
- <bean p:id="authn/Function" parent="shibboleth.AuthenticationFlow"
+ <bean id="authn/Function" parent="shibboleth.AuthenticationFlow"
+ p:flowId="authn/Function"
p:order="%{idp.authn.Function.order:1000}"
p:nonBrowserSupported="%{idp.authn.Function.nonBrowserSupported:true}"
p:passiveAuthenticationSupported="%{idp.authn.Function.passiveAuthenticationSupported:true}"
@@ -112,7 +115,8 @@
</property>
</bean>
- <bean p:id="authn/External" parent="shibboleth.AuthenticationFlow"
+ <bean id="authn/External" parent="shibboleth.AuthenticationFlow"
+ p:flowId="authn/External"
p:order="%{idp.authn.External.order:1000}"
p:nonBrowserSupported="%{idp.authn.External.nonBrowserSupported:false}"
p:passiveAuthenticationSupported="%{idp.authn.External.passiveAuthenticationSupported:false}"
@@ -133,7 +137,8 @@
</property>
</bean>
- <bean p:id="authn/RemoteUser" parent="shibboleth.AuthenticationFlow"
+ <bean id="authn/RemoteUser" parent="shibboleth.AuthenticationFlow"
+ p:flowId="authn/RemoteUser"
p:order="%{idp.authn.RemoteUser.order:1000}"
p:nonBrowserSupported="%{idp.authn.RemoteUser.nonBrowserSupported:false}"
p:passiveAuthenticationSupported="%{idp.authn.RemoteUser.passiveAuthenticationSupported:false}"
@@ -154,7 +159,8 @@
</property>
</bean>
- <bean p:id="authn/RemoteUserInternal" parent="shibboleth.AuthenticationFlow"
+ <bean id="authn/RemoteUserInternal" parent="shibboleth.AuthenticationFlow"
+ p:flowId="authn/RemoteUserInternal"
p:order="%{idp.authn.RemoteUserInternal.order:1000}"
p:nonBrowserSupported="%{idp.authn.RemoteUserInternal.nonBrowserSupported:true}"
p:passiveAuthenticationSupported="%{idp.authn.RemoteUserInternal.passiveAuthenticationSupported:false}"
@@ -175,7 +181,8 @@
</property>
</bean>
- <bean p:id="authn/SAML" parent="shibboleth.AuthenticationFlow"
+ <bean id="authn/SAML" parent="shibboleth.AuthenticationFlow"
+ p:flowId="authn/SAML"
p:order="%{idp.authn.SAML.order:1000}"
p:nonBrowserSupported="%{idp.authn.SAML.nonBrowserSupported:false}"
p:passiveAuthenticationSupported="%{idp.authn.SAML.passiveAuthenticationSupported:true}"
@@ -196,7 +203,8 @@
</property>
</bean>
- <bean p:id="authn/SPNEGO" parent="shibboleth.AuthenticationFlow"
+ <bean id="authn/SPNEGO" parent="shibboleth.AuthenticationFlow"
+ p:flowId="authn/SPNEGO"
p:order="%{idp.authn.SPNEGO.order:1000}"
p:nonBrowserSupported="%{idp.authn.SPNEGO.nonBrowserSupported:false}"
p:passiveAuthenticationSupported="%{idp.authn.SPNEGO.passiveAuthenticationSupported:false}"
@@ -217,7 +225,8 @@
</property>
</bean>
- <bean p:id="authn/X509" parent="shibboleth.AuthenticationFlow"
+ <bean id="authn/X509" parent="shibboleth.AuthenticationFlow"
+ p:flowId="authn/X509"
p:order="%{idp.authn.X509.order:1000}"
p:nonBrowserSupported="%{idp.authn.X509.nonBrowserSupported:false}"
p:passiveAuthenticationSupported="%{idp.authn.X509.passiveAuthenticationSupported:false}"
@@ -238,7 +247,8 @@
</property>
</bean>
- <bean p:id="authn/X509Internal" parent="shibboleth.AuthenticationFlow"
+ <bean id="authn/X509Internal" parent="shibboleth.AuthenticationFlow"
+ p:flowId="authn/X509Internal"
p:order="%{idp.authn.X509Internal.order:1000}"
p:nonBrowserSupported="%{idp.authn.X509Internal.nonBrowserSupported:false}"
p:passiveAuthenticationSupported="%{idp.authn.X509Internal.passiveAuthenticationSupported:false}"
@@ -259,7 +269,8 @@
</property>
</bean>
- <bean p:id="authn/MFA" parent="shibboleth.AuthenticationFlow"
+ <bean id="authn/MFA" parent="shibboleth.AuthenticationFlow"
+ p:flowId="authn/MFA"
p:order="%{idp.authn.MFA.order:1000}"
p:nonBrowserSupported="%{idp.authn.MFA.nonBrowserSupported:true}"
p:passiveAuthenticationSupported="%{idp.authn.MFA.passiveAuthenticationSupported:true}"
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/authn-beans.xml
index 824d6320e..1e0f5a0d2 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/authn-beans.xml
@@ -15,7 +15,7 @@
<bean id="PotentialFlowsLookup" parent="shibboleth.ContextFunctions.Expression"
p:customObject-ref="shibboleth.AuthenticationFlowDescriptorManager"
c:outputType="#{T(java.util.Collection)}"
- c:expression="#input.getSubcontext(T(net.shibboleth.idp.profile.context.SpringRequestContext)).getRequestContext().getFlowScope().get('potentialFlows') ?:
+ c:expression="#input.ensureSubcontext(T(net.shibboleth.idp.profile.context.SpringRequestContext)).getRequestContext().getFlowScope().get('potentialFlows') ?:
#custom.getComponents().?[id matches 'authn/(' + '%{idp.authn.flows:Password}'.trim() + ')']" />
<bean id="PopulateAuthenticationContext"
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/authn-flow.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/authn-flow.xml
index 3751a4855..bb2eda6f0 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/authn-flow.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/authn-flow.xml
@@ -35,7 +35,7 @@
<!-- Check for an existing session, and do additional steps in that case. -->
<decision-state id="TestForSession">
- <if test="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.session.context.SessionContext)) != null and opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.session.context.SessionContext)).getIdPSession() != null"
+ <if test="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.session.context.SessionContext)) != null and opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.idp.session.context.SessionContext)).getIdPSession() != null"
then="SessionExists"
else="FilterFlows" />
</decision-state>
@@ -86,7 +86,7 @@
<!-- Checks if authentication flow already completed c14n. -->
<decision-state id="CheckSubjectCanonicalization">
- <if test="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.SubjectCanonicalizationContext)).getPrincipalName() == null"
+ <if test="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.idp.authn.context.SubjectCanonicalizationContext)).getPrincipalName() == null"
then="PopulateSubjectCanonicalizationContext"
else="DetectIdentitySwitch" />
</decision-state>
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/mfa-authn-flow.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/mfa-authn-flow.xml
index 895a22d23..9298c033d 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/mfa-authn-flow.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/mfa-authn-flow.xml
@@ -21,7 +21,7 @@
<transition on="proceed" to="TransitionMultiFactorAuthentication" />
<on-exit>
- <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).getSubcontext(T(net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext))" result="flowScope.mfaContext" />
+ <evaluate expression="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).getSubcontext(T(net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext))" result="flowScope.mfaContext" />
</on-exit>
</action-state>
@@ -36,7 +36,7 @@
<if test="mfaContext.getNextFlowId() != null" then="CallSubflow" else="FinalizeMultiFactorAuthentication" />
</decision-state>
- <subflow-state id="CallSubflow" subflow="#{mfaContext.getNextFlowId()}">
+ <subflow-state id="CallSubflow" subflow="#{mfaContext.getNextWebFlowId()}">
<input name="calledAsSubflow" value="true" />
<transition to="TransitionMultiFactorAuthentication" />
</subflow-state>
diff --git a/idp-session-impl/src/test/java/net/shibboleth/idp/session/impl/StorageBackedSessionManagerTest.java b/idp-session-impl/src/test/java/net/shibboleth/idp/session/impl/StorageBackedSessionManagerTest.java
index 9acb67385..a4a9dc9dd 100644
--- a/idp-session-impl/src/test/java/net/shibboleth/idp/session/impl/StorageBackedSessionManagerTest.java
+++ b/idp-session-impl/src/test/java/net/shibboleth/idp/session/impl/StorageBackedSessionManagerTest.java
@@ -85,6 +85,7 @@ public class StorageBackedSessionManagerTest extends SessionManagerBaseTestCase
AuthenticationFlowDescriptor foo = new AuthenticationFlowDescriptor();
foo.setId("AuthenticationFlow/Foo");
+ foo.setFlowId("authn/Foo");
foo.setLifetime(Duration.ofMinutes(1));
foo.setInactivityTimeout(Duration.ofMinutes(1));
foo.setResultSerializer(resultSerializer);
@@ -92,6 +93,7 @@ public class StorageBackedSessionManagerTest extends SessionManagerBaseTestCase
AuthenticationFlowDescriptor bar = new AuthenticationFlowDescriptor();
bar.setId("AuthenticationFlow/Bar");
+ bar.setFlowId("authn/Bar");
bar.setLifetime(Duration.ofMinutes(1));
bar.setInactivityTimeout(Duration.ofMinutes(1));
bar.setResultSerializer(resultSerializer);
diff --git a/idp-session-impl/src/test/java/net/shibboleth/idp/session/impl/UpdateSessionWithAuthenticationResultTest.java b/idp-session-impl/src/test/java/net/shibboleth/idp/session/impl/UpdateSessionWithAuthenticationResultTest.java
index 1820578e5..0bacd845a 100644
--- a/idp-session-impl/src/test/java/net/shibboleth/idp/session/impl/UpdateSessionWithAuthenticationResultTest.java
+++ b/idp-session-impl/src/test/java/net/shibboleth/idp/session/impl/UpdateSessionWithAuthenticationResultTest.java
@@ -77,6 +77,7 @@ public class UpdateSessionWithAuthenticationResultTest extends SessionManagerBas
flowDescriptor = new AuthenticationFlowDescriptor();
flowDescriptor.setId("test1");
+ flowDescriptor.setFlowId("authn/test1");
flowDescriptor.setResultSerializer(resultSerializer);
flowDescriptor.initialize();
sessionManager.setAuthenticationFlowDescriptors(CollectionSupport.arrayAsList(flowDescriptor));
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list