QPID-8552: [Broker-J] Respond with forbidden error when request is made using unsupported method
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
index 239d269..dcc58f1 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
@@ -83,9 +83,9 @@
import org.apache.qpid.server.logging.messages.PortMessages;
import org.apache.qpid.server.management.plugin.filter.AuthenticationCheckFilter;
import org.apache.qpid.server.management.plugin.filter.ExceptionHandlingFilter;
-import org.apache.qpid.server.management.plugin.filter.ForbiddingTraceFilter;
import org.apache.qpid.server.management.plugin.filter.InteractiveAuthenticationFilter;
import org.apache.qpid.server.management.plugin.filter.LoggingFilter;
+import org.apache.qpid.server.management.plugin.filter.MethodFilter;
import org.apache.qpid.server.management.plugin.filter.RedirectFilter;
import org.apache.qpid.server.management.plugin.filter.RewriteRequestForUncompressedJavascript;
import org.apache.qpid.server.management.plugin.servlet.FileServlet;
@@ -348,7 +348,7 @@
corsFilter.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, String.valueOf(getCorsAllowCredentials()));
root.addFilter(corsFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
- root.addFilter(new FilterHolder(new ForbiddingTraceFilter()), "/*", EnumSet.of(DispatcherType.REQUEST));
+ root.addFilter(new FilterHolder(new MethodFilter()), "/*", EnumSet.of(DispatcherType.REQUEST));
addFiltersAndServletsForRest(root);
if (!Boolean.TRUE.equals(getContextValue(Boolean.class, DISABLE_UI_CONTEXT_NAME)))
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/filter/ForbiddingTraceFilter.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/filter/ForbiddingTraceFilter.java
deleted file mode 100644
index c35b0df..0000000
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/filter/ForbiddingTraceFilter.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- *
- * 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.qpid.server.management.plugin.filter;
-
-import java.io.IOException;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- *
- * This {@link Filter} blocks HTTP TRACE commands from being
- * processed. All TRACE requests are sent a 403 error.
- *
- */
-public class ForbiddingTraceFilter implements Filter
-{
- private static final String METHOD_TRACE = "TRACE";
-
- @Override
- public void destroy()
- {
- }
-
- @Override
- public void init(FilterConfig config) throws ServletException
- {
- }
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
- ServletException
- {
- HttpServletRequest httpRequest = (HttpServletRequest) request;
- HttpServletResponse httpResponse = (HttpServletResponse) response;
- if (httpRequest.getMethod().equals(METHOD_TRACE))
- {
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
- return;
- }
- chain.doFilter(request, response);
- }
-
-}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/filter/MethodFilter.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/filter/MethodFilter.java
new file mode 100644
index 0000000..8e8c577
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/filter/MethodFilter.java
@@ -0,0 +1,81 @@
+/*
+ *
+ * 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.qpid.server.management.plugin.filter;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.qpid.server.management.plugin.HttpManagementConfiguration;
+import org.apache.qpid.server.management.plugin.HttpManagementUtil;
+
+public class MethodFilter implements Filter
+{
+ private static final Set<String> REST_API_METHODS = new HashSet<>(Arrays.asList("GET", "POST", "PUT", "DELETE"));
+ private HttpManagementConfiguration<?> _managementConfiguration;
+
+ @Override
+ public void init(final FilterConfig filterConfig) throws ServletException
+ {
+ _managementConfiguration = HttpManagementUtil.getManagementConfiguration(filterConfig.getServletContext());
+ }
+
+ @Override
+ public void destroy()
+ {
+
+ }
+
+ @Override
+ public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
+ throws IOException, ServletException
+ {
+ final HttpServletRequest httpRequest = (HttpServletRequest) request;
+ final HttpServletResponse httpResponse = (HttpServletResponse) response;
+ final String method = String.valueOf(httpRequest.getMethod()).toUpperCase();
+
+ if (REST_API_METHODS.contains(method) || isCorsAllowedMethod(method))
+ {
+ chain.doFilter(request, response);
+ }
+ else
+ {
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
+ }
+ }
+
+ private boolean isCorsAllowedMethod(final String method)
+ {
+ return _managementConfiguration.getCorsAllowMethods()
+ .stream()
+ .anyMatch(m -> m.equalsIgnoreCase(method));
+ }
+}
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/filter/MethodFilterTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/filter/MethodFilterTest.java
new file mode 100644
index 0000000..83eb6fc
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/filter/MethodFilterTest.java
@@ -0,0 +1,92 @@
+/*
+ *
+ * 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.qpid.server.management.plugin.filter;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.HttpManagement;
+import org.apache.qpid.server.management.plugin.HttpManagementConfiguration;
+import org.apache.qpid.server.management.plugin.HttpManagementUtil;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class MethodFilterTest extends UnitTestBase
+{
+ private MethodFilter _methodFilter;
+
+ @Before
+ public void setUp() throws Exception
+ {
+ final HttpManagementConfiguration httManagement = mock(HttpManagement.class);
+ when(httManagement.getCorsAllowMethods()).thenReturn(Collections.singleton("HeAd"));
+
+ final ServletContext servletContext = mock(ServletContext.class);
+ when(servletContext.getAttribute(HttpManagementUtil.ATTR_MANAGEMENT_CONFIGURATION)).thenReturn(httManagement);
+ FilterConfig filterConfig = mock(FilterConfig.class);
+ when(filterConfig.getServletContext()).thenReturn(servletContext);
+
+ _methodFilter = new MethodFilter();
+ _methodFilter.init(filterConfig);
+ }
+
+ @Test
+ public void testDoFilterWhenMethodAllowed() throws Exception
+ {
+ final String[] allowedMethods = {"get", "post", "put", "delete", "head"};
+ for (String method : allowedMethods)
+ {
+ final FilterChain chain = mock(FilterChain.class);
+ final HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.getMethod()).thenReturn(method);
+ final HttpServletResponse response = mock(HttpServletResponse.class);
+ _methodFilter.doFilter(request, response, chain);
+ verify(chain).doFilter(request, response);
+ }
+ }
+
+ @Test
+ public void testDoFilterWhenMethodForbidden() throws Exception
+ {
+
+ final String[] forbiddenMethods = {"option", "trace", "patch", "connect", "foo"};
+ for (String method : forbiddenMethods)
+ {
+ final FilterChain chain = mock(FilterChain.class);
+ final HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.getMethod()).thenReturn(method);
+ final HttpServletResponse response = mock(HttpServletResponse.class);
+ _methodFilter.doFilter(request, response, chain);
+ verify(response).sendError(HttpServletResponse.SC_FORBIDDEN);
+ }
+ }
+}
\ No newline at end of file