[java-identity-provider] 01/02: IDP-1502 - Avoid object creation in the external web flows
Scott Cantor
cantor.2 at osu.edu
Thu Sep 26 12:19:23 EDT 2019
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch maint-3.4
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=f4a536b91d0982da4a035035ef327efcaeb5e444
commit f4a536b91d0982da4a035035ef327efcaeb5e444
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Sep 25 15:12:39 2019 -0400
IDP-1502 - Avoid object creation in the external web flows
https://issues.shibboleth.net/jira/browse/IDP-1502
Adapt external authentication flow design.
---
.../idp/authn/ExternalAuthentication.java | 131 ++++--
.../context/ExternalAuthenticationContext.java | 27 +-
.../idp/authn/impl/ExternalAuthenticationImpl.java | 60 +--
.../impl/ValidateExternalAuthenticationTest.java | 28 +-
.../spnego/impl/SPNEGOAuthnControllerTest.java | 438 ++++++++++-----------
.../system/flows/authn/external-authn-flow.xml | 3 +-
.../system/flows/authn/remoteuser-authn-flow.xml | 4 +-
.../system/flows/authn/spnego-authn-flow.xml | 3 +-
.../system/flows/authn/x509-authn-flow.xml | 3 +-
.../profile/interceptor/ExternalInterceptor.java | 227 +++++++++++
.../idp/profile/RequestContextBuilder.java | 1 +
11 files changed, 593 insertions(+), 332 deletions(-)
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/ExternalAuthentication.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/ExternalAuthentication.java
index 586911f..f955276 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/ExternalAuthentication.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/ExternalAuthentication.java
@@ -24,16 +24,28 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.context.ExternalContextHolder;
+import org.springframework.webflow.context.servlet.ServletExternalContext;
+import org.springframework.webflow.execution.FlowExecution;
+import org.springframework.webflow.execution.repository.FlowExecutionRepository;
+import org.springframework.webflow.execution.repository.FlowExecutionRepositoryException;
+import org.springframework.webflow.executor.FlowExecutorImpl;
+import com.google.common.base.Strings;
import com.google.common.net.UrlEscapers;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.ExternalAuthenticationContext;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.logic.Constraint;
/** Public interface supporting external authentication outside the webflow engine. */
-public class ExternalAuthentication {
+public abstract class ExternalAuthentication {
- /** Parameter supplied to identify the per-conversation structure in the session. */
+ /** Parameter supplied to locate the SWF object needed in the servlet context. */
+ @Nonnull @NotEmpty public static final String SWF_KEY = "net.shibboleth.idp.flowExecutor";
+
+ /** Parameter supplied to identify the per-conversation parameter. */
@Nonnull @NotEmpty public static final String CONVERSATION_KEY = "conversation";
/** Request attribute to which user's principal should be bound. */
@@ -139,18 +151,16 @@ public class ExternalAuthentication {
*/
@Nonnull @NotEmpty public static String startExternalAuthentication(@Nonnull final HttpServletRequest request)
throws ExternalAuthenticationException {
- final String conv = request.getParameter(CONVERSATION_KEY);
- if (conv == null || conv.isEmpty()) {
+ final String key = request.getParameter(CONVERSATION_KEY);
+ if (Strings.isNullOrEmpty(key)) {
throw new ExternalAuthenticationException("No conversation key found in request");
}
+
+ final ProfileRequestContext profileRequestContext = getProfileRequestContext(key, request);
+ final ExternalAuthenticationContext extContext = getExternalAuthenticationContext(profileRequestContext);
+ extContext.getExternalAuthentication().doStart(request, profileRequestContext, extContext);
- final Object obj = request.getSession().getAttribute(CONVERSATION_KEY + conv);
- if (obj == null || !(obj instanceof ExternalAuthentication)) {
- throw new ExternalAuthenticationException("No conversation state found in session for key (" + conv + ")");
- }
-
- ((ExternalAuthentication) obj).doStart(request);
- return conv;
+ return key;
}
/**
@@ -169,14 +179,9 @@ public class ExternalAuthentication {
@Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response)
throws ExternalAuthenticationException, IOException {
- final Object obj = request.getSession().getAttribute(CONVERSATION_KEY + key);
- if (obj == null || !(obj instanceof ExternalAuthentication)) {
- throw new ExternalAuthenticationException("No conversation state found in session for key (" + key + ")");
- }
-
- request.getSession().removeAttribute(CONVERSATION_KEY + key);
-
- ((ExternalAuthentication) obj).doFinish(request, response);
+ final ProfileRequestContext profileRequestContext = getProfileRequestContext(key, request);
+ final ExternalAuthenticationContext extContext = getExternalAuthenticationContext(profileRequestContext);
+ extContext.getExternalAuthentication().doFinish(request, response, profileRequestContext, extContext);
}
/**
@@ -191,12 +196,59 @@ public class ExternalAuthentication {
@Nonnull public static ProfileRequestContext getProfileRequestContext(@Nonnull @NotEmpty final String key,
@Nonnull final HttpServletRequest request) throws ExternalAuthenticationException {
- final Object obj = request.getSession().getAttribute(CONVERSATION_KEY + key);
- if (obj == null || !(obj instanceof ExternalAuthentication)) {
- throw new ExternalAuthenticationException("No conversation state found in session");
+ final Object obj = request.getServletContext().getAttribute(SWF_KEY);
+ if (!(obj instanceof FlowExecutorImpl)) {
+ // This is a testing hook for injecting the PRC directly.
+ if (obj instanceof ProfileRequestContext) {
+ return (ProfileRequestContext) obj;
+ }
+ throw new ExternalAuthenticationException("No FlowExecutor available in servlet context");
+ }
+
+ try {
+ final FlowExecutionRepository repo = ((FlowExecutorImpl) obj).getExecutionRepository();
+ ExternalContextHolder.setExternalContext(
+ new ServletExternalContext(request.getServletContext(), request, null));
+
+ final FlowExecution execution = repo.getFlowExecution(repo.parseFlowExecutionKey(key));
+ final Object prc = execution.getConversationScope().get(ProfileRequestContext.BINDING_KEY);
+ if (!(prc instanceof ProfileRequestContext)) {
+ throw new ExternalAuthenticationException(
+ "ProfileRequestContext not available in webflow conversation scope");
+ }
+
+ return (ProfileRequestContext) prc;
+ } catch (final FlowExecutionRepositoryException e) {
+ throw new ExternalAuthenticationException("Error retrieving flow conversation", e);
+ } finally {
+ ExternalContextHolder.setExternalContext(null);
+ }
+ }
+
+ /**
+ * Utility method to access the {@link ExternalAuthenticationContext}.
+ *
+ * @param profileRequestContext profile request context
+ *
+ * @return the {@link ExternalAuthenticationContext} to operate on
+ *
+ * @throws ExternalAuthenticationException if the context is missing
+ */
+ @Nonnull private static ExternalAuthenticationContext getExternalAuthenticationContext(
+ @Nonnull final ProfileRequestContext profileRequestContext) throws ExternalAuthenticationException {
+
+ final AuthenticationContext authContext = profileRequestContext.getSubcontext(AuthenticationContext.class);
+ if (authContext == null) {
+ throw new ExternalAuthenticationException("No AuthenticationContext found");
}
- return ((ExternalAuthentication) obj).getProfileRequestContext(request);
+
+ final ExternalAuthenticationContext extContext = authContext.getSubcontext(ExternalAuthenticationContext.class);
+ if (extContext == null) {
+ throw new ExternalAuthenticationException("No ExternalInterceptorContext found");
+ }
+
+ return extContext;
}
/**
@@ -204,11 +256,17 @@ public class ExternalAuthentication {
* the servlet session and exposing it as request attributes.
*
* @param request servlet request
+ * @param profileRequestContext current profile request context
+ * @param externalAuthenticationContext external authentication context
*
* @throws ExternalAuthenticationException if an error occurs
*/
- protected void doStart(@Nonnull final HttpServletRequest request) throws ExternalAuthenticationException {
- throw new ExternalAuthenticationException("Not implemented");
+ protected void doStart(@Nonnull final HttpServletRequest request,
+ @Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final ExternalAuthenticationContext externalAuthenticationContext)
+ throws ExternalAuthenticationException {
+
+ request.setAttribute(ProfileRequestContext.BINDING_KEY, profileRequestContext);
}
/**
@@ -218,26 +276,15 @@ public class ExternalAuthentication {
*
* @param request servlet request
* @param response servlet response
+ * @param profileRequestContext current profile request context
+ * @param externalAuthenticationContext external authentication context
*
* @throws ExternalAuthenticationException if an error occurs
* @throws IOException if the redirect cannot be issued
*/
- protected void doFinish(@Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response)
- throws ExternalAuthenticationException, IOException {
- throw new ExternalAuthenticationException("Not implemented");
- }
-
- /**
- * Get the {@link ProfileRequestContext} associated with a request.
- *
- * @param request servlet request
- *
- * @return the profile request context
- * @throws ExternalAuthenticationException if an error occurs
- */
- @Nonnull protected ProfileRequestContext getProfileRequestContext(@Nonnull final HttpServletRequest request)
- throws ExternalAuthenticationException {
- throw new ExternalAuthenticationException("Not implemented");
- }
+ protected abstract void doFinish(@Nonnull final HttpServletRequest request,
+ @Nonnull final HttpServletResponse response, @Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final ExternalAuthenticationContext externalAuthenticationContext)
+ throws ExternalAuthenticationException, IOException;
}
\ No newline at end of file
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/ExternalAuthenticationContext.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/ExternalAuthenticationContext.java
index 5eb5f06..19aa756 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/ExternalAuthenticationContext.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/ExternalAuthenticationContext.java
@@ -28,7 +28,9 @@ import javax.security.auth.Subject;
import org.joda.time.DateTime;
import org.opensaml.messaging.context.BaseContext;
+import net.shibboleth.idp.authn.ExternalAuthentication;
import net.shibboleth.utilities.java.support.annotation.constraint.Live;
+import net.shibboleth.utilities.java.support.logic.Constraint;
/**
* A context representing the state of an externalized authentication attempt,
@@ -39,6 +41,9 @@ import net.shibboleth.utilities.java.support.annotation.constraint.Live;
*/
public final class ExternalAuthenticationContext extends BaseContext {
+ /** Implementation object. */
+ @Nonnull private final ExternalAuthentication externalAuthentication;
+
/** Value of flowExecutionUrl on branching from flow. */
@Nullable private String flowExecutionUrl;
@@ -69,12 +74,30 @@ public final class ExternalAuthenticationContext extends BaseContext {
/** Flag indicating this "new" result is really "old". */
private boolean previousResult;
- /** Constructor. */
- public ExternalAuthenticationContext() {
+ /**
+ * Constructor.
+ *
+ * @param authentication implementation object
+ *
+ * @since 3.4.6
+ */
+ public ExternalAuthenticationContext(@Nonnull final ExternalAuthentication authentication) {
+ externalAuthentication = Constraint.isNotNull(authentication, "ExternalAuthentication cannot be null");
authenticatingAuthorities = new ArrayList<>();
}
/**
+ * Get the {@link ExternalAuthentication} object installed in the context.
+ *
+ * @return the external authentication implementation
+ *
+ * @since 3.4.6
+ */
+ @Nonnull public ExternalAuthentication getExternalAuthentication() {
+ return externalAuthentication;
+ }
+
+ /**
* Get the flow execution URL to return control to.
*
* @return return location
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 d942a58..4930857 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
@@ -29,8 +29,6 @@ import javax.servlet.http.HttpServletResponse;
import org.joda.time.DateTime;
import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
@@ -47,38 +45,24 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
* of request attributes.
*/
public class ExternalAuthenticationImpl extends ExternalAuthentication {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(ExternalAuthenticationImpl.class);
/** Lookup function for relying party context. */
@Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
- /** State of request to pull from. */
- @Nonnull private final ProfileRequestContext profileRequestContext;
-
/** Track whether we were invoked from within another login flow. */
private final boolean extendedFlow;
- /**
- * Constructor.
- *
- * @param input profile request context to expose
- */
- public ExternalAuthenticationImpl(@Nonnull final ProfileRequestContext input) {
- profileRequestContext = Constraint.isNotNull(input, "ProfileRequestContext cannot be null");
- extendedFlow = false;
- relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+ /** Constructor. */
+ public ExternalAuthenticationImpl() {
+ this(false);
}
/**
* Constructor.
*
- * @param input profile request context to expose
* @param extended called as extended flow from another login flow
*/
- public ExternalAuthenticationImpl(@Nonnull final ProfileRequestContext input, final boolean extended) {
- profileRequestContext = Constraint.isNotNull(input, "ProfileRequestContext cannot be null");
+ public ExternalAuthenticationImpl(final boolean extended) {
extendedFlow = extended;
relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
}
@@ -97,7 +81,12 @@ public class ExternalAuthenticationImpl extends ExternalAuthentication {
/** {@inheritDoc} */
@SuppressWarnings("deprecation")
@Override
- protected void doStart(@Nonnull final HttpServletRequest request) throws ExternalAuthenticationException {
+ protected void doStart(@Nonnull final HttpServletRequest request,
+ @Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final ExternalAuthenticationContext externalAuthenticationContext)
+ throws ExternalAuthenticationException {
+ super.doStart(request, profileRequestContext, externalAuthenticationContext);
+
final AuthenticationContext authnContext = profileRequestContext.getSubcontext(AuthenticationContext.class);
if (authnContext == null) {
throw new ExternalAuthenticationException("No AuthenticationContext found");
@@ -105,7 +94,6 @@ public class ExternalAuthenticationImpl extends ExternalAuthentication {
throw new ExternalAuthenticationException("No attempted authentication flow set");
}
- request.setAttribute(ProfileRequestContext.BINDING_KEY, profileRequestContext);
request.setAttribute(EXTENDED_FLOW_PARAM, extendedFlow);
request.setAttribute(PASSIVE_AUTHN_PARAM, authnContext.isPassive());
@@ -125,18 +113,12 @@ public class ExternalAuthenticationImpl extends ExternalAuthentication {
// Checkstyle: CyclomaticComplexity OFF
/** {@inheritDoc} */
@Override
- protected void doFinish(@Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response)
- throws ExternalAuthenticationException, IOException {
- final AuthenticationContext authnContext = profileRequestContext.getSubcontext(AuthenticationContext.class);
- if (authnContext == null) {
- throw new ExternalAuthenticationException("No AuthenticationContext found");
- }
+ protected void doFinish(@Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response,
+ @Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final ExternalAuthenticationContext extContext)
+ throws ExternalAuthenticationException, IOException {
- final ExternalAuthenticationContext extContext =
- authnContext.getSubcontext(ExternalAuthenticationContext.class);
- if (extContext == null) {
- throw new ExternalAuthenticationException("No ExternalAuthenticationContext found");
- } else if (extContext.getFlowExecutionUrl() == null) {
+ if (extContext.getFlowExecutionUrl() == null) {
throw new ExternalAuthenticationException("No flow execution URL found to return control");
}
@@ -188,20 +170,12 @@ public class ExternalAuthenticationImpl extends ExternalAuthentication {
attr = request.getAttribute(REVOKECONSENT_KEY);
if (attr != null && attr instanceof Boolean && ((Boolean) attr).booleanValue()) {
final ConsentManagementContext consentCtx =
- getProfileRequestContext(request).getSubcontext(ConsentManagementContext.class, true);
+ profileRequestContext.getSubcontext(ConsentManagementContext.class, true);
consentCtx.setRevokeConsent(true);
}
response.sendRedirect(extContext.getFlowExecutionUrl());
}
-// Checkstyle: CyclomaticComplexity OFF
+// Checkstyle: CyclomaticComplexity ON
- /** {@inheritDoc} */
- @Override
- protected ProfileRequestContext getProfileRequestContext(@Nonnull final HttpServletRequest request)
- throws ExternalAuthenticationException {
- return profileRequestContext;
- }
-
-
}
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateExternalAuthenticationTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateExternalAuthenticationTest.java
index a73aba0..3eb9c17 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateExternalAuthenticationTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateExternalAuthenticationTest.java
@@ -25,6 +25,7 @@ import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.ExternalAuthentication;
import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.authn.context.AuthenticationErrorContext;
import net.shibboleth.idp.authn.context.ExternalAuthenticationContext;
@@ -43,12 +44,16 @@ import org.testng.annotations.Test;
/** {@link ValidateExternalAuthentication} unit test. */
public class ValidateExternalAuthenticationTest extends BaseAuthenticationContextTest {
+ private ExternalAuthentication ext;
+
private ValidateExternalAuthentication action;
@BeforeMethod public void setUp() throws Exception {
super.setUp();
prc.getSubcontext(AuthenticationContext.class).setAttemptedFlow(authenticationFlows.get(0));
+
+ ext = new ExternalAuthenticationImpl();
action = new ValidateExternalAuthentication();
action.setHttpServletRequest((HttpServletRequest) src.getExternalContext().getNativeRequest());
@@ -69,7 +74,7 @@ public class ValidateExternalAuthenticationTest extends BaseAuthenticationContex
@Test public void testNoCredentials() {
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
- ac.getSubcontext(ExternalAuthenticationContext.class, true);
+ ac.addSubcontext(new ExternalAuthenticationContext(ext), true);
final Event event = action.execute(src);
ActionTestingSupport.assertEvent(event, AuthnEventIds.NO_CREDENTIALS);
@@ -77,7 +82,8 @@ public class ValidateExternalAuthenticationTest extends BaseAuthenticationContex
@Test public void testPrincipalName() {
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
- final ExternalAuthenticationContext eac = ac.getSubcontext(ExternalAuthenticationContext.class, true);
+ final ExternalAuthenticationContext eac =
+ (ExternalAuthenticationContext) ac.addSubcontext(new ExternalAuthenticationContext(ext), true);
eac.setPrincipalName("foo");
final Event event = action.execute(src);
@@ -90,7 +96,8 @@ public class ValidateExternalAuthenticationTest extends BaseAuthenticationContex
@Test public void testPrincipal() {
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
- final ExternalAuthenticationContext eac = ac.getSubcontext(ExternalAuthenticationContext.class, true);
+ final ExternalAuthenticationContext eac =
+ (ExternalAuthenticationContext) ac.addSubcontext(new ExternalAuthenticationContext(ext), true);
eac.setPrincipal(new TestPrincipal("foo"));
final Event event = action.execute(src);
@@ -103,7 +110,8 @@ public class ValidateExternalAuthenticationTest extends BaseAuthenticationContex
@Test public void testSubject() {
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
- final ExternalAuthenticationContext eac = ac.getSubcontext(ExternalAuthenticationContext.class, true);
+ final ExternalAuthenticationContext eac =
+ (ExternalAuthenticationContext) ac.addSubcontext(new ExternalAuthenticationContext(ext), true);
final Subject subject = new Subject();
eac.setSubject(subject);
subject.getPrincipals().add(new TestPrincipal("foo"));
@@ -118,7 +126,8 @@ public class ValidateExternalAuthenticationTest extends BaseAuthenticationContex
@Test public void testAuthnInstant() {
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
- final ExternalAuthenticationContext eac = ac.getSubcontext(ExternalAuthenticationContext.class, true);
+ final ExternalAuthenticationContext eac =
+ (ExternalAuthenticationContext) ac.addSubcontext(new ExternalAuthenticationContext(ext), true);
eac.setPrincipalName("foo");
final DateTime ts = DateTime.now().minus(3600);
eac.setAuthnInstant(ts);
@@ -133,7 +142,8 @@ public class ValidateExternalAuthenticationTest extends BaseAuthenticationContex
@Test public void testAuthnAuthorities() {
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
- final ExternalAuthenticationContext eac = ac.getSubcontext(ExternalAuthenticationContext.class, true);
+ final ExternalAuthenticationContext eac =
+ (ExternalAuthenticationContext) ac.addSubcontext(new ExternalAuthenticationContext(ext), true);
eac.setPrincipalName("foo");
eac.getAuthenticatingAuthorities().addAll(Arrays.asList("foo", "bar", "baz"));
eac.setPreviousResult(true);
@@ -150,7 +160,8 @@ public class ValidateExternalAuthenticationTest extends BaseAuthenticationContex
@Test public void testException() {
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
- final ExternalAuthenticationContext eac = ac.getSubcontext(ExternalAuthenticationContext.class, true);
+ final ExternalAuthenticationContext eac =
+ (ExternalAuthenticationContext) ac.addSubcontext(new ExternalAuthenticationContext(ext), true);
eac.setAuthnException(new LoginException("foo"));
final Event event = action.execute(src);
@@ -162,7 +173,8 @@ public class ValidateExternalAuthenticationTest extends BaseAuthenticationContex
@Test public void testError() {
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
- final ExternalAuthenticationContext eac = ac.getSubcontext(ExternalAuthenticationContext.class, true);
+ final ExternalAuthenticationContext eac =
+ (ExternalAuthenticationContext) ac.addSubcontext(new ExternalAuthenticationContext(ext), true);
eac.setAuthnError("foo");
final Event event = action.execute(src);
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/spnego/impl/SPNEGOAuthnControllerTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/spnego/impl/SPNEGOAuthnControllerTest.java
index c95ff49..31c02e6 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/spnego/impl/SPNEGOAuthnControllerTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/spnego/impl/SPNEGOAuthnControllerTest.java
@@ -21,8 +21,6 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import java.io.IOException;
-import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -30,26 +28,34 @@ import java.util.Map;
import javax.annotation.Nonnull;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
-import javax.security.auth.login.LoginException;
+import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import net.shibboleth.idp.authn.AuthenticationFlowDescriptor;
import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.authn.ExternalAuthentication;
import net.shibboleth.idp.authn.ExternalAuthenticationException;
import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.ExternalAuthenticationContext;
+import net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl;
import net.shibboleth.idp.authn.principal.UsernamePrincipal;
+import net.shibboleth.idp.profile.RequestContextBuilder;
import net.shibboleth.utilities.java.support.codec.Base64Support;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
import org.mockito.Matchers;
+import org.opensaml.messaging.context.BaseContext;
import org.opensaml.profile.context.ProfileRequestContext;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.mock.web.MockServletContext;
import org.springframework.web.servlet.ModelAndView;
+import org.springframework.webflow.execution.RequestContext;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -83,144 +89,128 @@ public class SPNEGOAuthnControllerTest {
}
@Test(expectedExceptions = {ExternalAuthenticationException.class})
- public void withoutConversationKeyParameter_startSPNEGO_shouldThrowExternalAuthenticationException()
- throws ExternalAuthenticationException, IOException {
- MockHttpServletRequest req = new MockHttpServletRequest();
- controller.startSPNEGO(TEST_CONVERSATION_KEY, req, null);
+ public void withoutConversationKeyParameter_startSPNEGO_shouldThrowExternalAuthenticationException() throws Exception {
+ controller.startSPNEGO(TEST_CONVERSATION_KEY,
+ (HttpServletRequest) buildConversationRequestContext(null).getExternalContext().getNativeRequest(), null);
}
@Test(expectedExceptions = ExternalAuthenticationException.class)
- public void givenMismatchedKeys_startSPNEGO_shouldThrowExternalAuthenticationException()
- throws ExternalAuthenticationException, IOException {
+ public void givenMismatchedKeys_startSPNEGO_shouldThrowExternalAuthenticationException() throws Exception {
controller.startSPNEGO("e1s2",
- buildConversationHttpServletRequest(TEST_CONVERSATION_KEY, new StubExternalAuthentication()), null);
+ (HttpServletRequest) buildConversationRequestContext(TEST_CONVERSATION_KEY).getExternalContext().getNativeRequest(), null);
}
@Test(expectedExceptions = ExternalAuthenticationException.class)
- public void givenNullKey_startSPNEGO_shouldReturnAuthenticationException() throws ExternalAuthenticationException,
- IOException {
- MockHttpServletRequest req = buildConversationHttpServletRequest(null, new StubExternalAuthentication());
- controller.startSPNEGO(TEST_CONVERSATION_KEY, req, null);
+ public void givenNullKey_startSPNEGO_shouldReturnAuthenticationException() throws Exception {
+ RequestContext req = buildConversationRequestContext(null);
+ controller.startSPNEGO(TEST_CONVERSATION_KEY,
+ (MockHttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (MockHttpServletResponse) req.getExternalContext().getNativeResponse());
}
@Test
- public void withoutSPNEGOContext_startSPNEGO_shouldReturnAuthenticationError()
- throws ExternalAuthenticationException, IOException {
- StubExternalAuthentication ea = new StubExternalAuthentication();
- ProfileRequestContext prc = new ProfileRequestContext();
- prc.addSubcontext(new AuthenticationContext());
- ea.setProfileRequestContext(prc);
- MockHttpServletRequest req = buildConversationHttpServletRequest(TEST_CONVERSATION_KEY, ea);
- ModelAndView mv = controller.startSPNEGO(TEST_CONVERSATION_KEY, req, new MockHttpServletResponse());
+ public void withoutSPNEGOContext_startSPNEGO_shouldReturnAuthenticationError() throws Exception {
+ RequestContext req = buildConversationRequestContext(TEST_CONVERSATION_KEY);
+ ModelAndView mv = controller.startSPNEGO(TEST_CONVERSATION_KEY,
+ (MockHttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (MockHttpServletResponse) req.getExternalContext().getNativeResponse());
assertAuthenticationError(req, mv, AuthnEventIds.INVALID_AUTHN_CTX);
}
@Test
- public void withoutKerberosSettings_startSPNEGO_shouldReturnAuthenticationError()
- throws ExternalAuthenticationException, IOException {
- StubExternalAuthentication ea = new StubExternalAuthentication();
- ProfileRequestContext prc = new ProfileRequestContext();
- AuthenticationContext ac = new AuthenticationContext();
+ public void withoutKerberosSettings_startSPNEGO_shouldReturnAuthenticationError() throws Exception {
+ RequestContext req = buildConversationRequestContext(TEST_CONVERSATION_KEY);
+ final ProfileRequestContext prc =
+ (ProfileRequestContext) req.getConversationScope().get(ProfileRequestContext.BINDING_KEY);
+ final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
SPNEGOContext sc = new SPNEGOContext();
ac.addSubcontext(sc);
- prc.addSubcontext(ac);
- ea.setProfileRequestContext(prc);
- MockHttpServletRequest req = buildConversationHttpServletRequest(TEST_CONVERSATION_KEY, ea);
- ModelAndView mv = controller.startSPNEGO(TEST_CONVERSATION_KEY, req, new MockHttpServletResponse());
+ ModelAndView mv = controller.startSPNEGO(TEST_CONVERSATION_KEY,
+ (MockHttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (MockHttpServletResponse) req.getExternalContext().getNativeResponse());
assertAuthenticationError(req, mv, AuthnEventIds.INVALID_AUTHN_CTX);
}
@Test
- public void givenKerberosSettings_startSPNEGO_shouldReturnModelAndView() throws ExternalAuthenticationException,
- IOException {
- MockHttpServletRequest req = buildKerberosContextHttpServletRequest();
- ModelAndView modelAndView = controller.startSPNEGO(TEST_CONVERSATION_KEY, req, new MockHttpServletResponse());
- assertModelAndView(modelAndView, req);
+ public void givenKerberosSettings_startSPNEGO_shouldReturnModelAndView() throws Exception {
+ RequestContext req = buildKerberosContextRequestContext();
+ ModelAndView mv = controller.startSPNEGO(TEST_CONVERSATION_KEY,
+ (MockHttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (MockHttpServletResponse) req.getExternalContext().getNativeResponse());
+ assertModelAndView(mv, req);
}
@Test
- public void givenKerberosSettings_startSPNEGO_shouldPreserveQueryString() throws ExternalAuthenticationException,
- IOException {
- MockHttpServletRequest req = buildKerberosContextHttpServletRequest();
- req.setQueryString("dummy query string");
- ModelAndView modelAndView = controller.startSPNEGO(TEST_CONVERSATION_KEY, req, new MockHttpServletResponse());
- assertModelAndView(modelAndView, req);
+ public void givenKerberosSettings_startSPNEGO_shouldPreserveQueryString() throws Exception {
+ RequestContext req = buildKerberosContextRequestContext();
+ ((MockHttpServletRequest) req.getExternalContext().getNativeRequest()).setQueryString("dummy query string");
+ ModelAndView mv = controller.startSPNEGO(TEST_CONVERSATION_KEY,
+ (MockHttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (MockHttpServletResponse) req.getExternalContext().getNativeResponse());
+ assertModelAndView(mv, req);
}
@Test
- public void givenKerberosSettings_startSPNEGO_shouldReplyUnauthorizedNegotiate()
- throws ExternalAuthenticationException, IOException {
- MockHttpServletRequest req = buildKerberosContextHttpServletRequest();
- MockHttpServletResponse res = new MockHttpServletResponse();
- controller.startSPNEGO(TEST_CONVERSATION_KEY, req, res);
- assertResponseUnauthorizedNegotiate(res);
+ public void givenKerberosSettings_startSPNEGO_shouldReplyUnauthorizedNegotiate() throws Exception {
+ RequestContext req = buildKerberosContextRequestContext();
+ controller.startSPNEGO(TEST_CONVERSATION_KEY,
+ (MockHttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (MockHttpServletResponse) req.getExternalContext().getNativeResponse());
+ assertResponseUnauthorizedNegotiate(req);
}
@Test
- public void withoutNegotiateToken_continueSPNEGO_shouldReturnModelAndView() throws ExternalAuthenticationException,
- IOException {
- StubExternalAuthentication ea = new StubExternalAuthentication();
- ProfileRequestContext prc = new ProfileRequestContext();
- ea.setProfileRequestContext(prc);
- MockHttpServletRequest req = buildConversationHttpServletRequest(TEST_CONVERSATION_KEY, ea);
+ public void withoutNegotiateToken_continueSPNEGO_shouldReturnModelAndView() throws Exception {
+ RequestContext req = buildConversationRequestContext(TEST_CONVERSATION_KEY);
ModelAndView mv =
- controller.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate", req, new MockHttpServletResponse());
+ controller.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate",
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
assertModelAndView(mv, req);
}
@Test
- public void withoutNegotiateToken_continueSPNEGO_shouldPreserveQueryString()
- throws ExternalAuthenticationException, IOException {
- StubExternalAuthentication ea = new StubExternalAuthentication();
- ProfileRequestContext prc = new ProfileRequestContext();
- ea.setProfileRequestContext(prc);
- MockHttpServletRequest req = buildConversationHttpServletRequest(TEST_CONVERSATION_KEY, ea);
- req.setQueryString("dummy query string");
+ public void withoutNegotiateToken_continueSPNEGO_shouldPreserveQueryString() throws Exception {
+ RequestContext req = buildConversationRequestContext(TEST_CONVERSATION_KEY);
+ ((MockHttpServletRequest) req.getExternalContext().getNativeRequest()).setQueryString("dummy query string");
ModelAndView mv =
- controller.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate", req, new MockHttpServletResponse());
+ controller.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate",
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
assertModelAndView(mv, req);
}
@Test
- public void withoutNegotiateToken_continueSPNEGO_shouldReplyUnauthorizedNegotiate()
- throws ExternalAuthenticationException, IOException {
- StubExternalAuthentication ea = new StubExternalAuthentication();
- ProfileRequestContext prc = new ProfileRequestContext();
- ea.setProfileRequestContext(prc);
- MockHttpServletRequest req = buildConversationHttpServletRequest(TEST_CONVERSATION_KEY, ea);
- MockHttpServletResponse res = new MockHttpServletResponse();
- controller.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate", req, res);
- assertResponseUnauthorizedNegotiate(res);
+ public void withoutNegotiateToken_continueSPNEGO_shouldReplyUnauthorizedNegotiate() throws Exception {
+ RequestContext req = buildConversationRequestContext(TEST_CONVERSATION_KEY);
+ controller.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate",
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
+ assertResponseUnauthorizedNegotiate(req);
}
@Test
- public void withoutSPNEGOContext_continueSPNEGO_shouldReturnAuthenticationError()
- throws ExternalAuthenticationException, IOException {
- StubExternalAuthentication ea = new StubExternalAuthentication();
- ProfileRequestContext prc = new ProfileRequestContext();
- prc.addSubcontext(new AuthenticationContext());
- ea.setProfileRequestContext(prc);
- MockHttpServletRequest req = buildConversationHttpServletRequest(TEST_CONVERSATION_KEY, ea);
+ public void withoutSPNEGOContext_continueSPNEGO_shouldReturnAuthenticationError() throws Exception {
+ RequestContext req = buildConversationRequestContext(TEST_CONVERSATION_KEY);
ModelAndView mv =
- controller.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA, req, null);
+ controller.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA,
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
assertAuthenticationError(req, mv, AuthnEventIds.INVALID_AUTHN_CTX);
}
@Test
- public void withoutKerberosSettings_continueSPNEGO_shouldReturnAuthenticationError()
- throws ExternalAuthenticationException, IOException {
- StubExternalAuthentication ea = new StubExternalAuthentication();
- ProfileRequestContext prc = new ProfileRequestContext();
- ea.setProfileRequestContext(prc);
- MockHttpServletRequest req = buildConversationHttpServletRequest(TEST_CONVERSATION_KEY, ea);
+ public void withoutKerberosSettings_continueSPNEGO_shouldReturnAuthenticationError() throws Exception {
+ final RequestContext req = buildConversationRequestContext(TEST_CONVERSATION_KEY);
ModelAndView mv =
- controller.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA, req, null);
+ controller.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA,
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
assertAuthenticationError(req, mv, AuthnEventIds.INVALID_AUTHN_CTX);
}
@Test
- public void givenFailedGSSContextAcceptorInstantiation_continueSPNEGO_shouldReturnAuthenticationException()
- throws ExternalAuthenticationException, IOException {
+ public void givenFailedGSSContextAcceptorInstantiation_continueSPNEGO_shouldReturnAuthenticationException() throws Exception {
final GSSException expected = new GSSException(0);
SPNEGOAuthnController failedGSSController = new SPNEGOAuthnController() {
@Override
@@ -230,155 +220,152 @@ public class SPNEGOAuthnControllerTest {
throw expected;
}
};
- MockHttpServletRequest req = buildKerberosContextHttpServletRequest();
+ final RequestContext req = buildKerberosContextRequestContext();
ModelAndView mv =
- failedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA, req,
- null);
+ failedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA,
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
- Assert.assertSame((GSSException) ((ExternalAuthenticationException) req
+ Assert.assertSame(((Exception) ((ServletRequest) req.getExternalContext().getNativeRequest())
.getAttribute(ExternalAuthentication.AUTHENTICATION_EXCEPTION_KEY)).getCause(), expected);
assertAuthenticationExceptionCause(req, mv, GSSException.class);
}
@Test
- public void givenSuccessfulGSSContextAcceptorInstantiation_continueSPNEGO_shouldHaveSetAcceptorInSPNEGOContext()
- throws ExternalAuthenticationException, IOException, Exception {
- GSSContext mockGSSContext = mock(GSSContext.class);
+ public void givenSuccessfulGSSContextAcceptorInstantiation_continueSPNEGO_shouldHaveSetAcceptorInSPNEGOContext() throws Exception {
+ final GSSContext mockGSSContext = mock(GSSContext.class);
when(mockGSSContextAcceptor.acceptSecContext(Matchers.<byte[]> any(), anyInt(), anyInt())).thenReturn(
"tokenBytes".getBytes());
when(mockGSSContextAcceptor.getContext()).thenReturn(mockGSSContext);
when(mockGSSContext.isEstablished()).thenReturn(false);
- MockHttpServletRequest req = buildSPNEGOHttpServletRequest(NEGOTIATE_HEADER_DATA);
- MockHttpServletResponse res = new MockHttpServletResponse();
- mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA, req, res);
-
- StubExternalAuthentication ea =
- (StubExternalAuthentication) req.getSession(true).getAttribute(
- ExternalAuthentication.CONVERSATION_KEY + TEST_CONVERSATION_KEY);
- ProfileRequestContext prc = ea.getProfileRequestContext(req);
- AuthenticationContext authnContext = prc.getSubcontext(AuthenticationContext.class);
- SPNEGOContext spnegoContext = authnContext != null ? authnContext.getSubcontext(SPNEGOContext.class) : null;
+ final RequestContext req = buildSPNEGORequestContext(NEGOTIATE_HEADER_DATA);
+ mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA,
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
+
+ final AuthenticationContext authnContext =
+ ((BaseContext) req.getConversationScope().get(ProfileRequestContext.BINDING_KEY)).getSubcontext(AuthenticationContext.class);
+ final SPNEGOContext spnegoContext = authnContext != null ? authnContext.getSubcontext(SPNEGOContext.class) : null;
Assert.assertNotNull(spnegoContext);
Assert.assertEquals(spnegoContext.getContextAcceptor(), mockGSSContextAcceptor);
}
@Test
- public void givenHeaderAuthorizationNegotiate_withNTLMdata_continueSPNEGO_shouldReturnAuthenticationError()
- throws ExternalAuthenticationException, IOException {
- MockHttpServletRequest req = buildSPNEGOHttpServletRequest(NTLMSSP_HEADER_DATA);
- ModelAndView mv =
- controller.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NTLMSSP_HEADER_DATA, req, null);
+ public void givenHeaderAuthorizationNegotiate_withNTLMdata_continueSPNEGO_shouldReturnAuthenticationError() throws Exception {
+ final RequestContext req = buildSPNEGORequestContext(NTLMSSP_HEADER_DATA);
+ final ModelAndView mv =
+ controller.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NTLMSSP_HEADER_DATA,
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
assertAuthenticationError(req, mv, SPNEGOAuthnController.NTLM_UNSUPPORTED);
}
@Test
- public void whenAcceptSecContextThrowsException_continueSPNEGO_shouldReturnAuthenticationException()
- throws ExternalAuthenticationException, IOException, LoginException, GSSException,
- PrivilegedActionException, Exception {
- RuntimeException e = new RuntimeException();
+ public void whenAcceptSecContextThrowsException_continueSPNEGO_shouldReturnAuthenticationException() throws Exception {
+ final RuntimeException e = new RuntimeException();
when(mockGSSContextAcceptor.acceptSecContext(Matchers.<byte[]> any(), anyInt(), anyInt())).thenThrow(e);
- MockHttpServletRequest req = buildSPNEGOHttpServletRequest(NEGOTIATE_HEADER_DATA);
- ModelAndView mv =
- mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA, req,
- null);
+ final RequestContext req = buildSPNEGORequestContext(NEGOTIATE_HEADER_DATA);
+ final ModelAndView mv =
+ mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA,
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
- Assert.assertSame((RuntimeException) ((ExternalAuthenticationException) req
+ Assert.assertSame(((Exception) ((HttpServletRequest) req.getExternalContext().getNativeRequest())
.getAttribute(ExternalAuthentication.AUTHENTICATION_EXCEPTION_KEY)).getCause(), e);
assertAuthenticationExceptionCause(req, mv, RuntimeException.class);
}
@Test
- public void withoutGSSContext_continueSPNEGO_shouldReturnModelAndView() throws LoginException, GSSException,
- PrivilegedActionException, ExternalAuthenticationException, IOException, Exception {
+ public void withoutGSSContext_continueSPNEGO_shouldReturnModelAndView() throws Exception {
when(mockGSSContextAcceptor.acceptSecContext(Matchers.<byte[]> any(), anyInt(), anyInt())).thenReturn(
"tokenBytes".getBytes());
when(mockGSSContextAcceptor.getContext()).thenReturn(null);
- MockHttpServletRequest req = buildSPNEGOHttpServletRequest(NEGOTIATE_HEADER_DATA);
- ModelAndView modelAndView =
- mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "", req, new MockHttpServletResponse());
+ final RequestContext req = buildSPNEGORequestContext(NEGOTIATE_HEADER_DATA);
+ final ModelAndView modelAndView =
+ mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "",
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
assertModelAndView(modelAndView, req);
}
@Test
- public void withoutGSSContext_continueSPNEGO_shouldReplyUnauthorizedNegotiate() throws LoginException,
- GSSException, PrivilegedActionException, ExternalAuthenticationException, IOException, Exception {
+ public void withoutGSSContext_continueSPNEGO_shouldReplyUnauthorizedNegotiate() throws Exception {
when(mockGSSContextAcceptor.acceptSecContext(Matchers.<byte[]> any(), anyInt(), anyInt())).thenReturn(
"tokenBytes".getBytes());
when(mockGSSContextAcceptor.getContext()).thenReturn(null);
- MockHttpServletRequest req = buildSPNEGOHttpServletRequest(NEGOTIATE_HEADER_DATA);
- MockHttpServletResponse res = new MockHttpServletResponse();
- mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA, req, res);
- assertResponseUnauthorizedNegotiate(res, Base64Support.encode("tokenBytes".getBytes(), false));
+ final RequestContext req = buildSPNEGORequestContext(NEGOTIATE_HEADER_DATA);
+ mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA,
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
+ assertResponseUnauthorizedNegotiate(req, Base64Support.encode("tokenBytes".getBytes(), false));
}
@Test
- public void givenGSSContextNotEstablished_continueSPNEGO_shouldReturnModelAndView() throws LoginException,
- GSSException, PrivilegedActionException, ExternalAuthenticationException, IOException, Exception {
- GSSContext mockGSSContext = mock(GSSContext.class);
+ public void givenGSSContextNotEstablished_continueSPNEGO_shouldReturnModelAndView() throws Exception {
+ final GSSContext mockGSSContext = mock(GSSContext.class);
when(mockGSSContextAcceptor.acceptSecContext(Matchers.<byte[]> any(), anyInt(), anyInt())).thenReturn(
"tokenBytes".getBytes());
when(mockGSSContextAcceptor.getContext()).thenReturn(mockGSSContext);
when(mockGSSContext.isEstablished()).thenReturn(false);
- MockHttpServletRequest req = buildSPNEGOHttpServletRequest(NEGOTIATE_HEADER_DATA);
- ModelAndView modelAndView =
- mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "", req, new MockHttpServletResponse());
+ final RequestContext req = buildSPNEGORequestContext(NEGOTIATE_HEADER_DATA);
+ final ModelAndView modelAndView =
+ mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "",
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
assertModelAndView(modelAndView, req);
}
@Test
- public void givenGSSContextNotEstablished_continueSPNEGO_shouldReplyUnauthorizedNegotiate() throws LoginException,
- GSSException, PrivilegedActionException, ExternalAuthenticationException, IOException, Exception {
- GSSContext mockGSSContext = mock(GSSContext.class);
+ public void givenGSSContextNotEstablished_continueSPNEGO_shouldReplyUnauthorizedNegotiate() throws Exception {
+ final GSSContext mockGSSContext = mock(GSSContext.class);
when(mockGSSContextAcceptor.acceptSecContext(Matchers.<byte[]> any(), anyInt(), anyInt())).thenReturn(
"tokenBytes".getBytes());
when(mockGSSContextAcceptor.getContext()).thenReturn(mockGSSContext);
when(mockGSSContext.isEstablished()).thenReturn(false);
- MockHttpServletRequest req = buildSPNEGOHttpServletRequest(NEGOTIATE_HEADER_DATA);
- MockHttpServletResponse res = new MockHttpServletResponse();
- mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA, req, res);
- assertResponseUnauthorizedNegotiate(res, Base64Support.encode("tokenBytes".getBytes(), false));
+ final RequestContext req = buildSPNEGORequestContext(NEGOTIATE_HEADER_DATA);
+ mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA,
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
+ assertResponseUnauthorizedNegotiate(req, Base64Support.encode("tokenBytes".getBytes(), false));
}
@Test
- public void givenGSSContextEstablished_andGSSException_continueSPNEGO_shouldReturnAuthenticationError()
- throws LoginException, GSSException, PrivilegedActionException, ExternalAuthenticationException,
- IOException, Exception {
- GSSContext mockGSSContext = mock(GSSContext.class);
- GSSException gssException = new GSSException(0);
+ public void givenGSSContextEstablished_andGSSException_continueSPNEGO_shouldReturnAuthenticationError() throws Exception {
+ final GSSContext mockGSSContext = mock(GSSContext.class);
+ final GSSException gssException = new GSSException(0);
when(mockGSSContextAcceptor.acceptSecContext(Matchers.<byte[]> any(), anyInt(), anyInt())).thenReturn(
"tokenBytes".getBytes());
when(mockGSSContextAcceptor.getContext()).thenReturn(mockGSSContext);
when(mockGSSContext.isEstablished()).thenReturn(true);
when(mockGSSContext.getSrcName()).thenThrow(gssException);
- MockHttpServletRequest req = buildSPNEGOHttpServletRequest(NEGOTIATE_HEADER_DATA);
- ModelAndView mv =
- mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA, req,
- null);
- Assert.assertSame((GSSException) ((ExternalAuthenticationException) req
+ final RequestContext req = buildSPNEGORequestContext(NEGOTIATE_HEADER_DATA);
+ final ModelAndView mv =
+ mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA,
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
+ Assert.assertSame(((Exception) ((HttpServletRequest) req.getExternalContext().getNativeRequest())
.getAttribute(ExternalAuthentication.AUTHENTICATION_EXCEPTION_KEY)).getCause(), gssException);
assertAuthenticationExceptionCause(req, mv, GSSException.class);
}
@Test
- public void givenGSSContextEstablished_continueSPNEGO_shouldReturnNull() throws LoginException, GSSException,
- PrivilegedActionException, ExternalAuthenticationException, IOException, Exception {
- GSSContext mockGSSContext = mock(GSSContext.class);
- GSSName mockGssName = mock(GSSName.class);
+ public void givenGSSContextEstablished_continueSPNEGO_shouldReturnNull() throws Exception {
+ final GSSContext mockGSSContext = mock(GSSContext.class);
+ final GSSName mockGssName = mock(GSSName.class);
when(mockGSSContextAcceptor.acceptSecContext(Matchers.<byte[]> any(), anyInt(), anyInt())).thenReturn(
"tokenBytes".getBytes());
when(mockGSSContextAcceptor.getContext()).thenReturn(mockGSSContext);
when(mockGSSContext.isEstablished()).thenReturn(true);
when(mockGSSContext.getSrcName()).thenReturn(mockGssName);
when(mockGssName.toString()).thenReturn("testname at realm");
- MockHttpServletRequest req = buildSPNEGOHttpServletRequest(NEGOTIATE_HEADER_DATA);
- Assert.assertNull(mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate "
- + NEGOTIATE_HEADER_DATA, req, null));
+ final RequestContext req = buildSPNEGORequestContext(NEGOTIATE_HEADER_DATA);
+ Assert.assertNull(mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA,
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse()));
}
@Test
- public void givenGSSContextEstablished_continueSPNEGO_shouldSetAuthenticationSubjectAttribute()
- throws LoginException, GSSException, PrivilegedActionException, ExternalAuthenticationException,
- IOException, Exception {
+ public void givenGSSContextEstablished_continueSPNEGO_shouldSetAuthenticationSubjectAttribute() throws Exception {
GSSContext mockGSSContext = mock(GSSContext.class);
GSSName mockGssName = mock(GSSName.class);
when(mockGSSContextAcceptor.acceptSecContext(Matchers.<byte[]> any(), anyInt(), anyInt())).thenReturn(
@@ -387,47 +374,49 @@ public class SPNEGOAuthnControllerTest {
when(mockGSSContext.isEstablished()).thenReturn(true);
when(mockGSSContext.getSrcName()).thenReturn(mockGssName);
when(mockGssName.toString()).thenReturn("testname at realm");
- MockHttpServletRequest req = buildSPNEGOHttpServletRequest(NEGOTIATE_HEADER_DATA);
- mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA, req, null);
- Subject s = (Subject) req.getAttribute(ExternalAuthentication.SUBJECT_KEY);
+ RequestContext req = buildSPNEGORequestContext(NEGOTIATE_HEADER_DATA);
+ mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA,
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
+ Subject s = (Subject) ((HttpServletRequest) req.getExternalContext().getNativeRequest()).getAttribute(ExternalAuthentication.SUBJECT_KEY);
Assert.assertEquals(s.getClass(), Subject.class);
Assert.assertTrue(s.getPrincipals(KerberosPrincipal.class).contains(new KerberosPrincipal("testname at realm")));
Assert.assertTrue(s.getPrincipals(UsernamePrincipal.class).contains(new UsernamePrincipal("testname at realm")));
}
@Test
- public void givenGSSContextEstablishedButNoGSSNameIsNull_continueSPNEGO_shouldSetAuthenticationSubjectAttribute()
- throws LoginException, GSSException, PrivilegedActionException, ExternalAuthenticationException,
- IOException, Exception {
+ public void givenGSSContextEstablishedButNoGSSNameIsNull_continueSPNEGO_shouldSetAuthenticationSubjectAttribute() throws Exception {
GSSContext mockGSSContext = mock(GSSContext.class);
when(mockGSSContextAcceptor.acceptSecContext(Matchers.<byte[]> any(), anyInt(), anyInt())).thenReturn(
"tokenBytes".getBytes());
when(mockGSSContextAcceptor.getContext()).thenReturn(mockGSSContext);
when(mockGSSContext.isEstablished()).thenReturn(true);
when(mockGSSContext.getSrcName()).thenReturn(null);
- MockHttpServletRequest req = buildSPNEGOHttpServletRequest(NEGOTIATE_HEADER_DATA);
- ModelAndView mv = mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA, req, null);
+ RequestContext req = buildSPNEGORequestContext(NEGOTIATE_HEADER_DATA);
+ ModelAndView mv = mockedGSSController.continueSPNEGO(TEST_CONVERSATION_KEY, "Negotiate " + NEGOTIATE_HEADER_DATA,
+ (HttpServletRequest) req.getExternalContext().getNativeRequest(),
+ (HttpServletResponse) req.getExternalContext().getNativeResponse());
Assert.assertNull(mv);
- Assert.assertEquals(((ExternalAuthenticationException) req
- .getAttribute(ExternalAuthentication.AUTHENTICATION_EXCEPTION_KEY)).getClass(),ExternalAuthenticationException.class);
+ Assert.assertEquals(
+ ((HttpServletRequest) req.getExternalContext().getNativeRequest()).getAttribute(ExternalAuthentication.AUTHENTICATION_EXCEPTION_KEY).getClass(),
+ ExternalAuthenticationException.class);
}
- private MockHttpServletRequest buildSPNEGOHttpServletRequest(String negotiateHeaderData) {
- MockHttpServletRequest req = buildKerberosContextHttpServletRequest();
- req.addHeader(HttpHeaders.AUTHORIZATION, "Negotiate " + negotiateHeaderData);
+ private RequestContext buildSPNEGORequestContext(String negotiateHeaderData) throws ComponentInitializationException {
+ RequestContext req = buildKerberosContextRequestContext();
+ ((MockHttpServletRequest) req.getExternalContext().getNativeRequest()).addHeader(HttpHeaders.AUTHORIZATION, "Negotiate " + negotiateHeaderData);
return req;
}
- private MockHttpServletRequest buildKerberosContextHttpServletRequest() {
- StubExternalAuthentication ea = new StubExternalAuthentication();
- ea.setProfileRequestContext(buildKerberosProfileRequestContext());
- MockHttpServletRequest req = buildConversationHttpServletRequest(TEST_CONVERSATION_KEY, ea);
+ private RequestContext buildKerberosContextRequestContext() throws ComponentInitializationException {
+ RequestContext req = buildConversationRequestContext(TEST_CONVERSATION_KEY);
+ buildKerberosProfileRequestContext(req);
return req;
}
- private ProfileRequestContext buildKerberosProfileRequestContext() {
- ProfileRequestContext prc = new ProfileRequestContext();
- AuthenticationContext ac = new AuthenticationContext();
+ private ProfileRequestContext buildKerberosProfileRequestContext(RequestContext rc) {
+ ProfileRequestContext prc = (ProfileRequestContext) rc.getConversationScope().get(ProfileRequestContext.BINDING_KEY);
+ AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
SPNEGOContext sc = new SPNEGOContext();
KerberosSettings ks = new KerberosSettings();
List<KerberosRealmSettings> realms = new ArrayList<KerberosRealmSettings>();
@@ -435,42 +424,55 @@ public class SPNEGOAuthnControllerTest {
ks.setRealms(realms);
sc.setKerberosSettings(ks);
ac.addSubcontext(sc);
- prc.addSubcontext(ac);
return prc;
}
- private MockHttpServletRequest buildConversationHttpServletRequest(String conversationKey,
- ExternalAuthentication externalAuthentication) {
- MockHttpServletRequest req = new MockHttpServletRequest();
- req.addParameter(ExternalAuthentication.CONVERSATION_KEY, conversationKey);
- req.getSession(true).setAttribute(ExternalAuthentication.CONVERSATION_KEY + TEST_CONVERSATION_KEY,
- externalAuthentication);
- return req;
- }
-
- private void assertAuthenticationError(MockHttpServletRequest request, ModelAndView mv, String expectedError) {
+ private RequestContext buildConversationRequestContext(String conversationKey) throws ComponentInitializationException {
+ final RequestContext rc = new RequestContextBuilder().buildRequestContext();
+ if (conversationKey != null) {
+ ((MockHttpServletRequest) rc.getExternalContext().getNativeRequest()).addParameter(
+ ExternalAuthentication.CONVERSATION_KEY, conversationKey);
+ }
+
+ final ProfileRequestContext prc =
+ (ProfileRequestContext) rc.getConversationScope().get(ProfileRequestContext.BINDING_KEY);
+ ((MockServletContext) rc.getExternalContext().getNativeContext()).setAttribute(ExternalAuthentication.SWF_KEY, prc);
+
+ final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class, true);
+ ac.setAttemptedFlow(new AuthenticationFlowDescriptor());
+
+ final ExternalAuthenticationContext eac = (ExternalAuthenticationContext) ac.addSubcontext(
+ new ExternalAuthenticationContext(new ExternalAuthenticationImpl()));
+ eac.setFlowExecutionUrl("foo");
+
+ return rc;
+ }
+
+ private void assertAuthenticationError(RequestContext request, ModelAndView mv, String expectedError) {
Assert.assertNull(mv);
- Assert.assertEquals(request.getAttribute(ExternalAuthentication.AUTHENTICATION_ERROR_KEY).toString(),
+ Assert.assertEquals(
+ ((ServletRequest) request.getExternalContext().getNativeRequest()).getAttribute(ExternalAuthentication.AUTHENTICATION_ERROR_KEY).toString(),
expectedError);
}
- private void assertAuthenticationExceptionCause(MockHttpServletRequest request, ModelAndView mv,
- Class exceptedExceptionClass) {
+ private void assertAuthenticationExceptionCause(RequestContext request, ModelAndView mv,
+ Class<?> exceptedExceptionClass) {
Assert.assertNull(mv);
- Assert.assertEquals(((ExternalAuthenticationException) request
+ Assert.assertEquals(((Exception) ((ServletRequest) request.getExternalContext().getNativeRequest())
.getAttribute(ExternalAuthentication.AUTHENTICATION_EXCEPTION_KEY)).getCause().getClass(),
exceptedExceptionClass);
}
- private void assertModelAndView(ModelAndView modelAndView, MockHttpServletRequest request) {
+ private void assertModelAndView(ModelAndView modelAndView, RequestContext request) {
Assert.assertEquals(modelAndView.getViewName(), "spnego-unavailable");
Map<String, Object> model = modelAndView.getModel();
Assert.assertTrue(model.containsKey("encoder"), "Model doesn't contain \"encoder\"");
Assert.assertEquals(model.get("encoder").getClass(), Class.class);
Assert.assertTrue(model.containsKey("errorUrl"), "Model doesn't contain \"errorUrl\"");
Assert.assertEquals(model.get("errorUrl").getClass(), String.class);
- if (request.getQueryString() != null) {
- Assert.assertTrue(((String) model.get("errorUrl")).endsWith("/error?" + request.getQueryString()));
+ if (((HttpServletRequest) request.getExternalContext().getNativeRequest()).getQueryString() != null) {
+ Assert.assertTrue(((String) model.get("errorUrl")).endsWith(
+ "/error?" + ((HttpServletRequest) request.getExternalContext().getNativeRequest()).getQueryString()));
} else {
Assert.assertTrue(((String) model.get("errorUrl")).endsWith("/error"));
}
@@ -478,38 +480,16 @@ public class SPNEGOAuthnControllerTest {
Assert.assertTrue(model.get("request") instanceof HttpServletRequest);
}
- private void assertResponseUnauthorizedNegotiate(MockHttpServletResponse response) {
+ private void assertResponseUnauthorizedNegotiate(RequestContext request) {
+ final HttpServletResponse response = (HttpServletResponse) request.getExternalContext().getNativeResponse();
Assert.assertEquals(response.getStatus(), 401);
Assert.assertEquals(response.getHeader("WWW-Authenticate"), "Negotiate");
}
- private void assertResponseUnauthorizedNegotiate(MockHttpServletResponse response, String base64token) {
+ private void assertResponseUnauthorizedNegotiate(RequestContext request, String base64token) {
+ final HttpServletResponse response = (HttpServletResponse) request.getExternalContext().getNativeResponse();
Assert.assertEquals(response.getStatus(), 401);
Assert.assertEquals(response.getHeader("WWW-Authenticate"), "Negotiate " + base64token);
}
- private class StubExternalAuthentication extends ExternalAuthentication {
-
- private ProfileRequestContext profileRequestContext;
-
- public void setProfileRequestContext(ProfileRequestContext profileRequestContext) {
- this.profileRequestContext = profileRequestContext;
- }
-
- @Override
- protected void doStart(HttpServletRequest request) throws ExternalAuthenticationException {
- }
-
- @Override
- protected void doFinish(HttpServletRequest request, HttpServletResponse response)
- throws ExternalAuthenticationException, IOException {
- }
-
- @Override
- protected ProfileRequestContext getProfileRequestContext(HttpServletRequest request)
- throws ExternalAuthenticationException {
- return profileRequestContext;
- }
-
- }
-}
+}
\ No newline at end of file
diff --git a/idp-conf/src/main/resources/system/flows/authn/external-authn-flow.xml b/idp-conf/src/main/resources/system/flows/authn/external-authn-flow.xml
index 3dc9b50..a0a67dc 100644
--- a/idp-conf/src/main/resources/system/flows/authn/external-authn-flow.xml
+++ b/idp-conf/src/main/resources/system/flows/authn/external-authn-flow.xml
@@ -7,8 +7,7 @@
<view-state id="ExternalTransfer" view="externalRedirect:#{T(net.shibboleth.idp.authn.ExternalAuthentication).getExternalRedirect(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.External.externalAuthnPathStrategy').apply(opensamlProfileRequestContext), flowExecutionContext.getKey().toString())}">
<on-render>
- <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.context.ExternalAuthenticationContext(), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
- <evaluate expression="externalContext.getNativeRequest().getSession().setAttribute('conversation' + flowExecutionContext.getKey().toString(), new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl(opensamlProfileRequestContext, calledAsExtendedFlow?:false))" />
+ <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.context.ExternalAuthenticationContext(new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl(calledAsExtendedFlow?:false)), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
</on-render>
<transition to="ValidateExternalAuthentication" />
</view-state>
diff --git a/idp-conf/src/main/resources/system/flows/authn/remoteuser-authn-flow.xml b/idp-conf/src/main/resources/system/flows/authn/remoteuser-authn-flow.xml
index c81ce16..4580a4f 100644
--- a/idp-conf/src/main/resources/system/flows/authn/remoteuser-authn-flow.xml
+++ b/idp-conf/src/main/resources/system/flows/authn/remoteuser-authn-flow.xml
@@ -7,10 +7,10 @@
<view-state id="ExternalTransfer" view="externalRedirect:#{T(net.shibboleth.idp.authn.ExternalAuthentication).getExternalRedirect(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.RemoteUser.externalAuthnPathStrategy').apply(opensamlProfileRequestContext), flowExecutionContext.getKey().toString())}">
<on-render>
- <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.context.ExternalAuthenticationContext(), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
- <evaluate expression="externalContext.getNativeRequest().getSession().setAttribute('conversation' + flowExecutionContext.getKey().toString(), new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl(opensamlProfileRequestContext, calledAsExtendedFlow?:false))" />
+ <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.context.ExternalAuthenticationContext(new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl(calledAsExtendedFlow?:false)), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
</on-render>
<transition to="ValidateExternalAuthentication" />
+
</view-state>
<action-state id="ValidateExternalAuthentication">
diff --git a/idp-conf/src/main/resources/system/flows/authn/spnego-authn-flow.xml b/idp-conf/src/main/resources/system/flows/authn/spnego-authn-flow.xml
index c0d8a4d..bfd5b96 100644
--- a/idp-conf/src/main/resources/system/flows/authn/spnego-authn-flow.xml
+++ b/idp-conf/src/main/resources/system/flows/authn/spnego-authn-flow.xml
@@ -54,8 +54,7 @@
<view-state id="RunSPNEGO" view="externalRedirect:#{flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.SPNEGO.externalAuthnPathStrategy').apply(opensamlProfileRequestContext)}/#{flowExecutionContext.getKey().toString()}?conversation=#{flowExecutionContext.getKey().toString()}">
<on-render>
<evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.spnego.impl.SPNEGOContext(), true).setKerberosSettings(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.SPNEGO.Krb5.Settings'))" />
- <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.context.ExternalAuthenticationContext(), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
- <evaluate expression="externalContext.getNativeRequest().getSession().setAttribute('conversation' + flowExecutionContext.getKey().toString(), new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl(opensamlProfileRequestContext))" />
+ <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.context.ExternalAuthenticationContext(new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl()), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
</on-render>
<transition on="proceed" to="ValidateExternalAuthentication" />
diff --git a/idp-conf/src/main/resources/system/flows/authn/x509-authn-flow.xml b/idp-conf/src/main/resources/system/flows/authn/x509-authn-flow.xml
index ce1e9df..d00cd30 100644
--- a/idp-conf/src/main/resources/system/flows/authn/x509-authn-flow.xml
+++ b/idp-conf/src/main/resources/system/flows/authn/x509-authn-flow.xml
@@ -10,8 +10,7 @@
<view-state id="ExternalTransfer" view="externalRedirect:#{T(net.shibboleth.idp.authn.ExternalAuthentication).getExternalRedirect(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.X509.externalAuthnPathStrategy').apply(opensamlProfileRequestContext), flowExecutionContext.getKey().toString())}">
<on-render>
- <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.context.ExternalAuthenticationContext(), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
- <evaluate expression="externalContext.getNativeRequest().getSession().setAttribute('conversation' + flowExecutionContext.getKey().toString(), new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl(opensamlProfileRequestContext))" />
+ <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.context.ExternalAuthenticationContext(new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl(calledAsExtendedFlow?:false)), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
</on-render>
<transition to="ValidateExternalAuthentication" />
</view-state>
diff --git a/idp-profile-api/src/main/java/net/shibboleth/idp/profile/interceptor/ExternalInterceptor.java b/idp-profile-api/src/main/java/net/shibboleth/idp/profile/interceptor/ExternalInterceptor.java
new file mode 100644
index 0000000..d6d4c70
--- /dev/null
+++ b/idp-profile-api/src/main/java/net/shibboleth/idp/profile/interceptor/ExternalInterceptor.java
@@ -0,0 +1,227 @@
+/*
+ * 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.profile.interceptor;
+
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.context.ExternalContextHolder;
+import org.springframework.webflow.context.servlet.ServletExternalContext;
+import org.springframework.webflow.execution.FlowExecution;
+import org.springframework.webflow.execution.repository.FlowExecutionRepository;
+import org.springframework.webflow.execution.repository.FlowExecutionRepositoryException;
+import org.springframework.webflow.executor.FlowExecutorImpl;
+
+import com.google.common.base.Strings;
+import com.google.common.net.UrlEscapers;
+
+import net.shibboleth.idp.profile.context.ExternalInterceptorContext;
+import net.shibboleth.idp.profile.context.ProfileInterceptorContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Public interface supporting external interceptor flows outside the webflow engine.
+ *
+ * @since 4.0.0
+ */
+public abstract class ExternalInterceptor {
+
+ /** Parameter supplied to locate the SWF object needed in the servlet context. */
+ @Nonnull @NotEmpty public static final String SWF_KEY = "net.shibboleth.idp.flowExecutor";
+
+ /** Parameter supplied to identify the per-conversation parameter. */
+ @Nonnull @NotEmpty public static final String CONVERSATION_KEY = "conversation";
+
+ /** Request attribute to which an event ID may be bound. */
+ @Nonnull @NotEmpty public static final String EVENT_KEY = "event";
+
+ /**
+ * Computes the appropriate location to pass control to to invoke an external interceptor mechanism.
+ *
+ * <p>The input location should be suitable for use in a Spring "externalRedirect" expression, and may
+ * contain a query string. The result will include any additional parameters needed to invoke the
+ * mechanism.</p>
+ *
+ * @param baseLocation the base location to build off of
+ * @param conversationValue the value to include as a conversation ID
+ *
+ * @return the computed location
+ */
+ @Nonnull @NotEmpty public static String getExternalRedirect(@Nonnull @NotEmpty final String baseLocation,
+ @Nonnull @NotEmpty final String conversationValue) {
+ Constraint.isNotEmpty(baseLocation, "Base location cannot be null or empty");
+
+ final StringBuilder url = new StringBuilder(baseLocation);
+
+ // Add a parameter separator for the conversation ID.
+ url.append(baseLocation.indexOf('?') == -1 ? '?' : '&');
+ url.append(CONVERSATION_KEY).append('=').append(
+ UrlEscapers.urlFormParameterEscaper().escape(conversationValue));
+
+ return url.toString();
+ }
+
+ /**
+ * Initialize a request to an external interceptor by seeking out the information stored in
+ * the servlet session and exposing it as request attributes.
+ *
+ * @param request servlet request
+ *
+ * @return a handle to subsequent use of
+ * {@link #finishExternalInterceptor(java.lang.String, HttpServletRequest, HttpServletResponse)}
+ *
+ * @throws ExternalInterceptorException if an error occurs
+ */
+ @Nonnull @NotEmpty public static String startExternalInterceptor(@Nonnull final HttpServletRequest request)
+ throws ExternalInterceptorException {
+ final String key = request.getParameter(CONVERSATION_KEY);
+ if (Strings.isNullOrEmpty(key)) {
+ throw new ExternalInterceptorException("No conversation key found in request");
+ }
+
+ final ProfileRequestContext profileRequestContext = getProfileRequestContext(key, request);
+ final ExternalInterceptorContext extContext = getExternalInterceptorContext(profileRequestContext);
+ extContext.getExternalInterceptor().doStart(request, profileRequestContext, extContext);
+
+ return key;
+ }
+
+ /**
+ * Complete a request to an external interceptor by seeking out the information stored in
+ * request attributes and transferring to the session's conversation state, and then transfer
+ * control back to the webflow.
+ *
+ * @param key the value returned by {@link #startExternalInterceptor(HttpServletRequest)}
+ * @param request servlet request
+ * @param response servlet response
+ *
+ * @throws ExternalInterceptorException if an error occurs
+ * @throws IOException if the redirect cannot be issued
+ */
+ public static void finishExternalInterceptor(@Nonnull @NotEmpty final String key,
+ @Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response)
+ throws ExternalInterceptorException, IOException {
+
+ final ProfileRequestContext profileRequestContext = getProfileRequestContext(key, request);
+ final ExternalInterceptorContext extContext = getExternalInterceptorContext(profileRequestContext);
+ extContext.getExternalInterceptor().doFinish(request, response, profileRequestContext, extContext);
+ }
+
+ /**
+ * Get the {@link ProfileRequestContext} associated with a request.
+ *
+ * @param key the value returned by {@link #startExternalInterceptor(HttpServletRequest)}
+ * @param request servlet request
+ *
+ * @return the profile request context
+ * @throws ExternalInterceptorException if an error occurs
+ */
+ @Nonnull public static ProfileRequestContext getProfileRequestContext(@Nonnull @NotEmpty final String key,
+ @Nonnull final HttpServletRequest request) throws ExternalInterceptorException {
+
+ final Object obj = request.getServletContext().getAttribute(SWF_KEY);
+ if (!(obj instanceof FlowExecutorImpl)) {
+ throw new ExternalInterceptorException("No FlowExecutor available in servlet context");
+ }
+
+ try {
+ final FlowExecutionRepository repo = ((FlowExecutorImpl) obj).getExecutionRepository();
+ ExternalContextHolder.setExternalContext(
+ new ServletExternalContext(request.getServletContext(), request, null));
+
+ final FlowExecution execution = repo.getFlowExecution(repo.parseFlowExecutionKey(key));
+ final Object prc = execution.getConversationScope().get(ProfileRequestContext.BINDING_KEY);
+ if (!(prc instanceof ProfileRequestContext)) {
+ throw new ExternalInterceptorException(
+ "ProfileRequestContext not available in webflow conversation scope");
+ }
+
+ return (ProfileRequestContext) prc;
+ } catch (final FlowExecutionRepositoryException e) {
+ throw new ExternalInterceptorException("Error retrieving flow conversation", e);
+ } finally {
+ ExternalContextHolder.setExternalContext(null);
+ }
+ }
+
+ /**
+ * Utility method to access the {@link ExternalInterceptorContext}.
+ *
+ * @param profileRequestContext profile request context
+ *
+ * @return the {@link ExternalInterceptorContext} to operate on
+ *
+ * @throws ExternalInterceptorException if the context is missing
+ */
+ @Nonnull private static ExternalInterceptorContext getExternalInterceptorContext(
+ @Nonnull final ProfileRequestContext profileRequestContext) throws ExternalInterceptorException {
+
+ final ProfileInterceptorContext piContext =
+ profileRequestContext.getSubcontext(ProfileInterceptorContext.class);
+ if (piContext == null) {
+ throw new ExternalInterceptorException("No ProfileInterceptorContext found");
+ }
+
+ final ExternalInterceptorContext extContext = piContext.getSubcontext(ExternalInterceptorContext.class);
+ if (extContext == null) {
+ throw new ExternalInterceptorException("No ExternalInterceptorContext found");
+ }
+
+ return extContext;
+ }
+
+ /**
+ * Initialize a request to an external interceptor by seeking out the information stored in
+ * the servlet session and exposing it as request attributes.
+ *
+ * @param request servlet request
+ * @param profileRequestContext profile request context
+ * @param externalInterceptorContext external interceptor context
+ *
+ * @throws ExternalInterceptorException if an error occurs
+ */
+ protected void doStart(@Nonnull final HttpServletRequest request,
+ @Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final ExternalInterceptorContext externalInterceptorContext) throws ExternalInterceptorException {
+ request.setAttribute(ProfileRequestContext.BINDING_KEY, profileRequestContext);
+ }
+
+ /**
+ * Complete a request to an external interceptor by seeking out the information stored in
+ * request attributes and transferring to the session's conversation state, and then transfer
+ * control back to the webflow.
+ *
+ * @param request servlet request
+ * @param response servlet response
+ * @param profileRequestContext profile request context
+ * @param externalInterceptorContext external interceptor context
+ *
+ * @throws ExternalInterceptorException if an error occurs
+ * @throws IOException if the redirect cannot be issued
+ */
+ protected abstract void doFinish(@Nonnull final HttpServletRequest request,
+ @Nonnull final HttpServletResponse response, @Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final ExternalInterceptorContext externalInterceptorContext)
+ throws ExternalInterceptorException, IOException;
+
+}
\ No newline at end of file
diff --git a/idp-profile-api/src/test/java/net/shibboleth/idp/profile/RequestContextBuilder.java b/idp-profile-api/src/test/java/net/shibboleth/idp/profile/RequestContextBuilder.java
index 5a425cd..076d897 100644
--- a/idp-profile-api/src/test/java/net/shibboleth/idp/profile/RequestContextBuilder.java
+++ b/idp-profile-api/src/test/java/net/shibboleth/idp/profile/RequestContextBuilder.java
@@ -19,6 +19,7 @@ package net.shibboleth.idp.profile;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list