[java-identity-provider] branch master updated: IDP-1494 - Login flow for proxied SAML authentication
Scott Cantor
cantor.2 at osu.edu
Wed Nov 27 14:09:45 EST 2019
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch master
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=38345927349c829c13baf0501995bb0e066b0ab7
The following commit(s) were added to refs/heads/master by this push:
new 3834592 IDP-1494 - Login flow for proxied SAML authentication
3834592 is described below
commit 38345927349c829c13baf0501995bb0e066b0ab7
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Nov 27 14:09:41 2019 -0500
IDP-1494 - Login flow for proxied SAML authentication
https://issues.shibboleth.net/jira/browse/IDP-1494
Implement proxy count reuse and flow selection controls.
Fix some warnings and remove some old classes.
---
.../idp/authn/AuthenticationFlowDescriptor.java | 69 +++++-
.../net/shibboleth/idp/authn/AuthnEventIds.java | 5 +-
.../authn/MultiFactorAuthenticationTransition.java | 1 +
.../idp/authn/impl/ExternalAuthenticationImpl.java | 1 +
.../idp/authn/impl/FilterFlowsByAttribute.java | 258 ---------------------
.../idp/authn/impl/FilterFlowsByForcedAuthn.java | 6 +-
.../authn/impl/FilterFlowsByNonBrowserSupport.java | 6 +-
.../idp/authn/impl/FilterFlowsByPassivity.java | 87 -------
.../idp/authn/impl/FinalizeAuthentication.java | 1 +
.../idp/authn/impl/SelectAuthenticationFlow.java | 62 +++--
.../impl/TransitionMultiFactorAuthentication.java | 6 +
.../shibboleth/idp/authn/impl/X509AuthServlet.java | 1 +
.../idp/authn/impl/FilterFlowsByAttributeTest.java | 144 ------------
.../idp/authn/impl/FilterFlowsByPassivityTest.java | 73 ------
.../main/resources/conf/authn/general-authn.xml | 24 +-
idp-conf/src/main/resources/conf/errors.xml | 1 +
.../src/main/resources/system/conf/utilities.xml | 5 +
.../system/flows/authn/authn-abstract-flow.xml | 2 +
18 files changed, 152 insertions(+), 600 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 067b39a..8f45d20 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
@@ -33,6 +33,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.security.auth.Subject;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.authn.principal.PrincipalSupportingComponent;
import net.shibboleth.idp.profile.FlowDescriptor;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
@@ -78,9 +79,12 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
/** Whether this flow supports forced authentication. */
private boolean supportsForced;
- /** Whether this flow should honor proxy restrictions. */
+ /** Whether this flow should honor proxy restrictions toward RPs. */
private boolean proxyRestrictionsEnforced;
-
+
+ /** Whether this flow should honor proxy scoping restrictions toward IdPs. */
+ private boolean proxyScopingEnforced;
+
/** Whether this flow allows reuse of its results. */
@Nonnull private Predicate<ProfileRequestContext> reuseCondition;
@@ -109,7 +113,8 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
public AuthenticationFlowDescriptor() {
supportsNonBrowser = true;
proxyRestrictionsEnforced = true;
- reuseCondition = Predicates.alwaysTrue();
+ proxyScopingEnforced = false;
+ reuseCondition = new ProxyCountPredicate();
supportedPrincipals = new Subject();
activationCondition = Predicates.alwaysTrue();
inactivityTimeout = Duration.ofMinutes(30);
@@ -177,9 +182,9 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
}
/**
- * Gets whether this flow's results should honor restrictions on proxying.
+ * Gets whether this flow's results should honor restrictions on proxying toward RPs.
*
- * @return true iff upstream proxying restrictions should be honored
+ * @return true iff proxying restrictions issued by IdPs should be honored
*
* @since 4.0.0
*/
@@ -188,7 +193,7 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
}
/**
- * Sets whether this flow's results should honor restrictions on proxying.
+ * Sets whether this flow's results should honor restrictions on proxying toward RPs
*
* <p>Defaults to true.</p>
*
@@ -199,8 +204,32 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
public void setProxyRestrictionsEnforced(final boolean flag) {
proxyRestrictionsEnforced = flag;
}
+
+ /**
+ * Gets whether this flow's results should honor restrictions on proxying toward IdPs.
+ *
+ * @return true iff proxying restrictions issued by RPs should be honored
+ *
+ * @since 4.0.0
+ */
+ public boolean isProxyScopingEnforced() {
+ return proxyScopingEnforced;
+ }
/**
+ * Sets whether this flow's results should honor restrictions on proxying toward IdPs.
+ *
+ * <p>Defaults to false. Should be enabled for flows that represent proxied authentication.</p>
+ *
+ * @param flag flag to set
+ *
+ * @since 4.0.0
+ */
+ public void setProxyScopingEnforced(final boolean flag) {
+ proxyScopingEnforced = flag;
+ }
+
+ /**
* Set condition controlling whether results from this flow should be reused for SSO.
*
* <p>Defaults to {@link Predicates#alwaysTrue()}.</p>
@@ -212,7 +241,9 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
public void setReuseCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- reuseCondition = Constraint.isNotNull(condition, "Predicate cannot be null");
+ // Auto-installs a guard against use of a proxied result if requester proxy count is zero.
+ reuseCondition = PredicateSupport.and(new ProxyCountPredicate(),
+ Constraint.isNotNull(condition, "Predicate cannot be null"));
}
/**
@@ -381,7 +412,7 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
*/
@Nonnull public AuthenticationResult newAuthenticationResult(@Nonnull final Subject subject) {
final AuthenticationResult result = new AuthenticationResult(getId(), subject);
-
+
if (proxyRestrictionsEnforced) {
result.setReuseCondition(PredicateSupport.and(reuseCondition, result.new ProxyRestrictionReusePredicate()));
} else {
@@ -427,6 +458,7 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
*
* @since 4.0.0
*/
+ @SuppressWarnings("unchecked")
@Nullable public <T extends Principal> T getHighestWeighted(
@Nonnull @NonnullElements final Collection<T> principals) {
if (principals.isEmpty()) {
@@ -478,7 +510,6 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
private class WeightedComparator<T> implements Comparator<T> {
/** {@inheritDoc} */
- @Override
public int compare(final T o1, final T o2) {
final int weight1 = principalWeightMap.containsKey(o1) ? principalWeightMap.get(o1) : 0;
@@ -494,6 +525,26 @@ public class AuthenticationFlowDescriptor extends AbstractIdentifiableInitializa
}
+ /**
+ * A {@link Predicate} that implements a cross-check between an effective proxy count of zero and
+ * whether a descriptor is honoring the limit.
+ */
+ private class ProxyCountPredicate implements Predicate<ProfileRequestContext> {
+
+ /** {@inheritDoc} */
+ public boolean test(@Nullable final ProfileRequestContext input) {
+
+ if (proxyScopingEnforced) {
+ final AuthenticationContext authnCtx = input.getSubcontext(AuthenticationContext.class);
+ if (authnCtx != null && authnCtx.getProxyCount() != null && authnCtx.getProxyCount() == 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+
static {
STORAGE_EXPIRATION_OFFSET = Duration.ofMinutes(10);
}
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthnEventIds.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthnEventIds.java
index c449094..ac0356b 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthnEventIds.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthnEventIds.java
@@ -80,7 +80,10 @@ public final class AuthnEventIds {
/** ID of event returned if an authenticated subject doesn't match the identity in an existing session. */
@Nonnull @NotEmpty public static final String IDENTITY_SWITCH = "IdentitySwitch";
-
+
+ /** ID of event returned if proxying is disallowed. */
+ @Nonnull @NotEmpty public static final String PROXY_COUNT_EXCEEDED = "ProxyCountExceeded";
+
/** ID of event returned if authentication throws an exception unrelated to credential validation. */
@Nonnull @NotEmpty public static final String AUTHN_EXCEPTION = "AuthenticationException";
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/MultiFactorAuthenticationTransition.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/MultiFactorAuthenticationTransition.java
index a861f22..6e5e78c 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/MultiFactorAuthenticationTransition.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/MultiFactorAuthenticationTransition.java
@@ -90,6 +90,7 @@ public class MultiFactorAuthenticationTransition {
*
* @param map map of transition rules
*/
+ @SuppressWarnings("unchecked")
public void setNextFlowStrategyMap(@Nonnull @NonnullElements final Map<String,Object> map) {
Constraint.isNotNull(map, "Transition strategy map cannot be null");
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/ExternalAuthenticationImpl.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/ExternalAuthenticationImpl.java
index 1b4c258..12e30d5 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/ExternalAuthenticationImpl.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/ExternalAuthenticationImpl.java
@@ -110,6 +110,7 @@ public class ExternalAuthenticationImpl extends ExternalAuthentication {
// Checkstyle: CyclomaticComplexity|MethodLength OFF
/** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
@Override
protected void doFinish(@Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response,
@Nonnull final ProfileRequestContext profileRequestContext,
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByAttribute.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByAttribute.java
deleted file mode 100644
index 5d1f683..0000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByAttribute.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You 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.impl;
-
-import java.security.Principal;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.attribute.context.AttributeContext;
-import net.shibboleth.idp.authn.AbstractAuthenticationAction;
-import net.shibboleth.idp.authn.AuthenticationFlowDescriptor;
-import net.shibboleth.idp.authn.AuthenticationResult;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.primitive.DeprecationSupport;
-import net.shibboleth.utilities.java.support.primitive.DeprecationSupport.ObjectType;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * An authentication action that filters out potential authentication flows by comparing an {@link IdPAttribute}'s
- * values to the custom principals supported by each flow.
- *
- * <p>It optionally (and by default) filters out active {@link AuthenticationResult} objects from possible reuse
- * for SSO.</p>
- *
- * <p>The type of principals is ignored, and only string-based values of an attribute are supported.</p>
- *
- * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
- * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class) != null</pre>
- * @post AuthenticationContext.getPotentialFlows() and AuthenticationContext.getActiveResults() are modified as above.
- */
-public class FilterFlowsByAttribute extends AbstractAuthenticationAction {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(FilterFlowsByAttribute.class);
-
- /** Lookup strategy for locating {@link AttributeContext}. */
- @Nonnull private Function<ProfileRequestContext, AttributeContext> attributeContextLookupStrategy;
-
- /** The attribute ID to look for. */
- @Nullable private String attributeId;
-
- /** Whether to also filter active results to limit SSO. */
- private boolean filterActiveResults;
-
- /** The attribute to match against. */
- @Nullable private IdPAttribute attribute;
-
- /** Constructor. */
- public FilterFlowsByAttribute() {
- attributeContextLookupStrategy =
- new ChildContextLookup<>(AttributeContext.class).compose(
- new ChildContextLookup<>(AuthenticationContext.class));
- filterActiveResults = true;
- }
-
- /**
- * Set the lookup strategy for the {@link AttributeContext}.
- *
- * @param strategy lookup strategy
- */
- public void setAttributeContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext, AttributeContext> strategy) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
- attributeContextLookupStrategy =
- Constraint.isNotNull(strategy, "AttributeContext lookup strategy cannot be null");
- }
-
- /**
- * Set the attribute ID to look for.
- *
- * @param id attribute ID to look for
- */
- public void setAttributeId(@Nullable final String id) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
- attributeId = StringSupport.trimOrNull(id);
- }
-
- /**
- * Set whether to filter active results (those usable for SSO) as well as inactive flows.
- *
- * <p>Defaults to true</p>
- *
- * @param flag flag to set
- */
- public void setFilterActiveResults(final boolean flag) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
- filterActiveResults = flag;
- }
-
- /** {@inheritDoc} */
- @Override protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
-
- if (!super.doPreExecute(profileRequestContext, authenticationContext) || attributeId == null) {
- return false;
- }
-
- DeprecationSupport.warnOnce(ObjectType.ACTION, getClass().getSimpleName(), null, "the MFA login flow");
-
- final AttributeContext attributeCtx = attributeContextLookupStrategy.apply(profileRequestContext);
- if (attributeCtx == null) {
- log.debug("{} Request does not contain an AttributeContext, nothing to do", getLogPrefix());
- return false;
- }
-
- attribute = attributeCtx.getIdPAttributes().get(attributeId);
- if (attribute == null || attribute.getValues().isEmpty()) {
- log.debug("{} Attribute {} has no values, nothing to do", getLogPrefix(), attributeId);
- return false;
- }
-
- return true;
- }
-
- /** {@inheritDoc} */
- @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
-
- log.debug("{} Filtering inactive flows available for use", getLogPrefix());
-
- final Map<String,AuthenticationFlowDescriptor> potentialFlows = authenticationContext.getPotentialFlows();
-
- final Iterator<Entry<String,AuthenticationFlowDescriptor>> descriptorItr = potentialFlows.entrySet().iterator();
- while (descriptorItr.hasNext()) {
- final AuthenticationFlowDescriptor descriptor = descriptorItr.next().getValue();
- final String match = getMatch(descriptor);
- if (match != null) {
- log.debug("{} Retaining flow {}, matched custom Principal {}", getLogPrefix(), descriptor.getId(),
- match);
- } else {
- log.debug("{} Removing flow {}, Principals did not match any attribute values", getLogPrefix(),
- descriptor.getId());
- descriptorItr.remove();
- }
- }
-
- if (potentialFlows.size() == 0) {
- log.info("{} No potential authentication flows remain after filtering", getLogPrefix());
- } else {
- log.debug("{} Potential authentication flows left after filtering: {}", getLogPrefix(), potentialFlows);
- }
-
- if (filterActiveResults) {
- log.debug("{} Filtering active results available for reuse", getLogPrefix());
-
- final Map<String,AuthenticationResult> activeResults = authenticationContext.getActiveResults();
-
- final Iterator<Entry<String,AuthenticationResult>> resultItr = activeResults.entrySet().iterator();
- while (resultItr.hasNext()) {
- final AuthenticationResult result = resultItr.next().getValue();
- final String match = getMatch(result);
- if (match != null) {
- log.debug("{} Retaining active result from flow {}, matched custom Principal {}", getLogPrefix(),
- result.getAuthenticationFlowId(), match);
- } else {
- log.debug("{} Removing active result from flow {}, Principals did not match any attribute values",
- getLogPrefix(), result.getAuthenticationFlowId());
- resultItr.remove();
- }
- }
-
- if (activeResults.size() == 0) {
- log.info("{} No active authentication results remain after filtering", getLogPrefix());
- } else {
- log.debug("{} Active authentication results left after filtering: {}", getLogPrefix(), activeResults);
- }
- }
- }
-
- /**
- * Compare the flow's custom principal names to the string values of the attribute.
- *
- * @param flow flow to examine
- *
- * @return a match between the flow's principal names and the attribute's string values, or null
- */
- @Nullable private String getMatch(@Nonnull final AuthenticationFlowDescriptor flow) {
-
- log.debug("{} Looking for match for flow {} against values for attribute {}", getLogPrefix(), flow.getId(),
- attribute.getId());
- for (final Principal p : flow.getSupportedPrincipals()) {
- log.debug("{} Comparing principal {} against attribute values {}", getLogPrefix(), p.getName(),
- attribute.getValues());
- for (final IdPAttributeValue val : attribute.getValues()) {
- if (val instanceof StringAttributeValue) {
- final StringAttributeValue stringVal = (StringAttributeValue) val;
- if (Objects.equals(stringVal.getValue(), p.getName())) {
- return p.getName();
- }
- }
- }
- }
-
- return null;
- }
-
- /**
- * Compare the result's custom principal names to the string values of the attribute.
- *
- * @param result result to examine
- *
- * @return a match between the result's principal names and the attribute's string values, or null
- */
- @Nullable private String getMatch(@Nonnull final AuthenticationResult result) {
-
- log.debug("{} Looking for match for active result of flow {} against values for attribute {}",
- getLogPrefix(), result.getAuthenticationFlowId(), attribute.getId());
- for (final Principal p : result.getSupportedPrincipals(Principal.class)) {
- log.debug("{} Comparing principal {} against attribute values {}", getLogPrefix(), p.getName(),
- attribute.getValues());
- for (final IdPAttributeValue val : attribute.getValues()) {
- if (val instanceof StringAttributeValue) {
- final StringAttributeValue stringVal = (StringAttributeValue) val;
- if (val instanceof StringAttributeValue && Objects.equals(stringVal.getValue(), p.getName())) {
- return p.getName();
- }
- }
- }
- }
-
- return null;
- }
-
-}
\ No newline at end of file
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 79cfe4d..d68caba 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
@@ -49,12 +49,16 @@ public class FilterFlowsByForcedAuthn extends AbstractAuthenticationAction {
protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@Nonnull final AuthenticationContext authenticationContext) {
+ if (!super.doPreExecute(profileRequestContext, authenticationContext)) {
+ return false;
+ }
+
if (!authenticationContext.isForceAuthn() && authenticationContext.getMaxAge() == null) {
log.debug("{} Request does not have forced authentication requirement, nothing to do", getLogPrefix());
return false;
}
- return super.doPreExecute(profileRequestContext, authenticationContext);
+ return true;
}
/** {@inheritDoc} */
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByNonBrowserSupport.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByNonBrowserSupport.java
index 69b4327..86311e5 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByNonBrowserSupport.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByNonBrowserSupport.java
@@ -49,12 +49,16 @@ public class FilterFlowsByNonBrowserSupport extends AbstractAuthenticationAction
protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@Nonnull final AuthenticationContext authenticationContext) {
+ if (!super.doPreExecute(profileRequestContext, authenticationContext)) {
+ return false;
+ }
+
if (profileRequestContext.isBrowserProfile()) {
log.debug("{} Request does not have non-browser requirement, nothing to do", getLogPrefix());
return false;
}
- return super.doPreExecute(profileRequestContext, authenticationContext);
+ return true;
}
/** {@inheritDoc} */
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByPassivity.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByPassivity.java
deleted file mode 100644
index d777b0a..0000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FilterFlowsByPassivity.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You 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.impl;
-
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.authn.AbstractAuthenticationAction;
-import net.shibboleth.idp.authn.AuthenticationFlowDescriptor;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * An authentication action that filters out potential authentication flows if the request requires
- * passive behavior and the flows don't support passive authentication.
- *
- * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
- * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class) != null</pre>
- * @post AuthenticationContext.getPotentialFlows() is modified as above.
- */
-public class FilterFlowsByPassivity extends AbstractAuthenticationAction {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(FilterFlowsByPassivity.class);
-
- /** {@inheritDoc} */
- @Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
-
- if (!authenticationContext.isPassive()) {
- log.debug("{} Request does not have passive requirement, nothing to do", getLogPrefix());
- return false;
- }
-
- return super.doPreExecute(profileRequestContext, authenticationContext);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
-
- final Map<String, AuthenticationFlowDescriptor> potentialFlows = authenticationContext.getPotentialFlows();
-
- final Iterator<Entry<String, AuthenticationFlowDescriptor>> descriptorItr =
- potentialFlows.entrySet().iterator();
- while (descriptorItr.hasNext()) {
- final AuthenticationFlowDescriptor descriptor = descriptorItr.next().getValue();
- if (descriptor.isPassiveAuthenticationSupported()) {
- log.debug("{} Retaining flow {}, it supports passive authentication", getLogPrefix(),
- descriptor.getId());
- } else {
- log.debug("{} Removing flow {}, it does not support passive authentication", getLogPrefix(),
- descriptor.getId());
- descriptorItr.remove();
- }
- }
-
- if (potentialFlows.size() == 0) {
- log.info("{} No potential authentication flows remain after filtering", getLogPrefix());
- } else {
- 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/FinalizeAuthentication.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FinalizeAuthentication.java
index 93bea52..7f96bb0 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FinalizeAuthentication.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FinalizeAuthentication.java
@@ -245,6 +245,7 @@ public class FinalizeAuthentication extends AbstractAuthenticationAction {
for (final Principal candidate
: authenticationContext.getAuthenticationResult().getSupportedPrincipals(p.getClass())) {
if (predicate.test(new PrincipalSupportingComponent() {
+ @SuppressWarnings("unchecked")
public <T extends Principal> Set<T> getSupportedPrincipals(final Class<T> c) {
return Collections.<T>singleton((T) candidate);
}
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 db0a41b..c530a1a 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
@@ -86,7 +86,10 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
@Nullable private RequestedPrincipalContext requestedPrincipalCtx;
/** A subordinate PreferredPrincipalContext, if any. */
- @Nullable private PreferredPrincipalContext preferredPrincipalCtx;
+ @Nullable private PreferredPrincipalContext preferredPrincipalCtx;
+
+ /** Tracks a proxy count of zero for the request. */
+ private boolean noProxying;
/**
* Get whether SSO should trump explicit relying party requirements preference.
@@ -117,6 +120,8 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
return false;
}
+ noProxying = authenticationContext.getProxyCount() != null && authenticationContext.getProxyCount() == 0;
+
requestedPrincipalCtx = authenticationContext.getSubcontext(RequestedPrincipalContext.class);
if (requestedPrincipalCtx != null) {
if (requestedPrincipalCtx.getOperator() == null
@@ -184,8 +189,15 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
log.debug("{} Attempting to honor signaled flow {}", getLogPrefix(), flow.getId());
- // If not forced, check for an active result for that flow.
+ if (noProxying && flow.isProxyScopingEnforced()) {
+ log.error("{} Signaled flow {} disallowed due to proxy count of zero", getLogPrefix(), flow.getId());
+ ActionSupport.buildEvent(profileRequestContext,
+ authenticationContext.isPassive() ? AuthnEventIds.NO_PASSIVE : AuthnEventIds.PROXY_COUNT_EXCEEDED);
+ return;
+ }
+ // If not forced, check for an active result for that flow.
+
AuthenticationResult activeResult = null;
if (!authenticationContext.isForceAuthn()) {
activeResult = authenticationContext.getActiveResults().get(flow.getId());
@@ -268,7 +280,8 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
if (flow == null) {
log.info("{} No potential flows left to choose from, authentication failed", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext,
- authenticationContext.isPassive() ? AuthnEventIds.NO_PASSIVE : AuthnEventIds.NO_POTENTIAL_FLOW);
+ authenticationContext.isPassive() ? AuthnEventIds.NO_PASSIVE :
+ (noProxying ? AuthnEventIds.PROXY_COUNT_EXCEEDED : AuthnEventIds.NO_POTENTIAL_FLOW));
return;
}
selectInactiveFlow(profileRequestContext, authenticationContext, flow);
@@ -302,7 +315,8 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
if (flow == null) {
log.info("{} No potential flows left to choose from, authentication failed", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext,
- authenticationContext.isPassive() ? AuthnEventIds.NO_PASSIVE : AuthnEventIds.NO_POTENTIAL_FLOW);
+ authenticationContext.isPassive() ? AuthnEventIds.NO_PASSIVE :
+ (noProxying ? AuthnEventIds.PROXY_COUNT_EXCEEDED : AuthnEventIds.NO_POTENTIAL_FLOW));
return;
}
selectInactiveFlow(profileRequestContext, authenticationContext, flow);
@@ -326,10 +340,12 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
for (final AuthenticationFlowDescriptor flow : authenticationContext.getPotentialFlows().values()) {
if (!authenticationContext.getIntermediateFlows().containsKey(flow.getId())) {
if (!authenticationContext.isPassive() || flow.isPassiveAuthenticationSupported()) {
- if (flow.test(profileRequestContext)) {
- selectedFlow = flow;
- if (preferredPrincipalCtx == null || preferredPrincipalCtx.isAcceptable(flow)) {
- break;
+ if (!noProxying || !flow.isProxyScopingEnforced()) {
+ if (flow.test(profileRequestContext)) {
+ selectedFlow = flow;
+ if (preferredPrincipalCtx == null || preferredPrincipalCtx.isAcceptable(flow)) {
+ break;
+ }
}
}
}
@@ -397,6 +413,7 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
}
}
+// Checkstyle: CyclomaticComplexity OFF
/**
* Selects an inactive flow in the presence of specific requested Principals, and completes processing.
*
@@ -411,7 +428,7 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
// Check each flow for compatibility with request. Don't check for an active result also.
// Also omit anything in the intermediates collection already.
for (final Principal p : requestedPrincipalCtx.getRequestedPrincipals()) {
- log.debug("{} Checking for an inactive flow compatible with operator '{}' and principal '{}'",
+ log.debug("{} Checking for inactive flow compatible with operator '{}' and principal '{}'",
getLogPrefix(), requestedPrincipalCtx.getOperator(), p.getName());
final PrincipalEvalPredicate predicate = requestedPrincipalCtx.getPredicate(p);
if (predicate != null) {
@@ -419,8 +436,12 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
if (!authenticationContext.getIntermediateFlows().containsKey(descriptor.getId())
&& predicate.test(descriptor) && descriptor.test(profileRequestContext)) {
if (!authenticationContext.isPassive() || descriptor.isPassiveAuthenticationSupported()) {
- selectInactiveFlow(profileRequestContext, authenticationContext, descriptor);
- return;
+ if (!noProxying || !descriptor.isProxyScopingEnforced()) {
+ selectInactiveFlow(profileRequestContext, authenticationContext, descriptor);
+ return;
+ }
+ log.debug("{} Flow '{}' disallowed by effective proxy count of zero", getLogPrefix(),
+ descriptor.getId());
}
}
}
@@ -431,12 +452,15 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
}
}
-
log.info("{} None of the potential authentication flows can satisfy the request", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext,
- authenticationContext.isPassive() ? AuthnEventIds.NO_PASSIVE : AuthnEventIds.REQUEST_UNSUPPORTED);
+ authenticationContext.isPassive() ? AuthnEventIds.NO_PASSIVE :
+ (noProxying ? AuthnEventIds.PROXY_COUNT_EXCEEDED : AuthnEventIds.REQUEST_UNSUPPORTED));
}
+// Checkstyle: CyclomaticComplexity ON
+
+// Checkstyle: MethodLength|CyclomaticComplexity|ReturnCount OFF
/**
* Selects a flow or an active result in the presence of specific requested Principals and completes processing.
*
@@ -444,7 +468,6 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
* @param authenticationContext the current authentication context
* @param activeResults active results that may be reused
*/
-// Checkstyle: MethodLength|CyclomaticComplexity|ReturnCount OFF
private void selectRequestedFlow(@Nonnull final ProfileRequestContext profileRequestContext,
@Nonnull final AuthenticationContext authenticationContext,
@Nonnull @NonnullElements final Map<String,AuthenticationResult> activeResults) {
@@ -504,8 +527,12 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
result.getAuthenticationFlowId());
}
if (!authenticationContext.isPassive() || descriptor.isPassiveAuthenticationSupported()) {
- selectInactiveFlow(profileRequestContext, authenticationContext, descriptor);
- return;
+ if (!noProxying || !descriptor.isProxyScopingEnforced()) {
+ selectInactiveFlow(profileRequestContext, authenticationContext, descriptor);
+ return;
+ }
+ log.debug("{} Flow '{}' disallowed by effective proxy count of zero", getLogPrefix(),
+ descriptor.getId());
}
} else {
selectActiveResult(profileRequestContext, authenticationContext, result);
@@ -522,7 +549,8 @@ public class SelectAuthenticationFlow extends AbstractAuthenticationAction {
log.info("{} None of the potential authentication flows can satisfy the request", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext,
- authenticationContext.isPassive() ? AuthnEventIds.NO_PASSIVE : AuthnEventIds.REQUEST_UNSUPPORTED);
+ authenticationContext.isPassive() ? AuthnEventIds.NO_PASSIVE :
+ (noProxying ? AuthnEventIds.PROXY_COUNT_EXCEEDED : AuthnEventIds.REQUEST_UNSUPPORTED));
}
// Checkstyle: MethodLength|CyclomaticComplexity|ReturnCount ON
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 53b2b6d..d09247f 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
@@ -302,6 +302,12 @@ public class TransitionMultiFactorAuthentication extends AbstractAuthenticationA
ActionSupport.buildEvent(profileRequestContext, authenticationContext.isPassive() ?
AuthnEventIds.NO_PASSIVE : AuthnEventIds.REQUEST_UNSUPPORTED);
return;
+ } else if (flow.isProxyScopingEnforced() &&
+ authenticationContext.getProxyCount() != null && authenticationContext.getProxyCount() == 0) {
+ log.error("{} Targeted login flow '{}' cannot be used with an effective ProxyCount of zero",
+ getLogPrefix(), flowId);
+ ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.PROXY_COUNT_EXCEEDED);
+ return;
}
}
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/X509AuthServlet.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/X509AuthServlet.java
index 2143f67..26dab75 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/X509AuthServlet.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/X509AuthServlet.java
@@ -77,6 +77,7 @@ public class X509AuthServlet extends HttpServlet {
}
/** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
@Override
public void init(final ServletConfig config) throws ServletException {
super.init(config);
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FilterFlowsByAttributeTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FilterFlowsByAttributeTest.java
deleted file mode 100644
index 523f4c0..0000000
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FilterFlowsByAttributeTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You 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.impl;
-
-import java.util.Arrays;
-import java.util.Collections;
-
-import javax.security.auth.Subject;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.attribute.context.AttributeContext;
-import net.shibboleth.idp.authn.AuthenticationResult;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.authn.principal.TestPrincipal;
-import net.shibboleth.idp.profile.ActionTestingSupport;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-import org.springframework.webflow.execution.Event;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-/** {@link FilterFlowsByAttribute} unit test. */
-public class FilterFlowsByAttributeTest extends BaseAuthenticationContextTest {
-
- private FilterFlowsByAttribute action;
-
- @BeforeMethod public void setUp() throws Exception {
- super.setUp();
-
- action = new FilterFlowsByAttribute();
- action.setAttributeId("foo");
-
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- final AuthenticationResult active = new AuthenticationResult("test3", new Subject());
- active.getSubject().getPrincipals().add(new TestPrincipal("test3"));
- authCtx.setActiveResults(Arrays.asList(active));
- }
-
- @Test public void testNoAttributeID() throws ComponentInitializationException {
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
-
- action = new FilterFlowsByAttribute();
- action.initialize();
-
- final Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
- Assert.assertEquals(authCtx.getPotentialFlows().size(), 3);
- Assert.assertEquals(authCtx.getActiveResults().size(), 1);
- }
-
- @Test public void testNoAttribute() throws ComponentInitializationException {
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
-
- action.initialize();
- Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
- Assert.assertEquals(authCtx.getPotentialFlows().size(), 3);
- Assert.assertEquals(authCtx.getActiveResults().size(), 1);
-
- authCtx.getSubcontext(AttributeContext.class, true).setIdPAttributes(
- Collections.singletonList(new IdPAttribute("foo")));
- event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
- Assert.assertEquals(authCtx.getPotentialFlows().size(), 3);
- Assert.assertEquals(authCtx.getActiveResults().size(), 1);
- }
-
- @Test public void testNoMatch() throws ComponentInitializationException {
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- final IdPAttribute attr = new IdPAttribute("foo");
- authCtx.getSubcontext(AttributeContext.class, true).setIdPAttributes(Collections.singletonList(attr));
- attr.setValues(Collections.singletonList(new StringAttributeValue("bar")));
-
- action.setFilterActiveResults(false);
- action.initialize();
- final Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
- Assert.assertEquals(authCtx.getPotentialFlows().size(), 0);
- Assert.assertEquals(authCtx.getActiveResults().size(), 1);
- }
-
- @Test public void testMatch() throws ComponentInitializationException {
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- final IdPAttribute attr = new IdPAttribute("foo");
- authCtx.getSubcontext(AttributeContext.class, true).setIdPAttributes(Collections.singletonList(attr));
- attr.setValues(Collections.singletonList(new StringAttributeValue("bar")));
-
- authCtx.getPotentialFlows().get("test1").getSupportedPrincipals().add(new TestPrincipal("baz"));
- authCtx.getPotentialFlows().get("test2").getSupportedPrincipals().add(new TestPrincipal("bar"));
- authCtx.getPotentialFlows().get("test3").getSupportedPrincipals().add(new TestPrincipal("bay"));
-
- action.setFilterActiveResults(false);
- action.initialize();
- final Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
- Assert.assertEquals(authCtx.getPotentialFlows().size(), 1);
- Assert.assertEquals(authCtx.getPotentialFlows().entrySet().iterator().next().getValue().getId(), "test2");
- Assert.assertEquals(authCtx.getActiveResults().size(), 1);
- }
-
- @Test public void testMatchActive() throws ComponentInitializationException {
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- final IdPAttribute attr = new IdPAttribute("foo");
- authCtx.getSubcontext(AttributeContext.class, true).setIdPAttributes(Collections.singletonList(attr));
- attr.setValues(Collections.singletonList(new StringAttributeValue("test3")));
-
- action.initialize();
- final Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
- Assert.assertEquals(authCtx.getPotentialFlows().size(), 0);
- Assert.assertEquals(authCtx.getActiveResults().size(), 1);
- }
-
- @Test public void testNoMatchActive() throws ComponentInitializationException {
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- final IdPAttribute attr = new IdPAttribute("foo");
- authCtx.getSubcontext(AttributeContext.class, true).setIdPAttributes(Collections.singletonList(attr));
- attr.setValues(Collections.singletonList(new StringAttributeValue("test2")));
-
- action.initialize();
- final Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
- Assert.assertEquals(authCtx.getPotentialFlows().size(), 0);
- Assert.assertEquals(authCtx.getActiveResults().size(), 0);
- }
-
-}
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FilterFlowsByPassivityTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FilterFlowsByPassivityTest.java
deleted file mode 100644
index 19abfae..0000000
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FilterFlowsByPassivityTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You 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.impl;
-
-import net.shibboleth.idp.authn.AuthenticationFlowDescriptor;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.profile.ActionTestingSupport;
-
-import org.springframework.webflow.execution.Event;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-/** {@link FilterFlowsByPassivity} unit test. */
-public class FilterFlowsByPassivityTest extends BaseAuthenticationContextTest {
-
- private FilterFlowsByPassivity action;
-
- @BeforeMethod public void setUp() throws Exception {
- super.setUp();
-
- action = new FilterFlowsByPassivity();
- action.initialize();
- }
-
- @Test public void testNonPassive() {
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- authCtx.setIsPassive(false);
-
- final Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
- Assert.assertEquals(authCtx.getPotentialFlows().size(), 3);
- }
-
- @Test public void testNoFiltering() {
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- authCtx.setIsPassive(true);
- for (final AuthenticationFlowDescriptor fd : authCtx.getPotentialFlows().values()) {
- fd.setPassiveAuthenticationSupported(true);
- }
-
- final Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
- Assert.assertEquals(authCtx.getPotentialFlows().size(), 3);
- }
-
- @Test public void testPartialFiltering() {
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- authCtx.setIsPassive(true);
- authCtx.getPotentialFlows().get("test2").setPassiveAuthenticationSupported(true);
-
- final Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
- Assert.assertEquals(authCtx.getPotentialFlows().size(), 1);
- Assert.assertNull(authCtx.getPotentialFlows().get("test1"));
- Assert.assertNotNull(authCtx.getPotentialFlows().get("test2"));
- }
-}
\ No newline at end of file
diff --git a/idp-conf/src/main/resources/conf/authn/general-authn.xml b/idp-conf/src/main/resources/conf/authn/general-authn.xml
index 906db6c..f3b660f 100644
--- a/idp-conf/src/main/resources/conf/authn/general-authn.xml
+++ b/idp-conf/src/main/resources/conf/authn/general-authn.xml
@@ -51,14 +51,6 @@
</property>
</bean>
- <bean id="authn/External" parent="shibboleth.AuthenticationFlow"
- p:nonBrowserSupported="false" />
-
- <bean id="authn/RemoteUser" parent="shibboleth.AuthenticationFlow"
- p:nonBrowserSupported="false" />
-
- <bean id="authn/RemoteUserInternal" parent="shibboleth.AuthenticationFlow" />
-
<bean id="authn/Function" parent="shibboleth.AuthenticationFlow" />
<bean id="authn/X509" parent="shibboleth.AuthenticationFlow"
@@ -137,7 +129,21 @@
<bean id="authn/Proxy" parent="shibboleth.AuthenticationFlow"
p:nonBrowserSupported="false"
p:passiveAuthenticationSupported="true"
- p:forcedAuthenticationSupported="true" />
+ p:forcedAuthenticationSupported="true"
+ p:proxyScopingEnforced="true" />
+
+ <!--
+ These flows are often, though not exclusively, used to proxy authentication, so may need
+ the proxyScopingEnforced property enabled by hand to honor RP/local proxy count limits.
+ -->
+
+ <bean id="authn/External" parent="shibboleth.AuthenticationFlow"
+ p:nonBrowserSupported="false" />
+
+ <bean id="authn/RemoteUser" parent="shibboleth.AuthenticationFlow"
+ p:nonBrowserSupported="false" />
+
+ <bean id="authn/RemoteUserInternal" parent="shibboleth.AuthenticationFlow" />
</util:list>
diff --git a/idp-conf/src/main/resources/conf/errors.xml b/idp-conf/src/main/resources/conf/errors.xml
index a2e8ca4..a5a8790 100644
--- a/idp-conf/src/main/resources/conf/errors.xml
+++ b/idp-conf/src/main/resources/conf/errors.xml
@@ -90,6 +90,7 @@
<entry key="SessionNotFound" value-ref="shibboleth.SAML2Status.UnknownPrincipal" />
<entry key="InvalidNameIDPolicy" value-ref="shibboleth.SAML2Status.InvalidNameIDPolicy" />
+ <entry key="ProxyCountExceeded" value-ref="shibboleth.SAML2Status.ProxyCountExceeded" />
<entry key="ChannelBindingsError" value-ref="shibboleth.SAML2Status.ChannelBindingsError" />
</util:map>
diff --git a/idp-conf/src/main/resources/system/conf/utilities.xml b/idp-conf/src/main/resources/system/conf/utilities.xml
index ba7afe0..532e842 100644
--- a/idp-conf/src/main/resources/system/conf/utilities.xml
+++ b/idp-conf/src/main/resources/system/conf/utilities.xml
@@ -331,6 +331,11 @@
<util:constant static-field="org.opensaml.saml.saml2.core.StatusCode.INVALID_NAMEID_POLICY" />
</util:list>
+ <util:list id="shibboleth.SAML2Status.ProxyCountExceeded">
+ <util:constant static-field="org.opensaml.saml.saml2.core.StatusCode.RESPONDER" />
+ <util:constant static-field="org.opensaml.saml.saml2.core.StatusCode.PROXY_COUNT_EXCEEDED" />
+ </util:list>
+
<util:list id="shibboleth.SAML2Status.ChannelBindingsError">
<util:constant static-field="org.opensaml.saml.saml2.core.StatusCode.REQUESTER" />
<util:constant static-field="org.opensaml.saml.common.xml.SAMLConstants.SAML20CB_NS" />
diff --git a/idp-conf/src/main/resources/system/flows/authn/authn-abstract-flow.xml b/idp-conf/src/main/resources/system/flows/authn/authn-abstract-flow.xml
index ba54cbf..1531b04 100644
--- a/idp-conf/src/main/resources/system/flows/authn/authn-abstract-flow.xml
+++ b/idp-conf/src/main/resources/system/flows/authn/authn-abstract-flow.xml
@@ -31,6 +31,7 @@
<end-state id="NoCredentials" />
<end-state id="NoPassive" />
<end-state id="NoPotentialFlow" />
+ <end-state id="ProxyCountExceeded" />
<end-state id="RequestUnsupported" />
<end-state id="ReselectFlow" />
<end-state id="RestartAuthentication" />
@@ -65,6 +66,7 @@
<transition on="NoCredentials" to="NoCredentials" />
<transition on="NoPassive" to="NoPassive" />
<transition on="NoPotentialFlow" to="NoPotentialFlow" />
+ <transition on="ProxyCountExceeded" to="ProxyCountExceeded" />
<transition on="RequestUnsupported" to="RequestUnsupported" />
<transition on="ReselectFlow" to="ReselectFlow" />
<transition on="RestartAuthentication" to="RestartAuthentication" />
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list