HADOOP-6832. Add an authentication plugin using a configurable static user

for the web UI. Contributed by Owen O'Malley and Todd Lipcon


git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1125043 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Christopher Douglas 2011-05-19 18:32:39 +00:00
parent ef543e386d
commit da15b2118c
5 changed files with 264 additions and 1 deletions

View File

@ -32,6 +32,9 @@ Trunk (unreleased changes)
HADOOP-7214. Add Common functionality necessary to provide an equivalent HADOOP-7214. Add Common functionality necessary to provide an equivalent
of /usr/bin/groups for Hadoop. (Aaron T. Myers via todd) of /usr/bin/groups for Hadoop. (Aaron T. Myers via todd)
HADOOP-6832. Add an authentication plugin using a configurable static user
for the web UI. (Owen O'Malley and Todd Lipcon via cdouglas)
IMPROVEMENTS IMPROVEMENTS
HADOOP-7042. Updates to test-patch.sh to include failed test names and HADOOP-7042. Updates to test-patch.sh to include failed test names and

View File

@ -45,7 +45,7 @@
<property> <property>
<name>hadoop.http.filter.initializers</name> <name>hadoop.http.filter.initializers</name>
<value></value> <value>org.apache.hadoop.http.lib.StaticUserWebFilter</value>
<description>A comma separated list of class names. Each class in the list <description>A comma separated list of class names. Each class in the list
must extend org.apache.hadoop.http.FilterInitializer. The corresponding must extend org.apache.hadoop.http.FilterInitializer. The corresponding
Filter will be initialized. Then, the Filter will be applied to all user Filter will be initialized. Then, the Filter will be applied to all user

View File

@ -0,0 +1,150 @@
/**
* 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.http.lib;
import java.io.IOException;
import java.security.Principal;
import java.util.HashMap;
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.HttpServletRequestWrapper;
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 javax.servlet.Filter;
/**
* Provides a servlet filter that pretends to authenticate a fake user (Dr.Who)
* so that the web UI is usable for a secure cluster without authentication.
*/
public class StaticUserWebFilter extends FilterInitializer {
static final String DEPRECATED_UGI_KEY = "dfs.web.ugi";
static final String USERNAME_KEY = "hadoop.http.staticuser.user";
static final String USERNAME_DEFAULT = "dr.who";
private static final Log LOG = LogFactory.getLog(StaticUserWebFilter.class);
static class User implements Principal {
private final String name;
public User(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
} else if (other == null || other.getClass() != getClass()) {
return false;
}
return ((User) other).name.equals(name);
}
@Override
public String toString() {
return name;
}
}
public static class StaticUserFilter implements Filter {
private User user;
private String username;
@Override
public void destroy() {
// NOTHING
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// if the user is already authenticated, don't override it
if (httpRequest.getRemoteUser() != null) {
chain.doFilter(request, response);
} else {
HttpServletRequestWrapper wrapper =
new HttpServletRequestWrapper(httpRequest) {
@Override
public Principal getUserPrincipal() {
return user;
}
@Override
public String getRemoteUser() {
return username;
}
};
chain.doFilter(wrapper, response);
}
}
@Override
public void init(FilterConfig conf) throws ServletException {
this.username = conf.getInitParameter(USERNAME_KEY);
this.user = new User(username);
}
}
@Override
public void initFilter(FilterContainer container, Configuration conf) {
HashMap<String, String> options = new HashMap<String, String>();
String username = getUsernameFromConf(conf);
options.put(USERNAME_KEY, username);
container.addFilter("static_user_filter",
StaticUserFilter.class.getName(),
options);
}
/**
* Retrieve the static username from the configuration.
*/
static String getUsernameFromConf(Configuration conf) {
String oldStyleUgi = conf.get(DEPRECATED_UGI_KEY);
if (oldStyleUgi != null) {
// We can't use the normal configuration deprecation mechanism here
// since we need to split out the username from the configured UGI.
LOG.warn(DEPRECATED_UGI_KEY + " should not be used. Instead, use " +
USERNAME_KEY + ".");
String[] parts = oldStyleUgi.split(",");
return parts[0];
} else {
return conf.get(USERNAME_KEY, USERNAME_DEFAULT);
}
}
}

View File

@ -0,0 +1,30 @@
<html>
<!--
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.
-->
<body>
This package provides user-selectable (via configuration) classes that add
functionality to the web UI. They are configured as a list of classes in the
configuration parameter <b>hadoop.http.filter.initializers</b>.
<ul>
<li> <b>StaticUserWebFilter</b> - An authorization plugin that makes all
users a static configured user.
</ul>
</body>
</html>

View File

@ -0,0 +1,80 @@
/**
* 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.http.lib;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.http.lib.StaticUserWebFilter.StaticUserFilter;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
public class TestStaticUserWebFilter {
private FilterConfig mockConfig(String username) {
FilterConfig mock = Mockito.mock(FilterConfig.class);
Mockito.doReturn(username).when(mock).getInitParameter(
StaticUserWebFilter.USERNAME_KEY);
return mock;
}
@Test
public void testFilter() throws Exception {
FilterConfig config = mockConfig("myuser");
StaticUserFilter suf = new StaticUserFilter();
suf.init(config);
ArgumentCaptor<HttpServletRequestWrapper> wrapperArg =
ArgumentCaptor.forClass(HttpServletRequestWrapper.class);
FilterChain chain = mock(FilterChain.class);
suf.doFilter(mock(HttpServletRequest.class), mock(ServletResponse.class),
chain);
Mockito.verify(chain).doFilter(wrapperArg.capture(), Mockito.<ServletResponse>anyObject());
HttpServletRequestWrapper wrapper = wrapperArg.getValue();
assertEquals("myuser", wrapper.getUserPrincipal().getName());
assertEquals("myuser", wrapper.getRemoteUser());
suf.destroy();
}
@Test
public void testOldStyleConfiguration() {
Configuration conf = new Configuration();
conf.set("dfs.web.ugi", "joe,group1,group2");
assertEquals("joe", StaticUserWebFilter.getUsernameFromConf(conf));
}
@Test
public void testConfiguration() {
Configuration conf = new Configuration();
conf.set(StaticUserWebFilter.USERNAME_KEY, "joe");
assertEquals("joe", StaticUserWebFilter.getUsernameFromConf(conf));
}
}