[java-identity-provider] branch master updated: IDP-1277 - Maximum Authentication Age
Scott Cantor
cantor.2 at osu.edu
Wed Aug 29 10:59:17 EDT 2018
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch master
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=e2831e533c703534466f031a9355934b6a50d7e9
The following commit(s) were added to refs/heads/master by this push:
new e2831e5 IDP-1277 - Maximum Authentication Age
e2831e5 is described below
commit e2831e533c703534466f031a9355934b6a50d7e9
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Aug 29 10:59:15 2018 -0400
IDP-1277 - Maximum Authentication Age
https://issues.shibboleth.net/jira/browse/IDP-1277
---
.../idp/authn/context/AuthenticationContext.java | 36 ++++++++++++++++++++++
.../idp/authn/impl/FilterFlowsByForcedAuthn.java | 5 +--
.../PopulateMultiFactorAuthenticationContext.java | 9 +++++-
.../authn/impl/FilterFlowsByForceAuthnTest.java | 10 +++---
...pulateMultiFactorAuthenticationContextTest.java | 26 +++++++++++++++-
.../impl/ExtractActiveAuthenticationResults.java | 11 ++++++-
6 files changed, 88 insertions(+), 9 deletions(-)
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/AuthenticationContext.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/AuthenticationContext.java
index cffda7e..8a5e3f6 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/AuthenticationContext.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/AuthenticationContext.java
@@ -30,6 +30,7 @@ import net.shibboleth.idp.authn.AuthenticationResult;
import net.shibboleth.idp.authn.AuthenticationFlowDescriptor;
import net.shibboleth.idp.authn.principal.PrincipalEvalPredicateFactoryRegistry;
import net.shibboleth.idp.authn.principal.PrincipalSupportingComponent;
+import net.shibboleth.utilities.java.support.annotation.Duration;
import net.shibboleth.utilities.java.support.annotation.constraint.Live;
import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
@@ -69,6 +70,9 @@ public final class AuthenticationContext extends BaseContext {
/** A non-normative hint some protocols support to indicate who the subject might be. */
@Nullable private String hintedName;
+
+ /** Allowed time in ms since an {@link AuthenticationResult} was created that it can be reused for this request. */
+ @NonNegative @Duration private long maxAge;
/** Flows that are known to the system. */
@Nonnull @NonnullElements private final Map<String,AuthenticationFlowDescriptor> availableFlows;
@@ -317,6 +321,37 @@ public final class AuthenticationContext extends BaseContext {
hintedName = StringSupport.trimOrNull(hint);
return this;
}
+
+ /**
+ * Get duration in milliseconds since an {@link AuthenticationResult} was created that
+ * allows it to be reused for this request.
+ *
+ * <p>If zero, no constraint is applied.</p>
+ *
+ * @return duration in milliseconds, or zero
+ *
+ * @since 3.4.0
+ */
+ @NonNegative @Duration public long getMaxAge() {
+ return maxAge;
+ }
+
+ /**
+ * Set duration in milliseconds since an {@link AuthenticationResult} was created that
+ * allows it to be reused for this request.
+ *
+ * <p>Set to zero to apply no constraint.</p>
+ *
+ * @param age duration in milliseconds, or zero
+ *
+ * @return this context
+ *
+ * @since 3.4.0
+ */
+ @Nonnull public AuthenticationContext setMaxAge(@NonNegative @Duration final long age) {
+ maxAge = Constraint.isGreaterThanOrEqual(0, age, "MaxAge cannot be negative");
+ return this;
+ }
/**
* Get the authentication flow that was attempted in order to authenticate the user.
@@ -542,6 +577,7 @@ public final class AuthenticationContext extends BaseContext {
.add("isPassive", isPassive)
.add("forceAuthn", forceAuthn)
.add("hintedName", hintedName)
+ .add("maxAge", maxAge)
.add("potentialFlows", potentialFlows.keySet())
.add("activeResults", activeResults.keySet())
.add("attemptedFlow", attemptedFlow)
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByForcedAuthn.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByForcedAuthn.java
index 910fc9d..40609ad 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByForcedAuthn.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByForcedAuthn.java
@@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory;
/**
* An authentication action that filters out potential authentication flows if the request requires
- * forced authentication behavior and the flows don't support forced authentication.
+ * forced authentication or max age behavior and the flows don't support forced authentication.
*
* @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
* @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class) != null</pre>
@@ -49,7 +49,7 @@ public class FilterFlowsByForcedAuthn extends AbstractAuthenticationAction {
protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@Nonnull final AuthenticationContext authenticationContext) {
- if (!authenticationContext.isForceAuthn()) {
+ if (!authenticationContext.isForceAuthn() && authenticationContext.getMaxAge() == 0) {
log.debug("{} Request does not have forced authentication requirement, nothing to do", getLogPrefix());
return false;
}
@@ -84,4 +84,5 @@ public class FilterFlowsByForcedAuthn extends AbstractAuthenticationAction {
log.debug("{} Potential authentication flows left after filtering: {}", getLogPrefix(), potentialFlows);
}
}
+
}
\ No newline at end of file
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 f037707..26aed1f 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
@@ -232,7 +232,14 @@ public class PopulateMultiFactorAuthenticationContext extends AbstractAuthentica
if (descriptor != null) {
if (descriptor.apply(profileRequestContext)) {
if (descriptor.isResultActive(candidate)) {
- results.add(candidate);
+ if (authenticationContext.getMaxAge() > 0
+ && candidate.getAuthenticationInstant() + authenticationContext.getMaxAge()
+ < System.currentTimeMillis()) {
+ log.debug("{} Ignoring active result from login flow {} due to maxAge on request",
+ getLogPrefix(), candidate.getAuthenticationFlowId());
+ } else {
+ results.add(candidate);
+ }
} else {
log.debug("{} Result from login flow {} has expired", getLogPrefix(), descriptor.getId());
}
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FilterFlowsByForceAuthnTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FilterFlowsByForceAuthnTest.java
index 308391c..4c09972 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FilterFlowsByForceAuthnTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FilterFlowsByForceAuthnTest.java
@@ -39,8 +39,9 @@ public class FilterFlowsByForceAuthnTest extends BaseAuthenticationContextTest {
}
@Test public void testNonForced() throws Exception {
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class, false);
+ final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
authCtx.setForceAuthn(false);
+ authCtx.setMaxAge(0);
final Event event = action.execute(src);
ActionTestingSupport.assertProceedEvent(event);
@@ -48,7 +49,7 @@ public class FilterFlowsByForceAuthnTest extends BaseAuthenticationContextTest {
}
@Test public void testNoFiltering() {
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class, false);
+ final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
authCtx.setForceAuthn(true);
for (final AuthenticationFlowDescriptor fd : authCtx.getPotentialFlows().values()) {
fd.setForcedAuthenticationSupported(true);
@@ -60,8 +61,8 @@ public class FilterFlowsByForceAuthnTest extends BaseAuthenticationContextTest {
}
@Test public void testPartialFiltering() {
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class, false);
- authCtx.setForceAuthn(true);
+ final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
+ authCtx.setMaxAge(3600 * 1000);
authCtx.getPotentialFlows().get("test2").setForcedAuthenticationSupported(true);
final Event event = action.execute(src);
@@ -70,4 +71,5 @@ public class FilterFlowsByForceAuthnTest extends BaseAuthenticationContextTest {
Assert.assertNull(authCtx.getPotentialFlows().get("test1"));
Assert.assertNotNull(authCtx.getPotentialFlows().get("test2"));
}
+
}
\ No newline at end of file
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 b6f0a73..3f64bae 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
@@ -96,6 +96,12 @@ public class PopulateMultiFactorAuthenticationContextTest {
subject.getPrincipals().add(new AuthenticationResultPrincipal(result));
result = new AuthenticationResult("baz", new Subject());
subject.getPrincipals().add(new AuthenticationResultPrincipal(result));
+ result = new AuthenticationResult("bav", new Subject());
+ result.setAuthenticationInstant(System.currentTimeMillis() - 1000 * 1000);
+ subject.getPrincipals().add(new AuthenticationResultPrincipal(result));
+ result = new AuthenticationResult("bag", new Subject());
+ result.setAuthenticationInstant(System.currentTimeMillis() - 2000 * 1000);
+ subject.getPrincipals().add(new AuthenticationResultPrincipal(result));
ac.getActiveResults().put("authn/MFA", new AuthenticationResult("authn/MFA", subject));
@@ -113,6 +119,22 @@ public class PopulateMultiFactorAuthenticationContextTest {
desc.initialize();
ac.getAvailableFlows().put(desc.getId(), desc);
+ desc = new AuthenticationFlowDescriptor();
+ desc.setId("bav");
+ desc.setResultSerializer(new DefaultAuthenticationResultSerializer());
+ desc.setLifetime(3600 * 1000);
+ desc.initialize();
+ ac.getAvailableFlows().put(desc.getId(), desc);
+
+ desc = new AuthenticationFlowDescriptor();
+ desc.setId("bag");
+ desc.setResultSerializer(new DefaultAuthenticationResultSerializer());
+ desc.setLifetime(3600 * 1000);
+ desc.initialize();
+ ac.getAvailableFlows().put(desc.getId(), desc);
+
+ ac.setMaxAge(1800 * 1000);
+
action.setTransitionMapLookupStrategy(
FunctionSupport.<ProfileRequestContext,Map<String,MultiFactorAuthenticationTransition>>constant(
Collections.singletonMap("", new MultiFactorAuthenticationTransition())));
@@ -123,10 +145,12 @@ public class PopulateMultiFactorAuthenticationContextTest {
final MultiFactorAuthenticationContext mfa = ac.getSubcontext(MultiFactorAuthenticationContext.class);
Assert.assertNotNull(mfa);
Assert.assertEquals(ac.getAttemptedFlow(), mfa.getAuthenticationFlowDescriptor());
- Assert.assertEquals(mfa.getActiveResults().size(), 1);
+ Assert.assertEquals(mfa.getActiveResults().size(), 2);
Assert.assertNull(mfa.getActiveResults().get("foo"));
Assert.assertNotNull(mfa.getActiveResults().get("bar"));
Assert.assertNull(mfa.getActiveResults().get("baz"));
+ Assert.assertNotNull(mfa.getActiveResults().get("bav"));
+ Assert.assertNull(mfa.getActiveResults().get("bag"));
}
}
\ No newline at end of file
diff --git a/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/ExtractActiveAuthenticationResults.java b/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/ExtractActiveAuthenticationResults.java
index 4c81071..77f1595 100644
--- a/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/ExtractActiveAuthenticationResults.java
+++ b/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/ExtractActiveAuthenticationResults.java
@@ -89,7 +89,7 @@ public class ExtractActiveAuthenticationResults extends AbstractAuthenticationAc
@Override
protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@Nonnull final AuthenticationContext authenticationContext) {
-
+
final SessionContext ctx = sessionContextLookupStrategy.apply(profileRequestContext);
if (ctx != null) {
session = ctx.getIdPSession();
@@ -110,6 +110,9 @@ public class ExtractActiveAuthenticationResults extends AbstractAuthenticationAc
authenticationContext.setHintedName(session.getPrincipalName());
}
+ final long maxAge = authenticationContext.getMaxAge();
+ final long now = System.currentTimeMillis();
+
final List<AuthenticationResult> actives = new ArrayList<>();
for (final AuthenticationResult result : session.getAuthenticationResults()) {
final AuthenticationFlowDescriptor descriptor =
@@ -121,6 +124,12 @@ public class ExtractActiveAuthenticationResults extends AbstractAuthenticationAc
}
if (descriptor.isResultActive(result)) {
+ if (maxAge > 0 && result.getAuthenticationInstant() + maxAge < now) {
+ log.debug("{} Authentication result {} exceeds maxAge setting, skiping it", getLogPrefix(),
+ result.getAuthenticationFlowId());
+ continue;
+ }
+
log.debug("{} Authentication result {} is active, copying from session", getLogPrefix(),
result.getAuthenticationFlowId());
actives.add(result);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list