[java-identity-provider] branch master updated: IDP-1469 - External intercept flow

Scott Cantor cantor.2 at osu.edu
Thu Jun 20 19:39:47 EDT 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=682cc69aa828d11c7c4ee7f764781005e6fcee5a

The following commit(s) were added to refs/heads/master by this push:
       new  682cc69   IDP-1469 - External intercept flow
682cc69 is described below

commit 682cc69aa828d11c7c4ee7f764781005e6fcee5a
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu Jun 20 19:37:20 2019 -0400

    IDP-1469 - External intercept flow
    
    https://issues.shibboleth.net/jira/browse/IDP-1469
---
 .../conf/intercept/external-intercept-config.xml   |  25 +++
 .../resources/conf/intercept/profile-intercept.xml |   2 +
 .../main/resources/system/conf/webflow-config.xml  |   1 +
 .../system/flows/intercept/external-beans.xml      |  27 +++
 .../system/flows/intercept/external-flow.xml       |  21 +++
 .../context/ExternalInterceptorContext.java        |  92 ++++++++++
 .../profile/interceptor/ExternalInterceptor.java   | 186 +++++++++++++++++++++
 .../interceptor/ExternalInterceptorException.java  |  66 ++++++++
 .../interceptor/impl/ExternalInterceptorImpl.java  |  97 +++++++++++
 9 files changed, 517 insertions(+)

diff --git a/idp-conf/src/main/resources/conf/intercept/external-intercept-config.xml b/idp-conf/src/main/resources/conf/intercept/external-intercept-config.xml
new file mode 100644
index 0000000..1d0fc29
--- /dev/null
+++ b/idp-conf/src/main/resources/conf/intercept/external-intercept-config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:c="http://www.springframework.org/schema/c"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+                           
+       default-init-method="initialize"
+       default-destroy-method="destroy">
+
+    <!-- Servlet context-relative path to wherever your implementation lives. -->
+    <bean id="shibboleth.intercept.externalPath" class="java.lang.String"
+        c:_0="contextRelative:intercept.jsp" />
+
+    <!--
+    Default is to always use the path in the bean above. If you want to determine it
+    dynamically, define a bean called "shibboleth.intercept.externalPathStrategy"
+    of type Function<ProfileRequestContext,String> that returns the path to use.
+    -->
+    
+</beans>
diff --git a/idp-conf/src/main/resources/conf/intercept/profile-intercept.xml b/idp-conf/src/main/resources/conf/intercept/profile-intercept.xml
index 7b4c8aa..f086cfa 100644
--- a/idp-conf/src/main/resources/conf/intercept/profile-intercept.xml
+++ b/idp-conf/src/main/resources/conf/intercept/profile-intercept.xml
@@ -33,6 +33,8 @@
                 <bean id="intercept/attribute-release" parent="shibboleth.consent.AttributeReleaseFlow" />
                 
                 <bean id="intercept/impersonate" parent="shibboleth.InterceptFlow" />
+                
+                <bean id="intercept/external" parent="shibboleth.InterceptFlow" />
             </list>
         </property>
     </bean>
diff --git a/idp-conf/src/main/resources/system/conf/webflow-config.xml b/idp-conf/src/main/resources/system/conf/webflow-config.xml
index 6b6a4b1..66ffbf7 100644
--- a/idp-conf/src/main/resources/system/conf/webflow-config.xml
+++ b/idp-conf/src/main/resources/system/conf/webflow-config.xml
@@ -123,6 +123,7 @@
                 <entry key="intercept/attribute-release" value="../system/flows/intercept/attribute-release-flow.xml" />
                 <entry key="intercept/attribute-release-query" value="../system/flows/intercept/attribute-release-query-flow.xml" />
                 <entry key="intercept/impersonate" value="../system/flows/intercept/impersonate-flow.xml" />
+                <entry key="intercept/external" value="../system/flows/intercept/external-flow.xml" />
         
                 <!-- Inbound security processing of SAML messages, implemented as intercept flows. -->
                 <entry key="security-policy.abstract" value="../system/flows/saml/security-abstract-flow.xml" />
diff --git a/idp-conf/src/main/resources/system/flows/intercept/external-beans.xml b/idp-conf/src/main/resources/system/flows/intercept/external-beans.xml
new file mode 100644
index 0000000..ebe1d98
--- /dev/null
+++ b/idp-conf/src/main/resources/system/flows/intercept/external-beans.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:c="http://www.springframework.org/schema/c"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+                           
+       default-init-method="initialize"
+       default-destroy-method="destroy">
+       
+    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
+        p:placeholderPrefix="%{" p:placeholderSuffix="}" />
+
+    <bean class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />
+    <bean class="net.shibboleth.idp.profile.impl.ProfileActionBeanPostProcessor" />
+
+    <!-- Default strategy function to obtain the external path. -->
+    <bean id="shibboleth.intercept.externalPathStrategy" parent="shibboleth.Functions.Constant"
+        c:target-ref="shibboleth.intercept.externalPath" />
+
+    <import resource="../../../conf/intercept/external-intercept-config.xml" />
+
+</beans>
diff --git a/idp-conf/src/main/resources/system/flows/intercept/external-flow.xml b/idp-conf/src/main/resources/system/flows/intercept/external-flow.xml
new file mode 100644
index 0000000..49ff7fc
--- /dev/null
+++ b/idp-conf/src/main/resources/system/flows/intercept/external-flow.xml
@@ -0,0 +1,21 @@
+<flow xmlns="http://www.springframework.org/schema/webflow"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
+      parent="intercept.abstract">
+
+    <!-- Rudimentary impediment to direct execution of subflow. -->
+    <input name="calledAsSubflow" type="boolean" required="true" />
+
+    <!-- This is an interceptor flow handled outside the webflow engine. -->
+    
+    <view-state id="ExternalTransfer" view="externalRedirect:#{T(net.shibboleth.idp.profile.interceptor.ExternalInterceptor).getExternalRedirect(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.intercept.externalPathStrategy').apply(opensamlProfileRequestContext), flowExecutionContext.getKey().toString())}">
+        <on-render>
+            <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.profile.context.ProfileInterceptorContext)).addSubcontext(new net.shibboleth.idp.profile.context.ExternalInterceptorContext(), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
+            <evaluate expression="externalContext.getNativeRequest().getSession().setAttribute('conversation' + flowExecutionContext.getKey().toString(), new net.shibboleth.idp.profile.interceptor.impl.ExternalInterceptorImpl(opensamlProfileRequestContext))" />
+        </on-render>
+        <transition to="#{opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.profile.context.ProfileInterceptorContext)).getSubcontext(T(net.shibboleth.idp.profile.context.ExternalInterceptorContext)).getEventId()}" />
+    </view-state>
+    
+    <bean-import resource="external-beans.xml" />
+
+</flow>
diff --git a/idp-profile-api/src/main/java/net/shibboleth/idp/profile/context/ExternalInterceptorContext.java b/idp-profile-api/src/main/java/net/shibboleth/idp/profile/context/ExternalInterceptorContext.java
new file mode 100644
index 0000000..409522f
--- /dev/null
+++ b/idp-profile-api/src/main/java/net/shibboleth/idp/profile/context/ExternalInterceptorContext.java
@@ -0,0 +1,92 @@
+/*
+ * 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.context;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.BaseContext;
+import org.opensaml.profile.action.EventIds;
+
+/**
+ * A context representing the state of an externalized interceptor flow.
+ * 
+ * @parent {@link ProfileInterceptorContext}
+ * @added Before dispatching control to an external interceptor flow
+ * 
+ * @since 4.0.0
+ */
+public final class ExternalInterceptorContext extends BaseContext {
+    
+    /** Value of flowExecutionUrl on branching from flow. */
+    @Nullable private String flowExecutionUrl;
+
+    /** Event to signal. */
+    @Nullable private String eventId;
+    
+    /** Constructor. */
+    public ExternalInterceptorContext() {
+        eventId = EventIds.PROCEED_EVENT_ID;
+    }
+    
+    /**
+     * Get the flow execution URL to return control to.
+     * 
+     * @return return location
+     */
+    @Nullable public String getFlowExecutionUrl() {
+        return flowExecutionUrl;
+    }
+    
+    /**
+     * 
+     * Set the flow execution URL to return control to.
+     * 
+     * @param url   return location
+     * 
+     * @return this context
+     */
+    @Nonnull public ExternalInterceptorContext setFlowExecutionUrl(@Nullable final String url) {
+        flowExecutionUrl = url;
+        
+        return this;
+    }
+
+    /**
+     * Get the event ID to signal as the result of this flow.
+     * 
+     * @return event ID
+     */
+    @Nullable public String getEventId() {
+        return eventId;
+    }
+
+    /**
+     * Set the event ID to signal as the result of this flow.
+     * 
+     * @param id event ID
+     * 
+     * @return this context
+     */
+    @Nonnull public ExternalInterceptorContext setEventId(@Nullable final String id) {
+        eventId = id;
+        
+        return this;
+    }
+
+}
\ No newline at end of file
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..4847dcd
--- /dev/null
+++ b/idp-profile-api/src/main/java/net/shibboleth/idp/profile/interceptor/ExternalInterceptor.java
@@ -0,0 +1,186 @@
+/*
+ * 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 com.google.common.net.UrlEscapers;
+
+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 class ExternalInterceptor {
+
+    /** Parameter supplied to identify the per-conversation structure in the session. */
+    @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 conv = request.getParameter(CONVERSATION_KEY);
+        if (conv == null || conv.isEmpty()) {
+            throw new ExternalInterceptorException("No conversation key found in request");
+        }
+        
+        final Object obj = request.getSession().getAttribute(CONVERSATION_KEY + conv);
+        if (obj == null || !(obj instanceof ExternalInterceptor)) {
+            throw new ExternalInterceptorException("No conversation state found in session for key (" + conv + ")");
+        }
+        
+        ((ExternalInterceptor) obj).doStart(request);
+        return conv;
+    }
+    
+    /**
+     * 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 Object obj = request.getSession().getAttribute(CONVERSATION_KEY + key);
+        if (obj == null || !(obj instanceof ExternalInterceptor)) {
+            throw new ExternalInterceptorException("No conversation state found in session for key (" + key + ")");
+        }
+        
+        request.getSession().removeAttribute(CONVERSATION_KEY + key);
+        
+        ((ExternalInterceptor) obj).doFinish(request, response);
+    }
+
+    /**
+     * Get the {@link ProfileRequestContext} associated with a request.
+     * 
+     * @param key   the value returned by {@link #startExternalAuthentication(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.getSession().getAttribute(CONVERSATION_KEY + key);
+        if (obj == null || !(obj instanceof ExternalInterceptor)) {
+            throw new ExternalInterceptorException("No conversation state found in session");
+        }
+        
+        return ((ExternalInterceptor) obj).getProfileRequestContext(request);
+    }
+    
+    /**
+     * 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
+     * 
+     * @throws ExternalInterceptorException if an error occurs
+     */
+    protected void doStart(@Nonnull final HttpServletRequest request) throws ExternalInterceptorException {
+        throw new ExternalInterceptorException("Not implemented");
+    }
+
+    /**
+     * 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
+     * 
+     * @throws ExternalInterceptorException if an error occurs
+     * @throws IOException if the redirect cannot be issued
+     */
+    protected void doFinish(@Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response)
+            throws ExternalInterceptorException, IOException {
+        throw new ExternalInterceptorException("Not implemented");
+    }
+    
+    /**
+     * Get the {@link ProfileRequestContext} associated with a request.
+     * 
+     * @param request servlet request
+     * 
+     * @return the profile request context
+     * @throws ExternalInterceptorException if an error occurs
+     */
+    @Nonnull protected ProfileRequestContext getProfileRequestContext(@Nonnull final HttpServletRequest request)
+            throws ExternalInterceptorException {
+        throw new ExternalInterceptorException("Not implemented");
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-profile-api/src/main/java/net/shibboleth/idp/profile/interceptor/ExternalInterceptorException.java b/idp-profile-api/src/main/java/net/shibboleth/idp/profile/interceptor/ExternalInterceptorException.java
new file mode 100644
index 0000000..329e60c
--- /dev/null
+++ b/idp-profile-api/src/main/java/net/shibboleth/idp/profile/interceptor/ExternalInterceptorException.java
@@ -0,0 +1,66 @@
+/*
+ * 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 javax.annotation.Nullable;
+
+/**
+ * Exception indicating a problem with the external interceptor process.
+ * 
+ * @since 4.0.0
+ */
+public class ExternalInterceptorException extends Exception {
+
+    /** Generated serial UID. */
+    private static final long serialVersionUID = 2344188427329594209L;
+
+    /** Constructor. */
+    public ExternalInterceptorException() {
+        
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param message exception message
+     */
+    public ExternalInterceptorException(@Nullable final String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param wrappedException exception to be wrapped by this one
+     */
+    public ExternalInterceptorException(@Nullable final Exception wrappedException) {
+        super(wrappedException);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param message exception message
+     * @param wrappedException exception to be wrapped by this one
+     */
+    public ExternalInterceptorException(@Nullable final String message,
+            @Nullable final Exception wrappedException) {
+        super(message, wrappedException);
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-profile-impl/src/main/java/net/shibboleth/idp/profile/interceptor/impl/ExternalInterceptorImpl.java b/idp-profile-impl/src/main/java/net/shibboleth/idp/profile/interceptor/impl/ExternalInterceptorImpl.java
new file mode 100644
index 0000000..68ae604
--- /dev/null
+++ b/idp-profile-impl/src/main/java/net/shibboleth/idp/profile/interceptor/impl/ExternalInterceptorImpl.java
@@ -0,0 +1,97 @@
+/*
+ * 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.impl;
+
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.profile.context.ExternalInterceptorContext;
+import net.shibboleth.idp.profile.context.ProfileInterceptorContext;
+import net.shibboleth.idp.profile.interceptor.ExternalInterceptor;
+import net.shibboleth.idp.profile.interceptor.ExternalInterceptorException;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Implementation of the {@link ExternalInterceptor} API that handles moving information in and out
+ * of request attributes.
+ * 
+ * @since 4.0.0
+ */
+public class ExternalInterceptorImpl extends ExternalInterceptor {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ExternalInterceptorImpl.class);
+    
+    /** State of request to pull from. */
+    @Nonnull private final ProfileRequestContext profileRequestContext;
+
+    /**
+     * Constructor.
+     * 
+     * @param input profile request context to expose
+     */
+    public ExternalInterceptorImpl(@Nonnull final ProfileRequestContext input) {
+        profileRequestContext = Constraint.isNotNull(input, "ProfileRequestContext cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doStart(@Nonnull final HttpServletRequest request) {
+        request.setAttribute(ProfileRequestContext.BINDING_KEY, profileRequestContext);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doFinish(@Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response)
+            throws IOException, 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");
+        } else if (extContext.getFlowExecutionUrl() == null) {
+            throw new ExternalInterceptorException("No flow execution URL found to return control");
+        }
+        
+        final Object attr = request.getAttribute(EVENT_KEY);
+        if (attr != null && attr instanceof String) {
+            extContext.setEventId((String) attr);
+        }
+                
+        response.sendRedirect(extContext.getFlowExecutionUrl());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected ProfileRequestContext getProfileRequestContext(@Nonnull final HttpServletRequest request)
+            throws ExternalInterceptorException {
+        return profileRequestContext;
+    }
+    
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list