You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by je...@apache.org on 2015/10/23 17:34:38 UTC
hadoop git commit: YARN-4009. CORS support for ResourceManager REST
API. ( Varun Vasudev via jeagles)
Repository: hadoop
Updated Branches:
refs/heads/trunk 35a303dfb -> f8adeb712
YARN-4009. CORS support for ResourceManager REST API. ( Varun Vasudev via jeagles)
Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/f8adeb71
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/f8adeb71
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/f8adeb71
Branch: refs/heads/trunk
Commit: f8adeb712dc834c27cec15c04a986f2f635aba83
Parents: 35a303d
Author: Jonathan Eagles <je...@yahoo-inc.com>
Authored: Fri Oct 23 10:34:08 2015 -0500
Committer: Jonathan Eagles <je...@yahoo-inc.com>
Committed: Fri Oct 23 10:34:08 2015 -0500
----------------------------------------------------------------------
.../HttpCrossOriginFilterInitializer.java | 74 +++++
.../hadoop/security/http/CrossOriginFilter.java | 259 +++++++++++++++
.../src/main/resources/core-default.xml | 36 ++
.../src/site/markdown/HttpAuthentication.md | 14 +
.../TestHttpCrossOriginFilterInitializer.java | 58 ++++
.../security/http/TestCrossOriginFilter.java | 330 +++++++++++++++++++
hadoop-yarn-project/CHANGES.txt | 2 +
.../hadoop/yarn/conf/YarnConfiguration.java | 10 +
.../src/main/resources/yarn-default.xml | 16 +
.../ApplicationHistoryServer.java | 14 +-
.../timeline/webapp/CrossOriginFilter.java | 259 ---------------
.../webapp/CrossOriginFilterInitializer.java | 40 +--
.../timeline/webapp/TestCrossOriginFilter.java | 324 ------------------
.../TestCrossOriginFilterInitializer.java | 57 ----
.../hadoop-yarn-server-common/pom.xml | 5 +
.../server/nodemanager/webapp/WebServer.java | 10 +-
.../server/resourcemanager/ResourceManager.java | 14 +-
.../src/site/markdown/NodeManagerRest.md | 10 +-
.../src/site/markdown/ResourceManagerRest.md | 8 +
19 files changed, 871 insertions(+), 669 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/HttpCrossOriginFilterInitializer.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/HttpCrossOriginFilterInitializer.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/HttpCrossOriginFilterInitializer.java
new file mode 100644
index 0000000..f9c1816
--- /dev/null
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/HttpCrossOriginFilterInitializer.java
@@ -0,0 +1,74 @@
+/**
+ * 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.hadoop.security;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.http.FilterContainer;
+import org.apache.hadoop.http.FilterInitializer;
+import org.apache.hadoop.security.http.CrossOriginFilter;
+
+public class HttpCrossOriginFilterInitializer extends FilterInitializer {
+
+ public static final String PREFIX = "hadoop.http.cross-origin.";
+ public static final String ENABLED_SUFFIX = "enabled";
+
+ private static final Log LOG =
+ LogFactory.getLog(HttpCrossOriginFilterInitializer.class);
+
+ @Override
+ public void initFilter(FilterContainer container, Configuration conf) {
+
+ String key = getEnabledConfigKey();
+ boolean enabled = conf.getBoolean(key, false);
+ if (enabled) {
+ container.addGlobalFilter("Cross Origin Filter",
+ CrossOriginFilter.class.getName(),
+ getFilterParameters(conf, getPrefix()));
+ } else {
+ LOG.info("CORS filter not enabled. Please set " + key
+ + " to 'true' to enable it");
+ }
+ }
+
+ protected static Map<String, String> getFilterParameters(Configuration conf,
+ String prefix) {
+ Map<String, String> filterParams = new HashMap<String, String>();
+ for (Map.Entry<String, String> entry : conf.getValByRegex(prefix)
+ .entrySet()) {
+ String name = entry.getKey();
+ String value = entry.getValue();
+ name = name.substring(prefix.length());
+ filterParams.put(name, value);
+ }
+ return filterParams;
+ }
+
+ protected String getPrefix() {
+ return HttpCrossOriginFilterInitializer.PREFIX;
+ }
+
+ protected String getEnabledConfigKey() {
+ return getPrefix() + HttpCrossOriginFilterInitializer.ENABLED_SUFFIX;
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http/CrossOriginFilter.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http/CrossOriginFilter.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http/CrossOriginFilter.java
new file mode 100644
index 0000000..ea78762
--- /dev/null
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http/CrossOriginFilter.java
@@ -0,0 +1,259 @@
+/**
+ * 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.hadoop.security.http;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+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.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+
+public class CrossOriginFilter implements Filter {
+
+ private static final Log LOG = LogFactory.getLog(CrossOriginFilter.class);
+
+ // HTTP CORS Request Headers
+ static final String ORIGIN = "Origin";
+ static final String ACCESS_CONTROL_REQUEST_METHOD =
+ "Access-Control-Request-Method";
+ static final String ACCESS_CONTROL_REQUEST_HEADERS =
+ "Access-Control-Request-Headers";
+
+ // HTTP CORS Response Headers
+ static final String ACCESS_CONTROL_ALLOW_ORIGIN =
+ "Access-Control-Allow-Origin";
+ static final String ACCESS_CONTROL_ALLOW_CREDENTIALS =
+ "Access-Control-Allow-Credentials";
+ static final String ACCESS_CONTROL_ALLOW_METHODS =
+ "Access-Control-Allow-Methods";
+ static final String ACCESS_CONTROL_ALLOW_HEADERS =
+ "Access-Control-Allow-Headers";
+ static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
+
+ // Filter configuration
+ public static final String ALLOWED_ORIGINS = "allowed-origins";
+ public static final String ALLOWED_ORIGINS_DEFAULT = "*";
+ public static final String ALLOWED_METHODS = "allowed-methods";
+ public static final String ALLOWED_METHODS_DEFAULT = "GET,POST,HEAD";
+ public static final String ALLOWED_HEADERS = "allowed-headers";
+ public static final String ALLOWED_HEADERS_DEFAULT =
+ "X-Requested-With,Content-Type,Accept,Origin";
+ public static final String MAX_AGE = "max-age";
+ public static final String MAX_AGE_DEFAULT = "1800";
+
+ private List<String> allowedMethods = new ArrayList<String>();
+ private List<String> allowedHeaders = new ArrayList<String>();
+ private List<String> allowedOrigins = new ArrayList<String>();
+ private boolean allowAllOrigins = true;
+ private String maxAge;
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ initializeAllowedMethods(filterConfig);
+ initializeAllowedHeaders(filterConfig);
+ initializeAllowedOrigins(filterConfig);
+ initializeMaxAge(filterConfig);
+ }
+
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res,
+ FilterChain chain)
+ throws IOException, ServletException {
+ doCrossFilter((HttpServletRequest) req, (HttpServletResponse) res);
+ chain.doFilter(req, res);
+ }
+
+ @Override
+ public void destroy() {
+ allowedMethods.clear();
+ allowedHeaders.clear();
+ allowedOrigins.clear();
+ }
+
+ private void doCrossFilter(HttpServletRequest req, HttpServletResponse res) {
+
+ String originsList = encodeHeader(req.getHeader(ORIGIN));
+ if (!isCrossOrigin(originsList)) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Header origin is null. Returning");
+ }
+ return;
+ }
+
+ if (!areOriginsAllowed(originsList)) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Header origins '" + originsList + "' not allowed. Returning");
+ }
+ return;
+ }
+
+ String accessControlRequestMethod =
+ req.getHeader(ACCESS_CONTROL_REQUEST_METHOD);
+ if (!isMethodAllowed(accessControlRequestMethod)) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Access control method '" + accessControlRequestMethod +
+ "' not allowed. Returning");
+ }
+ return;
+ }
+
+ String accessControlRequestHeaders =
+ req.getHeader(ACCESS_CONTROL_REQUEST_HEADERS);
+ if (!areHeadersAllowed(accessControlRequestHeaders)) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Access control headers '" + accessControlRequestHeaders +
+ "' not allowed. Returning");
+ }
+ return;
+ }
+
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Completed cross origin filter checks. Populating " +
+ "HttpServletResponse");
+ }
+ res.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, originsList);
+ res.setHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.TRUE.toString());
+ res.setHeader(ACCESS_CONTROL_ALLOW_METHODS, getAllowedMethodsHeader());
+ res.setHeader(ACCESS_CONTROL_ALLOW_HEADERS, getAllowedHeadersHeader());
+ res.setHeader(ACCESS_CONTROL_MAX_AGE, maxAge);
+ }
+
+ @VisibleForTesting
+ String getAllowedHeadersHeader() {
+ return StringUtils.join(allowedHeaders, ',');
+ }
+
+ @VisibleForTesting
+ String getAllowedMethodsHeader() {
+ return StringUtils.join(allowedMethods, ',');
+ }
+
+ private void initializeAllowedMethods(FilterConfig filterConfig) {
+ String allowedMethodsConfig =
+ filterConfig.getInitParameter(ALLOWED_METHODS);
+ if (allowedMethodsConfig == null) {
+ allowedMethodsConfig = ALLOWED_METHODS_DEFAULT;
+ }
+ allowedMethods.addAll(
+ Arrays.asList(allowedMethodsConfig.trim().split("\\s*,\\s*")));
+ LOG.info("Allowed Methods: " + getAllowedMethodsHeader());
+ }
+
+ private void initializeAllowedHeaders(FilterConfig filterConfig) {
+ String allowedHeadersConfig =
+ filterConfig.getInitParameter(ALLOWED_HEADERS);
+ if (allowedHeadersConfig == null) {
+ allowedHeadersConfig = ALLOWED_HEADERS_DEFAULT;
+ }
+ allowedHeaders.addAll(
+ Arrays.asList(allowedHeadersConfig.trim().split("\\s*,\\s*")));
+ LOG.info("Allowed Headers: " + getAllowedHeadersHeader());
+ }
+
+ private void initializeAllowedOrigins(FilterConfig filterConfig) {
+ String allowedOriginsConfig =
+ filterConfig.getInitParameter(ALLOWED_ORIGINS);
+ if (allowedOriginsConfig == null) {
+ allowedOriginsConfig = ALLOWED_ORIGINS_DEFAULT;
+ }
+ allowedOrigins.addAll(
+ Arrays.asList(allowedOriginsConfig.trim().split("\\s*,\\s*")));
+ allowAllOrigins = allowedOrigins.contains("*");
+ LOG.info("Allowed Origins: " + StringUtils.join(allowedOrigins, ','));
+ LOG.info("Allow All Origins: " + allowAllOrigins);
+ }
+
+ private void initializeMaxAge(FilterConfig filterConfig) {
+ maxAge = filterConfig.getInitParameter(MAX_AGE);
+ if (maxAge == null) {
+ maxAge = MAX_AGE_DEFAULT;
+ }
+ LOG.info("Max Age: " + maxAge);
+ }
+
+ static String encodeHeader(final String header) {
+ if (header == null) {
+ return null;
+ }
+ // Protect against HTTP response splitting vulnerability
+ // since value is written as part of the response header
+ // Ensure this header only has one header by removing
+ // CRs and LFs
+ return header.split("\n|\r")[0].trim();
+ }
+
+ static boolean isCrossOrigin(String originsList) {
+ return originsList != null;
+ }
+
+ @VisibleForTesting
+ boolean areOriginsAllowed(String originsList) {
+ if (allowAllOrigins) {
+ return true;
+ }
+
+ String[] origins = originsList.trim().split("\\s+");
+ for (String origin : origins) {
+ for (String allowedOrigin : allowedOrigins) {
+ if (allowedOrigin.contains("*")) {
+ String regex = allowedOrigin.replace(".", "\\.").replace("*", ".*");
+ Pattern p = Pattern.compile(regex);
+ Matcher m = p.matcher(origin);
+ if (m.matches()) {
+ return true;
+ }
+ } else if (allowedOrigin.equals(origin)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean areHeadersAllowed(String accessControlRequestHeaders) {
+ if (accessControlRequestHeaders == null) {
+ return true;
+ }
+ String[] headers = accessControlRequestHeaders.trim().split("\\s*,\\s*");
+ return allowedHeaders.containsAll(Arrays.asList(headers));
+ }
+
+ private boolean isMethodAllowed(String accessControlRequestMethod) {
+ if (accessControlRequestMethod == null) {
+ return true;
+ }
+ return allowedMethods.contains(accessControlRequestMethod);
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml b/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml
index 9af86e0..df597a0 100644
--- a/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml
+++ b/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml
@@ -1404,6 +1404,42 @@ for ldap providers in the same way as above does.
</description>
</property>
+<!-- HTTP CORS support -->
+<property>
+ <description>Enable/disable the cross-origin (CORS) filter.</description>
+ <name>hadoop.http.cross-origin.enabled</name>
+ <value>false</value>
+</property>
+
+<property>
+ <description>Comma separated list of origins that are allowed for web
+ services needing cross-origin (CORS) support. Wildcards (*) and patterns
+ allowed</description>
+ <name>hadoop.http.cross-origin.allowed-origins</name>
+ <value>*</value>
+</property>
+
+<property>
+ <description>Comma separated list of methods that are allowed for web
+ services needing cross-origin (CORS) support.</description>
+ <name>hadoop.http.cross-origin.allowed-methods</name>
+ <value>GET,POST,HEAD</value>
+</property>
+
+<property>
+ <description>Comma separated list of headers that are allowed for web
+ services needing cross-origin (CORS) support.</description>
+ <name>hadoop.http.cross-origin.allowed-headers</name>
+ <value>X-Requested-With,Content-Type,Accept,Origin</value>
+</property>
+
+<property>
+ <description>The number of seconds a pre-flighted request can be cached
+ for web services needing cross-origin (CORS) support.</description>
+ <name>hadoop.http.cross-origin.max-age</name>
+ <value>1800</value>
+</property>
+
<property>
<name>dfs.ha.fencing.methods</name>
<value></value>
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-common-project/hadoop-common/src/site/markdown/HttpAuthentication.md
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/src/site/markdown/HttpAuthentication.md b/hadoop-common-project/hadoop-common/src/site/markdown/HttpAuthentication.md
index 46daaa9..ab2b06c 100644
--- a/hadoop-common-project/hadoop-common/src/site/markdown/HttpAuthentication.md
+++ b/hadoop-common-project/hadoop-common/src/site/markdown/HttpAuthentication.md
@@ -60,3 +60,17 @@ IMPORTANT: when using IP addresses, browsers ignore cookies with domain settings
`hadoop.http.authentication.kerberos.principal`: Indicates the Kerberos principal to be used for HTTP endpoint when using 'kerberos' authentication. The principal short name must be `HTTP` per Kerberos HTTP SPNEGO specification. The default value is `HTTP/_HOST@$LOCALHOST`, where `_HOST` -if present- is replaced with bind address of the HTTP server.
`hadoop.http.authentication.kerberos.keytab`: Location of the keytab file with the credentials for the Kerberos principal used for the HTTP endpoint. The default value is `$user.home/hadoop.keytab`.i
+
+CORS
+----
+To enable cross-origin support (CORS), please set the following configuration parameters:
+
+Add org.apache.hadoop.security.HttpCrossOriginFilterInitializer to hadoop.http.filter.initializers in core-site.xml. You will also need to set the following properties in core-site.xml -
+
+| Property | Default Value | Description |
+|:---- |:---- |:---- |:---- |
+| hadoop.http.cross-origin.enabled | false | Enables cross origin support for all web-services |
+| hadoop.http.cross-origin.allowed-origins | \* | Comma separated list of origins that are allowed, wildcards (\*) and patterns allowed |
+| hadoop.http.cross-origin.allowed-methods | GET,POST,HEAD | Comma separated list of methods that are allowed |
+| hadoop.http.cross-origin.allowed-headers | X-Requested-With,Content-Type,Accept,Origin | Comma separated list of headers that are allowed |
+| hadoop.http.cross-origin.max-age | 1800 | Number of seconds a pre-flighted request can be cached |
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestHttpCrossOriginFilterInitializer.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestHttpCrossOriginFilterInitializer.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestHttpCrossOriginFilterInitializer.java
new file mode 100644
index 0000000..3db6ef1
--- /dev/null
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestHttpCrossOriginFilterInitializer.java
@@ -0,0 +1,58 @@
+/**
+ * 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.hadoop.security;
+
+import java.util.Map;
+
+import org.apache.hadoop.conf.Configuration;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestHttpCrossOriginFilterInitializer {
+
+ @Test
+ public void testGetFilterParameters() {
+
+ // Initialize configuration object
+ Configuration conf = new Configuration();
+ conf.set(HttpCrossOriginFilterInitializer.PREFIX + "rootparam",
+ "rootvalue");
+ conf.set(HttpCrossOriginFilterInitializer.PREFIX + "nested.param",
+ "nestedvalue");
+ conf.set("outofscopeparam", "outofscopevalue");
+
+ // call function under test
+ Map<String, String> filterParameters = HttpCrossOriginFilterInitializer
+ .getFilterParameters(conf, HttpCrossOriginFilterInitializer.PREFIX);
+
+ // retrieve values
+ String rootvalue = filterParameters.get("rootparam");
+ String nestedvalue = filterParameters.get("nested.param");
+ String outofscopeparam = filterParameters.get("outofscopeparam");
+
+ // verify expected values are in place
+ Assert.assertEquals("Could not find filter parameter", "rootvalue",
+ rootvalue);
+ Assert.assertEquals("Could not find filter parameter", "nestedvalue",
+ nestedvalue);
+ Assert.assertNull("Found unexpected value in filter parameters",
+ outofscopeparam);
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http/TestCrossOriginFilter.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http/TestCrossOriginFilter.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http/TestCrossOriginFilter.java
new file mode 100644
index 0000000..5c9b413
--- /dev/null
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http/TestCrossOriginFilter.java
@@ -0,0 +1,330 @@
+/**
+ * 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.hadoop.security.http;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.hadoop.security.http.CrossOriginFilter;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class TestCrossOriginFilter {
+
+ @Test
+ public void testSameOrigin() throws ServletException, IOException {
+
+ // Setup the configuration settings of the server
+ Map<String, String> conf = new HashMap<String, String>();
+ conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "");
+ FilterConfig filterConfig = new FilterConfigTest(conf);
+
+ // Origin is not specified for same origin requests
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn(null);
+
+ // Objects to verify interactions based on request
+ HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
+ FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+ // Object under test
+ CrossOriginFilter filter = new CrossOriginFilter();
+ filter.init(filterConfig);
+ filter.doFilter(mockReq, mockRes, mockChain);
+
+ Mockito.verifyZeroInteractions(mockRes);
+ Mockito.verify(mockChain).doFilter(mockReq, mockRes);
+ }
+
+ @Test
+ public void testAllowAllOrigins() throws ServletException, IOException {
+
+ // Setup the configuration settings of the server
+ Map<String, String> conf = new HashMap<String, String>();
+ conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "*");
+ FilterConfig filterConfig = new FilterConfigTest(conf);
+
+ // Object under test
+ CrossOriginFilter filter = new CrossOriginFilter();
+ filter.init(filterConfig);
+ Assert.assertTrue(filter.areOriginsAllowed("example.com"));
+ }
+
+ @Test
+ public void testEncodeHeaders() {
+ String validOrigin = "http://localhost:12345";
+ String encodedValidOrigin = CrossOriginFilter.encodeHeader(validOrigin);
+ Assert.assertEquals("Valid origin encoding should match exactly",
+ validOrigin, encodedValidOrigin);
+
+ String httpResponseSplitOrigin = validOrigin + " \nSecondHeader: value";
+ String encodedResponseSplitOrigin =
+ CrossOriginFilter.encodeHeader(httpResponseSplitOrigin);
+ Assert.assertEquals("Http response split origin should be protected against",
+ validOrigin, encodedResponseSplitOrigin);
+
+ // Test Origin List
+ String validOriginList = "http://foo.example.com:12345 http://bar.example.com:12345";
+ String encodedValidOriginList = CrossOriginFilter
+ .encodeHeader(validOriginList);
+ Assert.assertEquals("Valid origin list encoding should match exactly",
+ validOriginList, encodedValidOriginList);
+ }
+
+ @Test
+ public void testPatternMatchingOrigins() throws ServletException, IOException {
+
+ // Setup the configuration settings of the server
+ Map<String, String> conf = new HashMap<String, String>();
+ conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "*.example.com");
+ FilterConfig filterConfig = new FilterConfigTest(conf);
+
+ // Object under test
+ CrossOriginFilter filter = new CrossOriginFilter();
+ filter.init(filterConfig);
+
+ // match multiple sub-domains
+ Assert.assertFalse(filter.areOriginsAllowed("example.com"));
+ Assert.assertFalse(filter.areOriginsAllowed("foo:example.com"));
+ Assert.assertTrue(filter.areOriginsAllowed("foo.example.com"));
+ Assert.assertTrue(filter.areOriginsAllowed("foo.bar.example.com"));
+
+ // First origin is allowed
+ Assert.assertTrue(filter.areOriginsAllowed("foo.example.com foo.nomatch.com"));
+ // Second origin is allowed
+ Assert.assertTrue(filter.areOriginsAllowed("foo.nomatch.com foo.example.com"));
+ // No origin in list is allowed
+ Assert.assertFalse(filter.areOriginsAllowed("foo.nomatch1.com foo.nomatch2.com"));
+ }
+
+ @Test
+ public void testDisallowedOrigin() throws ServletException, IOException {
+
+ // Setup the configuration settings of the server
+ Map<String, String> conf = new HashMap<String, String>();
+ conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "example.com");
+ FilterConfig filterConfig = new FilterConfigTest(conf);
+
+ // Origin is not specified for same origin requests
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.org");
+
+ // Objects to verify interactions based on request
+ HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
+ FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+ // Object under test
+ CrossOriginFilter filter = new CrossOriginFilter();
+ filter.init(filterConfig);
+ filter.doFilter(mockReq, mockRes, mockChain);
+
+ Mockito.verifyZeroInteractions(mockRes);
+ Mockito.verify(mockChain).doFilter(mockReq, mockRes);
+ }
+
+ @Test
+ public void testDisallowedMethod() throws ServletException, IOException {
+
+ // Setup the configuration settings of the server
+ Map<String, String> conf = new HashMap<String, String>();
+ conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "example.com");
+ FilterConfig filterConfig = new FilterConfigTest(conf);
+
+ // Origin is not specified for same origin requests
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com");
+ Mockito.when(
+ mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD))
+ .thenReturn("DISALLOWED_METHOD");
+
+ // Objects to verify interactions based on request
+ HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
+ FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+ // Object under test
+ CrossOriginFilter filter = new CrossOriginFilter();
+ filter.init(filterConfig);
+ filter.doFilter(mockReq, mockRes, mockChain);
+
+ Mockito.verifyZeroInteractions(mockRes);
+ Mockito.verify(mockChain).doFilter(mockReq, mockRes);
+ }
+
+ @Test
+ public void testDisallowedHeader() throws ServletException, IOException {
+
+ // Setup the configuration settings of the server
+ Map<String, String> conf = new HashMap<String, String>();
+ conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "example.com");
+ FilterConfig filterConfig = new FilterConfigTest(conf);
+
+ // Origin is not specified for same origin requests
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com");
+ Mockito.when(
+ mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD))
+ .thenReturn("GET");
+ Mockito.when(
+ mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_HEADERS))
+ .thenReturn("Disallowed-Header");
+
+ // Objects to verify interactions based on request
+ HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
+ FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+ // Object under test
+ CrossOriginFilter filter = new CrossOriginFilter();
+ filter.init(filterConfig);
+ filter.doFilter(mockReq, mockRes, mockChain);
+
+ Mockito.verifyZeroInteractions(mockRes);
+ Mockito.verify(mockChain).doFilter(mockReq, mockRes);
+ }
+
+ @Test
+ public void testCrossOriginFilter() throws ServletException, IOException {
+
+ // Setup the configuration settings of the server
+ Map<String, String> conf = new HashMap<String, String>();
+ conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "example.com");
+ FilterConfig filterConfig = new FilterConfigTest(conf);
+
+ // Origin is not specified for same origin requests
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com");
+ Mockito.when(
+ mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD))
+ .thenReturn("GET");
+ Mockito.when(
+ mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_HEADERS))
+ .thenReturn("X-Requested-With");
+
+ // Objects to verify interactions based on request
+ HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
+ FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+ // Object under test
+ CrossOriginFilter filter = new CrossOriginFilter();
+ filter.init(filterConfig);
+ filter.doFilter(mockReq, mockRes, mockChain);
+
+ Mockito.verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN,
+ "example.com");
+ Mockito.verify(mockRes).setHeader(
+ CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS,
+ Boolean.TRUE.toString());
+ Mockito.verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_METHODS,
+ filter.getAllowedMethodsHeader());
+ Mockito.verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_HEADERS,
+ filter.getAllowedHeadersHeader());
+ Mockito.verify(mockChain).doFilter(mockReq, mockRes);
+ }
+
+ @Test
+ public void testCrossOriginFilterAfterRestart() throws ServletException {
+
+ // Setup the configuration settings of the server
+ Map<String, String> conf = new HashMap<String, String>();
+ conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "example.com");
+ conf.put(CrossOriginFilter.ALLOWED_HEADERS, "X-Requested-With,Accept");
+ conf.put(CrossOriginFilter.ALLOWED_METHODS, "GET,POST");
+ FilterConfig filterConfig = new FilterConfigTest(conf);
+
+ // Object under test
+ CrossOriginFilter filter = new CrossOriginFilter();
+ filter.init(filterConfig);
+
+ //verify filter values
+ Assert.assertTrue("Allowed headers do not match",
+ filter.getAllowedHeadersHeader()
+ .compareTo("X-Requested-With,Accept") == 0);
+ Assert.assertTrue("Allowed methods do not match",
+ filter.getAllowedMethodsHeader()
+ .compareTo("GET,POST") == 0);
+ Assert.assertTrue(filter.areOriginsAllowed("example.com"));
+
+ //destroy filter values and clear conf
+ filter.destroy();
+ conf.clear();
+
+ // Setup the configuration settings of the server
+ conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "newexample.com");
+ conf.put(CrossOriginFilter.ALLOWED_HEADERS, "Content-Type,Origin");
+ conf.put(CrossOriginFilter.ALLOWED_METHODS, "GET,HEAD");
+ filterConfig = new FilterConfigTest(conf);
+
+ //initialize filter
+ filter.init(filterConfig);
+
+ //verify filter values
+ Assert.assertTrue("Allowed headers do not match",
+ filter.getAllowedHeadersHeader()
+ .compareTo("Content-Type,Origin") == 0);
+ Assert.assertTrue("Allowed methods do not match",
+ filter.getAllowedMethodsHeader()
+ .compareTo("GET,HEAD") == 0);
+ Assert.assertTrue(filter.areOriginsAllowed("newexample.com"));
+
+ //destroy filter values
+ filter.destroy();
+ }
+
+ private static class FilterConfigTest implements FilterConfig {
+
+ final Map<String, String> map;
+
+ FilterConfigTest(Map<String, String> map) {
+ this.map = map;
+ }
+
+ @Override
+ public String getFilterName() {
+ return "test-filter";
+ }
+
+ @Override
+ public String getInitParameter(String key) {
+ return map.get(key);
+ }
+
+ @Override
+ public Enumeration<String> getInitParameterNames() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/CHANGES.txt
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/CHANGES.txt b/hadoop-yarn-project/CHANGES.txt
index 9f35307..53bf85a 100644
--- a/hadoop-yarn-project/CHANGES.txt
+++ b/hadoop-yarn-project/CHANGES.txt
@@ -988,6 +988,8 @@ Release 2.7.2 - UNRELEASED
IMPROVEMENTS
+ YARN-4009. CORS support for ResourceManager REST API. ( Varun Vasudev via jeagles)
+
YARN-3170. YARN architecture document needs updating. (Brahma Reddy Battula
via ozawa)
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java
index 913b5df..dafd311 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java
@@ -337,6 +337,11 @@ public class YarnConfiguration extends Configuration {
public static final boolean DEFAULT_RM_WEBAPP_DELEGATION_TOKEN_AUTH_FILTER =
true;
+ /** Enable cross origin (CORS) support. **/
+ public static final String RM_WEBAPP_ENABLE_CORS_FILTER =
+ RM_PREFIX + "webapp.cross-origin.enabled";
+ public static final boolean DEFAULT_RM_WEBAPP_ENABLE_CORS_FILTER = false;
+
/** How long to wait until a container is considered dead.*/
public static final String RM_CONTAINER_ALLOC_EXPIRY_INTERVAL_MS =
RM_PREFIX + "rm.container-allocation.expiry-interval-ms";
@@ -974,6 +979,11 @@ public class YarnConfiguration extends Configuration {
public static final String DEFAULT_NM_WEBAPP_HTTPS_ADDRESS = "0.0.0.0:"
+ DEFAULT_NM_WEBAPP_HTTPS_PORT;
+ /** Enable/disable CORS filter. */
+ public static final String NM_WEBAPP_ENABLE_CORS_FILTER =
+ NM_PREFIX + "webapp.cross-origin.enabled";
+ public static final boolean DEFAULT_NM_WEBAPP_ENABLE_CORS_FILTER = false;
+
/** How often to monitor resource in a node.*/
public static final String NM_RESOURCE_MON_INTERVAL_MS =
NM_PREFIX + "resource-monitor.interval-ms";
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml
index c6ffe18..6d5b05a 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml
@@ -255,6 +255,14 @@
</property>
<property>
+ <description>Flag to enable cross-origin (CORS) support in the RM. This flag
+ requires the CORS filter initializer to be added to the filter initializers
+ list in core-site.xml.</description>
+ <name>yarn.resourcemanager.webapp.cross-origin.enabled</name>
+ <value>false</value>
+ </property>
+
+ <property>
<description>How long to wait until a node manager is considered dead.</description>
<name>yarn.nm.liveness-monitor.expiry-interval-ms</name>
<value>600000</value>
@@ -2318,6 +2326,14 @@
</property>
<property>
+ <description>Flag to enable cross-origin (CORS) support in the NM. This flag
+ requires the CORS filter initializer to be added to the filter initializers
+ list in core-site.xml.</description>
+ <name>yarn.nodemanager.webapp.cross-origin.enabled</name>
+ <value>false</value>
+ </property>
+
+ <property>
<description>
Defines maximum application priority in a cluster.
If an application is submitted with a priority higher than this value, it will be
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/ApplicationHistoryServer.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/ApplicationHistoryServer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/ApplicationHistoryServer.java
index dfacaf6..111a85f 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/ApplicationHistoryServer.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/ApplicationHistoryServer.java
@@ -30,6 +30,7 @@ import org.apache.hadoop.http.HttpServer2;
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
import org.apache.hadoop.metrics2.source.JvmMetrics;
import org.apache.hadoop.security.AuthenticationFilterInitializer;
+import org.apache.hadoop.security.HttpCrossOriginFilterInitializer;
import org.apache.hadoop.security.SecurityUtil;
import org.apache.hadoop.service.CompositeService;
import org.apache.hadoop.service.Service;
@@ -252,10 +253,17 @@ public class ApplicationHistoryServer extends CompositeService {
if(conf.getBoolean(YarnConfiguration
.TIMELINE_SERVICE_HTTP_CROSS_ORIGIN_ENABLED, YarnConfiguration
.TIMELINE_SERVICE_HTTP_CROSS_ORIGIN_ENABLED_DEFAULT)) {
- if (initializers.length() != 0) {
- initializers += ",";
+ if (initializers.contains(HttpCrossOriginFilterInitializer.class.getName())) {
+ initializers =
+ initializers.replaceAll(HttpCrossOriginFilterInitializer.class.getName(),
+ CrossOriginFilterInitializer.class.getName());
+ }
+ else {
+ if (initializers.length() != 0) {
+ initializers += ",";
+ }
+ initializers += CrossOriginFilterInitializer.class.getName();
}
- initializers += CrossOriginFilterInitializer.class.getName();
modifiedInitializers = true;
}
}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilter.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilter.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilter.java
deleted file mode 100644
index 9edaefb..0000000
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilter.java
+++ /dev/null
@@ -1,259 +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.hadoop.yarn.server.timeline.webapp;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-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.commons.lang.StringUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import com.google.common.annotations.VisibleForTesting;
-
-public class CrossOriginFilter implements Filter {
-
- private static final Log LOG = LogFactory.getLog(CrossOriginFilter.class);
-
- // HTTP CORS Request Headers
- static final String ORIGIN = "Origin";
- static final String ACCESS_CONTROL_REQUEST_METHOD =
- "Access-Control-Request-Method";
- static final String ACCESS_CONTROL_REQUEST_HEADERS =
- "Access-Control-Request-Headers";
-
- // HTTP CORS Response Headers
- static final String ACCESS_CONTROL_ALLOW_ORIGIN =
- "Access-Control-Allow-Origin";
- static final String ACCESS_CONTROL_ALLOW_CREDENTIALS =
- "Access-Control-Allow-Credentials";
- static final String ACCESS_CONTROL_ALLOW_METHODS =
- "Access-Control-Allow-Methods";
- static final String ACCESS_CONTROL_ALLOW_HEADERS =
- "Access-Control-Allow-Headers";
- static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
-
- // Filter configuration
- public static final String ALLOWED_ORIGINS = "allowed-origins";
- public static final String ALLOWED_ORIGINS_DEFAULT = "*";
- public static final String ALLOWED_METHODS = "allowed-methods";
- public static final String ALLOWED_METHODS_DEFAULT = "GET,POST,HEAD";
- public static final String ALLOWED_HEADERS = "allowed-headers";
- public static final String ALLOWED_HEADERS_DEFAULT =
- "X-Requested-With,Content-Type,Accept,Origin";
- public static final String MAX_AGE = "max-age";
- public static final String MAX_AGE_DEFAULT = "1800";
-
- private List<String> allowedMethods = new ArrayList<String>();
- private List<String> allowedHeaders = new ArrayList<String>();
- private List<String> allowedOrigins = new ArrayList<String>();
- private boolean allowAllOrigins = true;
- private String maxAge;
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- initializeAllowedMethods(filterConfig);
- initializeAllowedHeaders(filterConfig);
- initializeAllowedOrigins(filterConfig);
- initializeMaxAge(filterConfig);
- }
-
- @Override
- public void doFilter(ServletRequest req, ServletResponse res,
- FilterChain chain)
- throws IOException, ServletException {
- doCrossFilter((HttpServletRequest) req, (HttpServletResponse) res);
- chain.doFilter(req, res);
- }
-
- @Override
- public void destroy() {
- allowedMethods.clear();
- allowedHeaders.clear();
- allowedOrigins.clear();
- }
-
- private void doCrossFilter(HttpServletRequest req, HttpServletResponse res) {
-
- String originsList = encodeHeader(req.getHeader(ORIGIN));
- if (!isCrossOrigin(originsList)) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("Header origin is null. Returning");
- }
- return;
- }
-
- if (!areOriginsAllowed(originsList)) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("Header origins '" + originsList + "' not allowed. Returning");
- }
- return;
- }
-
- String accessControlRequestMethod =
- req.getHeader(ACCESS_CONTROL_REQUEST_METHOD);
- if (!isMethodAllowed(accessControlRequestMethod)) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("Access control method '" + accessControlRequestMethod +
- "' not allowed. Returning");
- }
- return;
- }
-
- String accessControlRequestHeaders =
- req.getHeader(ACCESS_CONTROL_REQUEST_HEADERS);
- if (!areHeadersAllowed(accessControlRequestHeaders)) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("Access control headers '" + accessControlRequestHeaders +
- "' not allowed. Returning");
- }
- return;
- }
-
- if(LOG.isDebugEnabled()) {
- LOG.debug("Completed cross origin filter checks. Populating " +
- "HttpServletResponse");
- }
- res.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, originsList);
- res.setHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.TRUE.toString());
- res.setHeader(ACCESS_CONTROL_ALLOW_METHODS, getAllowedMethodsHeader());
- res.setHeader(ACCESS_CONTROL_ALLOW_HEADERS, getAllowedHeadersHeader());
- res.setHeader(ACCESS_CONTROL_MAX_AGE, maxAge);
- }
-
- @VisibleForTesting
- String getAllowedHeadersHeader() {
- return StringUtils.join(allowedHeaders, ',');
- }
-
- @VisibleForTesting
- String getAllowedMethodsHeader() {
- return StringUtils.join(allowedMethods, ',');
- }
-
- private void initializeAllowedMethods(FilterConfig filterConfig) {
- String allowedMethodsConfig =
- filterConfig.getInitParameter(ALLOWED_METHODS);
- if (allowedMethodsConfig == null) {
- allowedMethodsConfig = ALLOWED_METHODS_DEFAULT;
- }
- allowedMethods.addAll(
- Arrays.asList(allowedMethodsConfig.trim().split("\\s*,\\s*")));
- LOG.info("Allowed Methods: " + getAllowedMethodsHeader());
- }
-
- private void initializeAllowedHeaders(FilterConfig filterConfig) {
- String allowedHeadersConfig =
- filterConfig.getInitParameter(ALLOWED_HEADERS);
- if (allowedHeadersConfig == null) {
- allowedHeadersConfig = ALLOWED_HEADERS_DEFAULT;
- }
- allowedHeaders.addAll(
- Arrays.asList(allowedHeadersConfig.trim().split("\\s*,\\s*")));
- LOG.info("Allowed Headers: " + getAllowedHeadersHeader());
- }
-
- private void initializeAllowedOrigins(FilterConfig filterConfig) {
- String allowedOriginsConfig =
- filterConfig.getInitParameter(ALLOWED_ORIGINS);
- if (allowedOriginsConfig == null) {
- allowedOriginsConfig = ALLOWED_ORIGINS_DEFAULT;
- }
- allowedOrigins.addAll(
- Arrays.asList(allowedOriginsConfig.trim().split("\\s*,\\s*")));
- allowAllOrigins = allowedOrigins.contains("*");
- LOG.info("Allowed Origins: " + StringUtils.join(allowedOrigins, ','));
- LOG.info("Allow All Origins: " + allowAllOrigins);
- }
-
- private void initializeMaxAge(FilterConfig filterConfig) {
- maxAge = filterConfig.getInitParameter(MAX_AGE);
- if (maxAge == null) {
- maxAge = MAX_AGE_DEFAULT;
- }
- LOG.info("Max Age: " + maxAge);
- }
-
- static String encodeHeader(final String header) {
- if (header == null) {
- return null;
- }
- // Protect against HTTP response splitting vulnerability
- // since value is written as part of the response header
- // Ensure this header only has one header by removing
- // CRs and LFs
- return header.split("\n|\r")[0].trim();
- }
-
- static boolean isCrossOrigin(String originsList) {
- return originsList != null;
- }
-
- @VisibleForTesting
- boolean areOriginsAllowed(String originsList) {
- if (allowAllOrigins) {
- return true;
- }
-
- String[] origins = originsList.trim().split("\\s+");
- for (String origin : origins) {
- for (String allowedOrigin : allowedOrigins) {
- if (allowedOrigin.contains("*")) {
- String regex = allowedOrigin.replace(".", "\\.").replace("*", ".*");
- Pattern p = Pattern.compile(regex);
- Matcher m = p.matcher(origin);
- if (m.matches()) {
- return true;
- }
- } else if (allowedOrigin.equals(origin)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean areHeadersAllowed(String accessControlRequestHeaders) {
- if (accessControlRequestHeaders == null) {
- return true;
- }
- String headers[] = accessControlRequestHeaders.trim().split("\\s*,\\s*");
- return allowedHeaders.containsAll(Arrays.asList(headers));
- }
-
- private boolean isMethodAllowed(String accessControlRequestMethod) {
- if (accessControlRequestMethod == null) {
- return true;
- }
- return allowedMethods.contains(accessControlRequestMethod);
- }
-}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilterInitializer.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilterInitializer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilterInitializer.java
index 148cc63..1e6a7ff 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilterInitializer.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilterInitializer.java
@@ -18,35 +18,35 @@
package org.apache.hadoop.yarn.server.timeline.webapp;
-import java.util.HashMap;
-import java.util.Map;
-
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.http.FilterContainer;
-import org.apache.hadoop.http.FilterInitializer;
+import org.apache.hadoop.security.HttpCrossOriginFilterInitializer;
+import org.apache.hadoop.security.http.CrossOriginFilter;
+
+import java.util.Map;
-public class CrossOriginFilterInitializer extends FilterInitializer {
+public class CrossOriginFilterInitializer extends HttpCrossOriginFilterInitializer {
public static final String PREFIX =
"yarn.timeline-service.http-cross-origin.";
@Override
+ protected String getPrefix() {
+ return PREFIX;
+ }
+
+ @Override
public void initFilter(FilterContainer container, Configuration conf) {
- container.addGlobalFilter("Cross Origin Filter",
- CrossOriginFilter.class.getName(), getFilterParameters(conf));
- }
+ // setup the filter
+ // use the keys with "yarn.timeline-service.http-cross-origin" prefix to
+ // override the ones with the "hadoop.http.cross-origin" prefix.
- static Map<String, String> getFilterParameters(Configuration conf) {
- Map<String, String> filterParams =
- new HashMap<String, String>();
- for (Map.Entry<String, String> entry : conf.getValByRegex(PREFIX)
- .entrySet()) {
- String name = entry.getKey();
- String value = entry.getValue();
- name = name.substring(PREFIX.length());
- filterParams.put(name, value);
- }
- return filterParams;
+ Map<String, String> filterParameters =
+ getFilterParameters(conf, HttpCrossOriginFilterInitializer.PREFIX);
+ filterParameters.putAll(getFilterParameters(conf, getPrefix()));
+
+ container.addGlobalFilter("Cross Origin Filter",
+ CrossOriginFilter.class.getName(), filterParameters);
}
-}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilter.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilter.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilter.java
deleted file mode 100644
index 5e09341..0000000
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilter.java
+++ /dev/null
@@ -1,324 +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.hadoop.yarn.server.timeline.webapp;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-public class TestCrossOriginFilter {
-
- @Test
- public void testSameOrigin() throws ServletException, IOException {
-
- // Setup the configuration settings of the server
- Map<String, String> conf = new HashMap<String, String>();
- conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "");
- FilterConfig filterConfig = new FilterConfigTest(conf);
-
- // Origin is not specified for same origin requests
- HttpServletRequest mockReq = mock(HttpServletRequest.class);
- when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn(null);
-
- // Objects to verify interactions based on request
- HttpServletResponse mockRes = mock(HttpServletResponse.class);
- FilterChain mockChain = mock(FilterChain.class);
-
- // Object under test
- CrossOriginFilter filter = new CrossOriginFilter();
- filter.init(filterConfig);
- filter.doFilter(mockReq, mockRes, mockChain);
-
- verifyZeroInteractions(mockRes);
- verify(mockChain).doFilter(mockReq, mockRes);
- }
-
- @Test
- public void testAllowAllOrigins() throws ServletException, IOException {
-
- // Setup the configuration settings of the server
- Map<String, String> conf = new HashMap<String, String>();
- conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "*");
- FilterConfig filterConfig = new FilterConfigTest(conf);
-
- // Object under test
- CrossOriginFilter filter = new CrossOriginFilter();
- filter.init(filterConfig);
- Assert.assertTrue(filter.areOriginsAllowed("example.com"));
- }
-
- @Test
- public void testEncodeHeaders() {
- String validOrigin = "http://localhost:12345";
- String encodedValidOrigin = CrossOriginFilter.encodeHeader(validOrigin);
- Assert.assertEquals("Valid origin encoding should match exactly",
- validOrigin, encodedValidOrigin);
-
- String httpResponseSplitOrigin = validOrigin + " \nSecondHeader: value";
- String encodedResponseSplitOrigin =
- CrossOriginFilter.encodeHeader(httpResponseSplitOrigin);
- Assert.assertEquals("Http response split origin should be protected against",
- validOrigin, encodedResponseSplitOrigin);
-
- // Test Origin List
- String validOriginList = "http://foo.example.com:12345 http://bar.example.com:12345";
- String encodedValidOriginList = CrossOriginFilter.encodeHeader(validOriginList);
- Assert.assertEquals("Valid origin list encoding should match exactly",
- validOriginList, encodedValidOriginList);
- }
-
- @Test
- public void testPatternMatchingOrigins() throws ServletException, IOException {
-
- // Setup the configuration settings of the server
- Map<String, String> conf = new HashMap<String, String>();
- conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "*.example.com");
- FilterConfig filterConfig = new FilterConfigTest(conf);
-
- // Object under test
- CrossOriginFilter filter = new CrossOriginFilter();
- filter.init(filterConfig);
-
- // match multiple sub-domains
- Assert.assertFalse(filter.areOriginsAllowed("example.com"));
- Assert.assertFalse(filter.areOriginsAllowed("foo:example.com"));
- Assert.assertTrue(filter.areOriginsAllowed("foo.example.com"));
- Assert.assertTrue(filter.areOriginsAllowed("foo.bar.example.com"));
-
- // First origin is allowed
- Assert.assertTrue(filter.areOriginsAllowed("foo.example.com foo.nomatch.com"));
- // Second origin is allowed
- Assert.assertTrue(filter.areOriginsAllowed("foo.nomatch.com foo.example.com"));
- // No origin in list is allowed
- Assert.assertFalse(filter.areOriginsAllowed("foo.nomatch1.com foo.nomatch2.com"));
- }
-
- @Test
- public void testDisallowedOrigin() throws ServletException, IOException {
-
- // Setup the configuration settings of the server
- Map<String, String> conf = new HashMap<String, String>();
- conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "example.com");
- FilterConfig filterConfig = new FilterConfigTest(conf);
-
- // Origin is not specified for same origin requests
- HttpServletRequest mockReq = mock(HttpServletRequest.class);
- when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.org");
-
- // Objects to verify interactions based on request
- HttpServletResponse mockRes = mock(HttpServletResponse.class);
- FilterChain mockChain = mock(FilterChain.class);
-
- // Object under test
- CrossOriginFilter filter = new CrossOriginFilter();
- filter.init(filterConfig);
- filter.doFilter(mockReq, mockRes, mockChain);
-
- verifyZeroInteractions(mockRes);
- verify(mockChain).doFilter(mockReq, mockRes);
- }
-
- @Test
- public void testDisallowedMethod() throws ServletException, IOException {
-
- // Setup the configuration settings of the server
- Map<String, String> conf = new HashMap<String, String>();
- conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "example.com");
- FilterConfig filterConfig = new FilterConfigTest(conf);
-
- // Origin is not specified for same origin requests
- HttpServletRequest mockReq = mock(HttpServletRequest.class);
- when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com");
- when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD))
- .thenReturn("DISALLOWED_METHOD");
-
- // Objects to verify interactions based on request
- HttpServletResponse mockRes = mock(HttpServletResponse.class);
- FilterChain mockChain = mock(FilterChain.class);
-
- // Object under test
- CrossOriginFilter filter = new CrossOriginFilter();
- filter.init(filterConfig);
- filter.doFilter(mockReq, mockRes, mockChain);
-
- verifyZeroInteractions(mockRes);
- verify(mockChain).doFilter(mockReq, mockRes);
- }
-
- @Test
- public void testDisallowedHeader() throws ServletException, IOException {
-
- // Setup the configuration settings of the server
- Map<String, String> conf = new HashMap<String, String>();
- conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "example.com");
- FilterConfig filterConfig = new FilterConfigTest(conf);
-
- // Origin is not specified for same origin requests
- HttpServletRequest mockReq = mock(HttpServletRequest.class);
- when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com");
- when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD))
- .thenReturn("GET");
- when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_HEADERS))
- .thenReturn("Disallowed-Header");
-
- // Objects to verify interactions based on request
- HttpServletResponse mockRes = mock(HttpServletResponse.class);
- FilterChain mockChain = mock(FilterChain.class);
-
- // Object under test
- CrossOriginFilter filter = new CrossOriginFilter();
- filter.init(filterConfig);
- filter.doFilter(mockReq, mockRes, mockChain);
-
- verifyZeroInteractions(mockRes);
- verify(mockChain).doFilter(mockReq, mockRes);
- }
-
- @Test
- public void testCrossOriginFilter() throws ServletException, IOException {
-
- // Setup the configuration settings of the server
- Map<String, String> conf = new HashMap<String, String>();
- conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "example.com");
- FilterConfig filterConfig = new FilterConfigTest(conf);
-
- // Origin is not specified for same origin requests
- HttpServletRequest mockReq = mock(HttpServletRequest.class);
- when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com");
- when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD))
- .thenReturn("GET");
- when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_HEADERS))
- .thenReturn("X-Requested-With");
-
- // Objects to verify interactions based on request
- HttpServletResponse mockRes = mock(HttpServletResponse.class);
- FilterChain mockChain = mock(FilterChain.class);
-
- // Object under test
- CrossOriginFilter filter = new CrossOriginFilter();
- filter.init(filterConfig);
- filter.doFilter(mockReq, mockRes, mockChain);
-
- verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN,
- "example.com");
- verify(mockRes).setHeader(
- CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS,
- Boolean.TRUE.toString());
- verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_METHODS,
- filter.getAllowedMethodsHeader());
- verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_HEADERS,
- filter.getAllowedHeadersHeader());
- verify(mockChain).doFilter(mockReq, mockRes);
- }
-
- @Test
- public void testCrossOriginFilterAfterRestart() throws ServletException {
-
- // Setup the configuration settings of the server
- Map<String, String> conf = new HashMap<String, String>();
- conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "example.com");
- conf.put(CrossOriginFilter.ALLOWED_HEADERS, "X-Requested-With,Accept");
- conf.put(CrossOriginFilter.ALLOWED_METHODS, "GET,POST");
- FilterConfig filterConfig = new FilterConfigTest(conf);
-
- // Object under test
- CrossOriginFilter filter = new CrossOriginFilter();
- filter.init(filterConfig);
-
- //verify filter values
- Assert.assertTrue("Allowed headers do not match",
- filter.getAllowedHeadersHeader()
- .compareTo("X-Requested-With,Accept") == 0);
- Assert.assertTrue("Allowed methods do not match",
- filter.getAllowedMethodsHeader()
- .compareTo("GET,POST") == 0);
- Assert.assertTrue(filter.areOriginsAllowed("example.com"));
-
- //destroy filter values and clear conf
- filter.destroy();
- conf.clear();
-
- // Setup the configuration settings of the server
- conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "newexample.com");
- conf.put(CrossOriginFilter.ALLOWED_HEADERS, "Content-Type,Origin");
- conf.put(CrossOriginFilter.ALLOWED_METHODS, "GET,HEAD");
- filterConfig = new FilterConfigTest(conf);
-
- //initialize filter
- filter.init(filterConfig);
-
- //verify filter values
- Assert.assertTrue("Allowed headers do not match",
- filter.getAllowedHeadersHeader()
- .compareTo("Content-Type,Origin") == 0);
- Assert.assertTrue("Allowed methods do not match",
- filter.getAllowedMethodsHeader()
- .compareTo("GET,HEAD") == 0);
- Assert.assertTrue(filter.areOriginsAllowed("newexample.com"));
-
- //destroy filter values
- filter.destroy();
- }
-
- private static class FilterConfigTest implements FilterConfig {
-
- final Map<String, String> map;
-
- FilterConfigTest(Map<String, String> map) {
- this.map = map;
- }
-
- @Override
- public String getFilterName() {
- return "test-filter";
- }
-
- @Override
- public String getInitParameter(String key) {
- return map.get(key);
- }
-
- @Override
- public Enumeration<String> getInitParameterNames() {
- return Collections.enumeration(map.keySet());
- }
-
- @Override
- public ServletContext getServletContext() {
- return null;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilterInitializer.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilterInitializer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilterInitializer.java
deleted file mode 100644
index cf26368..0000000
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilterInitializer.java
+++ /dev/null
@@ -1,57 +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.hadoop.yarn.server.timeline.webapp;
-
-import java.util.Map;
-
-import org.apache.hadoop.conf.Configuration;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestCrossOriginFilterInitializer {
-
- @Test
- public void testGetFilterParameters() {
-
- // Initialize configuration object
- Configuration conf = new Configuration();
- conf.set(CrossOriginFilterInitializer.PREFIX + "rootparam", "rootvalue");
- conf.set(CrossOriginFilterInitializer.PREFIX + "nested.param",
- "nestedvalue");
- conf.set("outofscopeparam", "outofscopevalue");
-
- // call function under test
- Map<String, String> filterParameters =
- CrossOriginFilterInitializer.getFilterParameters(conf);
-
- // retrieve values
- String rootvalue = filterParameters.get("rootparam");
- String nestedvalue = filterParameters.get("nested.param");
- String outofscopeparam = filterParameters.get("outofscopeparam");
-
- // verify expected values are in place
- Assert.assertEquals("Could not find filter parameter", "rootvalue",
- rootvalue);
- Assert.assertEquals("Could not find filter parameter", "nestedvalue",
- nestedvalue);
- Assert.assertNull("Found unexpected value in filter parameters",
- outofscopeparam);
- }
-}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/pom.xml
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/pom.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/pom.xml
index 18040b4..2958b81 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/pom.xml
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/pom.xml
@@ -82,6 +82,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<exclusions>
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/WebServer.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/WebServer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/WebServer.java
index 0e0c1e2..319c10c 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/WebServer.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/WebServer.java
@@ -22,6 +22,7 @@ import static org.apache.hadoop.yarn.util.StringHelper.pajoin;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.security.HttpCrossOriginFilterInitializer;
import org.apache.hadoop.service.AbstractService;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
@@ -59,7 +60,14 @@ public class WebServer extends AbstractService {
String bindAddress = WebAppUtils.getWebAppBindURL(getConfig(),
YarnConfiguration.NM_BIND_HOST,
WebAppUtils.getNMWebAppURLWithoutScheme(getConfig()));
-
+ boolean enableCors = getConfig()
+ .getBoolean(YarnConfiguration.NM_WEBAPP_ENABLE_CORS_FILTER,
+ YarnConfiguration.DEFAULT_NM_WEBAPP_ENABLE_CORS_FILTER);
+ if (enableCors) {
+ getConfig().setBoolean(HttpCrossOriginFilterInitializer.PREFIX
+ + HttpCrossOriginFilterInitializer.ENABLED_SUFFIX, true);
+ }
+
LOG.info("Instantiating NMWebApp at " + bindAddress);
try {
this.webApp =
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
index 88fb1cf..01a1c8f 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
@@ -28,10 +28,7 @@ import org.apache.hadoop.ha.HAServiceProtocol.HAServiceState;
import org.apache.hadoop.http.lib.StaticUserWebFilter;
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
import org.apache.hadoop.metrics2.source.JvmMetrics;
-import org.apache.hadoop.security.AuthenticationFilterInitializer;
-import org.apache.hadoop.security.Groups;
-import org.apache.hadoop.security.SecurityUtil;
-import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.*;
import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
import org.apache.hadoop.security.authorize.ProxyUsers;
import org.apache.hadoop.service.AbstractService;
@@ -866,6 +863,9 @@ public class ResourceManager extends CompositeService implements Recoverable {
// 4. hadoop.http.filter.initializers container AuthenticationFilterInitializer
Configuration conf = getConfig();
+ boolean enableCorsFilter =
+ conf.getBoolean(YarnConfiguration.RM_WEBAPP_ENABLE_CORS_FILTER,
+ YarnConfiguration.DEFAULT_RM_WEBAPP_ENABLE_CORS_FILTER);
boolean useYarnAuthenticationFilter =
conf.getBoolean(
YarnConfiguration.RM_WEBAPP_DELEGATION_TOKEN_AUTH_FILTER,
@@ -877,6 +877,12 @@ public class ResourceManager extends CompositeService implements Recoverable {
Class<?>[] initializersClasses =
conf.getClasses(filterInitializerConfKey);
+ // setup CORS
+ if (enableCorsFilter) {
+ conf.setBoolean(HttpCrossOriginFilterInitializer.PREFIX
+ + HttpCrossOriginFilterInitializer.ENABLED_SUFFIX, true);
+ }
+
boolean hasHadoopAuthFilterInitializer = false;
boolean hasRMAuthFilterInitializer = false;
if (initializersClasses != null) {
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/NodeManagerRest.md
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/NodeManagerRest.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/NodeManagerRest.md
index acafd28..2613b63 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/NodeManagerRest.md
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/NodeManagerRest.md
@@ -16,6 +16,7 @@ NodeManager REST API's
=======================
* [Overview](#Overview)
+* [Enabling CORS support](#Enabling_CORS_support)
* [NodeManager Information API](#NodeManager_Information_API)
* [Applications API](#Applications_API)
* [Application API](#Application_API)
@@ -27,6 +28,13 @@ Overview
The NodeManager REST API's allow the user to get status on the node and information about applications and containers running on that node.
+Enabling CORS support
+---------------------
+To enable cross-origin support (CORS) for the NM only(without enabling it for the RM), please set the following configuration parameters:
+
+In core-site.xml, add org.apache.hadoop.security.HttpCrossOriginFilterInitializer to hadoop.http.filter.initializers.
+In yarn-site.xml, set yarn.nodemanager.webapp.cross-origin.enabled to true.
+
NodeManager Information API
---------------------------
@@ -540,4 +548,4 @@ Response Body:
<containerLogsLink>http://host.domain.com:8042/node/containerlogs/container_1326121700862_0007_01_000001/user1</containerLogsLink>
<nodeId>host.domain.com:8041</nodeId>
</container>
-```
\ No newline at end of file
+```
http://git-wip-us.apache.org/repos/asf/hadoop/blob/f8adeb71/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/ResourceManagerRest.md
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/ResourceManagerRest.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/ResourceManagerRest.md
index 53df195..1d11ed4 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/ResourceManagerRest.md
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/ResourceManagerRest.md
@@ -16,6 +16,7 @@ ResourceManager REST API's.
===========================
* [Overview](#Overview)
+* [Enabling CORS support](#Enabling_CORS_support)
* [Cluster Information API](#Cluster_Information_API)
* [Cluster Metrics API](#Cluster_Metrics_API)
* [Cluster Scheduler API](#Cluster_Scheduler_API)
@@ -38,6 +39,13 @@ Overview
The ResourceManager REST API's allow the user to get information about the cluster - status on the cluster, metrics on the cluster, scheduler information, information about nodes in the cluster, and information about applications on the cluster.
+Enabling CORS support
+---------------------
+To enable cross-origin support (CORS) for the RM only(without enabling it for the NM), please set the following configuration parameters:
+
+In core-site.xml, add org.apache.hadoop.security.HttpCrossOriginFilterInitializer to hadoop.http.filter.initializers.
+In yarn-site.xml, set yarn.resourcemanager.webapp.cross-origin.enabled to true.
+
Cluster Information API
-----------------------