This is an automated email from the ASF dual-hosted git repository.

mcgilman pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 141ca71cbc NIFI-13622 Corrected Logout Complete Redirect for Proxies 
(#9143)
141ca71cbc is described below

commit 141ca71cbc21321f9e0cf07d99340f9eaee08ab0
Author: David Handermann <[email protected]>
AuthorDate: Fri Aug 2 15:40:47 2024 -0500

    NIFI-13622 Corrected Logout Complete Redirect for Proxies (#9143)
    
    This closes #9143
---
 .../nifi-framework/nifi-web/nifi-jetty/pom.xml     |   5 +
 .../org/apache/nifi/web/server/JettyServer.java    |  37 +++++---
 .../filter/LogoutCompleteRedirectFilter.java       |  64 +++++++++++++
 .../filter/LogoutCompleteRedirectFilterTest.java   | 105 +++++++++++++++++++++
 4 files changed, 196 insertions(+), 15 deletions(-)

diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml
index 79ae89a85d..773a84a217 100644
--- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml
+++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml
@@ -85,6 +85,11 @@
             <artifactId>nifi-web-security</artifactId>
             <version>2.0.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-web-servlet-shared</artifactId>
+            <version>2.0.0-SNAPSHOT</version>
+        </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-web</artifactId>
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
index 917dd23e1c..e524144067 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
@@ -100,6 +100,7 @@ import org.apache.nifi.web.NiFiWebConfigurationContext;
 import org.apache.nifi.web.UiExtensionType;
 import org.apache.nifi.web.server.connector.FrameworkServerConnectorFactory;
 import org.apache.nifi.web.server.filter.FilterParameter;
+import org.apache.nifi.web.server.filter.LogoutCompleteRedirectFilter;
 import org.apache.nifi.web.server.filter.RequestFilterProvider;
 import org.apache.nifi.web.server.filter.RestApiRequestFilterProvider;
 import org.apache.nifi.web.server.filter.StandardRequestFilterProvider;
@@ -226,21 +227,8 @@ public class JettyServer implements NiFiServer, 
ExtensionUiLoader {
         configureConnectors(server);
 
         final ContextHandlerCollection handlerCollection = new 
ContextHandlerCollection();
-
-        // Only restrict the host header if running in HTTPS mode
-        if (props.isHTTPSConfigured()) {
-            final HostHeaderHandler hostHeaderHandler = new 
HostHeaderHandler(props);
-            handlerCollection.addHandler(hostHeaderHandler);
-        }
-
-        final Handler warHandlers = loadInitialWars(bundles);
-        handlerCollection.addHandler(warHandlers);
-
-        final RewriteHandler logoutCompleteRewriteHandler = new 
RewriteHandler();
-        final RedirectPatternRule redirectLogoutComplete = new 
RedirectPatternRule("/nifi/logout-complete", "/nifi/#/logout-complete");
-        logoutCompleteRewriteHandler.addRule(redirectLogoutComplete);
-        logoutCompleteRewriteHandler.setHandler(handlerCollection);
-        server.setHandler(logoutCompleteRewriteHandler);
+        final Handler standardHandler = getStandardHandler(handlerCollection);
+        server.setHandler(standardHandler);
 
         final RewriteHandler defaultRewriteHandler = new RewriteHandler();
         final RedirectPatternRule redirectDefault = new 
RedirectPatternRule("/*", "/nifi");
@@ -256,6 +244,18 @@ public class JettyServer implements NiFiServer, 
ExtensionUiLoader {
         server.setRequestLog(requestLog);
     }
 
+    private Handler getStandardHandler(final ContextHandlerCollection 
handlerCollection) {
+        // Only restrict the host header if running in HTTPS mode
+        if (props.isHTTPSConfigured()) {
+            final HostHeaderHandler hostHeaderHandler = new 
HostHeaderHandler(props);
+            handlerCollection.addHandler(hostHeaderHandler);
+        }
+
+        final Handler warHandlers = loadInitialWars(bundles);
+        handlerCollection.addHandler(warHandlers);
+        return handlerCollection;
+    }
+
     private Handler loadInitialWars(final Set<Bundle> bundles) {
         final Map<File, Bundle> warToBundleLookup = findWars(bundles);
 
@@ -644,6 +644,13 @@ public class JettyServer implements NiFiServer, 
ExtensionUiLoader {
                 ? REST_API_REQUEST_FILTER_PROVIDER.getFilters(props)
                 : REQUEST_FILTER_PROVIDER.getFilters(props);
 
+        // Add Logout Complete Filter for web user interface integration
+        if (CONTEXT_PATH_NIFI.equals(contextPath)) {
+            final FilterHolder logoutCompleteFilterHolder = new 
FilterHolder(LogoutCompleteRedirectFilter.class);
+            
logoutCompleteFilterHolder.setName(LogoutCompleteRedirectFilter.class.getSimpleName());
+            requestFilters.add(logoutCompleteFilterHolder);
+        }
+
         requestFilters.forEach(filter -> {
             final String pathSpecification = 
filter.getInitParameter(FilterParameter.PATH_SPECIFICATION.name());
             final String filterPathSpecification = pathSpecification == null ? 
CONTEXT_PATH_ALL : pathSpecification;
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/LogoutCompleteRedirectFilter.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/LogoutCompleteRedirectFilter.java
new file mode 100644
index 0000000000..852c3c81a5
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/LogoutCompleteRedirectFilter.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.apache.nifi.web.server.filter;
+
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.nifi.web.servlet.shared.RequestUriBuilder;
+
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * Logout Complete Redirect Filter for web user interface integration with 
fragment-based routing
+ */
+public class LogoutCompleteRedirectFilter implements Filter {
+    private static final String LOGOUT_COMPLETE_PATH = "/nifi/logout-complete";
+
+    private static final String USER_INTERFACE_PATH = "/nifi/";
+
+    private static final String FRAGMENT_PATH = "/logout-complete";
+
+    @Override
+    public void doFilter(final ServletRequest servletRequest, final 
ServletResponse servletResponse, final FilterChain filterChain) throws 
IOException, ServletException {
+        if (servletRequest instanceof HttpServletRequest httpServletRequest) {
+            final String requestUri = httpServletRequest.getRequestURI();
+            if (requestUri.endsWith(LOGOUT_COMPLETE_PATH)) {
+                final HttpServletResponse httpServletResponse = 
(HttpServletResponse) servletResponse;
+                doRedirect(httpServletRequest, httpServletResponse);
+            } else {
+                filterChain.doFilter(servletRequest, servletResponse);
+            }
+        } else {
+            filterChain.doFilter(servletRequest, servletResponse);
+        }
+    }
+
+    private void doRedirect(final HttpServletRequest httpServletRequest, final 
HttpServletResponse httpServletResponse) throws IOException {
+        final URI redirectUri = 
RequestUriBuilder.fromHttpServletRequest(httpServletRequest)
+                .path(USER_INTERFACE_PATH)
+                .fragment(FRAGMENT_PATH)
+                .build();
+        final String redirectLocation = redirectUri.toString();
+        httpServletResponse.sendRedirect(redirectLocation);
+    }
+}
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/LogoutCompleteRedirectFilterTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/LogoutCompleteRedirectFilterTest.java
new file mode 100644
index 0000000000..c75b522967
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/LogoutCompleteRedirectFilterTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.apache.nifi.web.server.filter;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class LogoutCompleteRedirectFilterTest {
+    private static final String LOGOUT_COMPLETE_URI = "/nifi/logout-complete";
+
+    private static final String ROOT_URI = "/nifi/";
+
+    private static final String ALLOWED_CONTEXT_PATHS_PARAMETER = 
"allowedContextPaths";
+
+    private static final String FORWARDED_PATH = "/forwarded";
+
+    private static final String LOGOUT_COMPLETE_EXPECTED = 
"/forwarded/nifi/#/logout-complete";
+
+    private static final String CONTEXT_PATH_HEADER = "X-ProxyContextPath";
+
+    @Mock
+    private ServletContext servletContext;
+
+    @Mock
+    private FilterConfig filterConfig;
+
+    @Mock
+    private FilterChain filterChain;
+
+    @Mock(strictness = Mock.Strictness.LENIENT)
+    private HttpServletRequest request;
+
+    @Mock
+    private HttpServletResponse response;
+
+    @Captor
+    private ArgumentCaptor<String> redirectCaptor;
+
+    private LogoutCompleteRedirectFilter filter;
+
+    @BeforeEach
+    public void setFilter() throws ServletException {
+        filter = new LogoutCompleteRedirectFilter();
+        filter.init(filterConfig);
+    }
+
+    @Test
+    public void testDoFilter() throws ServletException, IOException {
+        when(request.getRequestURI()).thenReturn(ROOT_URI);
+
+        filter.doFilter(request, response, filterChain);
+
+        verify(response, never()).sendRedirect(anyString());
+    }
+
+    @Test
+    public void testDoFilterLogoutComplete() throws ServletException, 
IOException {
+        when(request.getRequestURI()).thenReturn(LOGOUT_COMPLETE_URI);
+        when(request.getServletContext()).thenReturn(servletContext);
+        
when(servletContext.getInitParameter(eq(ALLOWED_CONTEXT_PATHS_PARAMETER))).thenReturn(FORWARDED_PATH);
+        
when(request.getHeader(eq(CONTEXT_PATH_HEADER))).thenReturn(FORWARDED_PATH);
+
+        filter.doFilter(request, response, filterChain);
+
+        verify(response).sendRedirect(redirectCaptor.capture());
+
+        final String redirect = redirectCaptor.getValue();
+        assertEquals(LOGOUT_COMPLETE_EXPECTED, redirect);
+    }
+}

Reply via email to