HADOOP-10835. Implement HTTP proxyuser support in HTTP authentication client/server libraries. (tucu)
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1617385 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
afa125ccfd
commit
ff6ef4a430
|
@ -127,6 +127,7 @@ public class AuthenticationFilter implements Filter {
|
||||||
public static final String SIGNATURE_PROVIDER_ATTRIBUTE =
|
public static final String SIGNATURE_PROVIDER_ATTRIBUTE =
|
||||||
"org.apache.hadoop.security.authentication.util.SignerSecretProvider";
|
"org.apache.hadoop.security.authentication.util.SignerSecretProvider";
|
||||||
|
|
||||||
|
private Properties config;
|
||||||
private Signer signer;
|
private Signer signer;
|
||||||
private SignerSecretProvider secretProvider;
|
private SignerSecretProvider secretProvider;
|
||||||
private AuthenticationHandler authHandler;
|
private AuthenticationHandler authHandler;
|
||||||
|
@ -150,7 +151,7 @@ public class AuthenticationFilter implements Filter {
|
||||||
public void init(FilterConfig filterConfig) throws ServletException {
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
String configPrefix = filterConfig.getInitParameter(CONFIG_PREFIX);
|
String configPrefix = filterConfig.getInitParameter(CONFIG_PREFIX);
|
||||||
configPrefix = (configPrefix != null) ? configPrefix + "." : "";
|
configPrefix = (configPrefix != null) ? configPrefix + "." : "";
|
||||||
Properties config = getConfiguration(configPrefix, filterConfig);
|
config = getConfiguration(configPrefix, filterConfig);
|
||||||
String authHandlerName = config.getProperty(AUTH_TYPE, null);
|
String authHandlerName = config.getProperty(AUTH_TYPE, null);
|
||||||
String authHandlerClassName;
|
String authHandlerClassName;
|
||||||
if (authHandlerName == null) {
|
if (authHandlerName == null) {
|
||||||
|
@ -224,6 +225,17 @@ public class AuthenticationFilter implements Filter {
|
||||||
cookiePath = config.getProperty(COOKIE_PATH, null);
|
cookiePath = config.getProperty(COOKIE_PATH, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configuration properties of the {@link AuthenticationFilter}
|
||||||
|
* without the prefix. The returned properties are the same that the
|
||||||
|
* {@link #getConfiguration(String, FilterConfig)} method returned.
|
||||||
|
*
|
||||||
|
* @return the configuration properties.
|
||||||
|
*/
|
||||||
|
protected Properties getConfiguration() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the authentication handler being used.
|
* Returns the authentication handler being used.
|
||||||
*
|
*
|
||||||
|
@ -457,7 +469,7 @@ public class AuthenticationFilter implements Filter {
|
||||||
createAuthCookie(httpResponse, signedToken, getCookieDomain(),
|
createAuthCookie(httpResponse, signedToken, getCookieDomain(),
|
||||||
getCookiePath(), token.getExpires(), isHttps);
|
getCookiePath(), token.getExpires(), isHttps);
|
||||||
}
|
}
|
||||||
filterChain.doFilter(httpRequest, httpResponse);
|
doFilter(filterChain, httpRequest, httpResponse);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unauthorizedResponse = false;
|
unauthorizedResponse = false;
|
||||||
|
@ -481,6 +493,15 @@ public class AuthenticationFilter implements Filter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegates call to the servlet filter chain. Sub-classes my override this
|
||||||
|
* method to perform pre and post tasks.
|
||||||
|
*/
|
||||||
|
protected void doFilter(FilterChain filterChain, HttpServletRequest request,
|
||||||
|
HttpServletResponse response) throws IOException, ServletException {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the Hadoop authentication HTTP cookie.
|
* Creates the Hadoop authentication HTTP cookie.
|
||||||
*
|
*
|
||||||
|
|
|
@ -63,7 +63,11 @@ Release 2.6.0 - UNRELEASED
|
||||||
HADOOP-10791. AuthenticationFilter should support externalizing the
|
HADOOP-10791. AuthenticationFilter should support externalizing the
|
||||||
secret for signing and provide rotation support. (rkanter via tucu)
|
secret for signing and provide rotation support. (rkanter via tucu)
|
||||||
|
|
||||||
HADOOP-10771. Refactor HTTP delegation support out of httpfs to common, PART 1. (tucu)
|
HADOOP-10771. Refactor HTTP delegation support out of httpfs to common.
|
||||||
|
(tucu)
|
||||||
|
|
||||||
|
HADOOP-10835. Implement HTTP proxyuser support in HTTP authentication
|
||||||
|
client/server libraries. (tucu)
|
||||||
|
|
||||||
OPTIMIZATIONS
|
OPTIMIZATIONS
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ import java.io.IOException;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -60,6 +61,13 @@ import java.util.Map;
|
||||||
@InterfaceStability.Unstable
|
@InterfaceStability.Unstable
|
||||||
public class DelegationTokenAuthenticatedURL extends AuthenticatedURL {
|
public class DelegationTokenAuthenticatedURL extends AuthenticatedURL {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant used in URL's query string to perform a proxy user request, the
|
||||||
|
* value of the <code>DO_AS</code> parameter is the user the request will be
|
||||||
|
* done on behalf of.
|
||||||
|
*/
|
||||||
|
static final String DO_AS = "doAs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client side authentication token that handles Delegation Tokens.
|
* Client side authentication token that handles Delegation Tokens.
|
||||||
*/
|
*/
|
||||||
|
@ -247,6 +255,11 @@ public class DelegationTokenAuthenticatedURL extends AuthenticatedURL {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// proxyuser
|
||||||
|
if (doAs != null) {
|
||||||
|
extraParams.put(DO_AS, URLEncoder.encode(doAs, "UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
url = augmentURL(url, extraParams);
|
url = augmentURL(url, extraParams);
|
||||||
return super.openConnection(url, token);
|
return super.openConnection(url, token);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,39 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.security.token.delegation.web;
|
package org.apache.hadoop.security.token.delegation.web;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.classification.InterfaceStability;
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.security.SaslRpcServer;
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
||||||
import org.apache.hadoop.security.authentication.server.AuthenticationHandler;
|
import org.apache.hadoop.security.authentication.server.AuthenticationHandler;
|
||||||
|
import org.apache.hadoop.security.authentication.server.AuthenticationToken;
|
||||||
import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
|
import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
|
||||||
import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler;
|
import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler;
|
||||||
|
import org.apache.hadoop.security.authorize.AuthorizationException;
|
||||||
|
import org.apache.hadoop.security.authorize.ProxyUsers;
|
||||||
import org.apache.hadoop.security.token.delegation.AbstractDelegationTokenSecretManager;
|
import org.apache.hadoop.security.token.delegation.AbstractDelegationTokenSecretManager;
|
||||||
|
import org.apache.http.NameValuePair;
|
||||||
|
import org.apache.http.client.utils.URLEncodedUtils;
|
||||||
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.FilterConfig;
|
import javax.servlet.FilterConfig;
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletRequestWrapper;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,6 +71,10 @@ import java.util.Properties;
|
||||||
public class DelegationTokenAuthenticationFilter
|
public class DelegationTokenAuthenticationFilter
|
||||||
extends AuthenticationFilter {
|
extends AuthenticationFilter {
|
||||||
|
|
||||||
|
private static final String APPLICATION_JSON_MIME = "application/json";
|
||||||
|
private static final String ERROR_EXCEPTION_JSON = "exception";
|
||||||
|
private static final String ERROR_MESSAGE_JSON = "message";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets an external <code>DelegationTokenSecretManager</code> instance to
|
* Sets an external <code>DelegationTokenSecretManager</code> instance to
|
||||||
* manage creation and verification of Delegation Tokens.
|
* manage creation and verification of Delegation Tokens.
|
||||||
|
@ -60,6 +86,14 @@ public class DelegationTokenAuthenticationFilter
|
||||||
public static final String DELEGATION_TOKEN_SECRET_MANAGER_ATTR =
|
public static final String DELEGATION_TOKEN_SECRET_MANAGER_ATTR =
|
||||||
"hadoop.http.delegation-token-secret-manager";
|
"hadoop.http.delegation-token-secret-manager";
|
||||||
|
|
||||||
|
private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
|
||||||
|
|
||||||
|
private static final ThreadLocal<UserGroupInformation> UGI_TL =
|
||||||
|
new ThreadLocal<UserGroupInformation>();
|
||||||
|
public static final String PROXYUSER_PREFIX = "proxyuser";
|
||||||
|
|
||||||
|
private SaslRpcServer.AuthMethod handlerAuthMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It delegates to
|
* It delegates to
|
||||||
* {@link AuthenticationFilter#getConfiguration(String, FilterConfig)} and
|
* {@link AuthenticationFilter#getConfiguration(String, FilterConfig)} and
|
||||||
|
@ -86,17 +120,155 @@ public class DelegationTokenAuthenticationFilter
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the proxyuser configuration. All returned properties must start
|
||||||
|
* with <code>proxyuser.</code>'
|
||||||
|
* <p/>
|
||||||
|
* Subclasses may override this method if the proxyuser configuration is
|
||||||
|
* read from other place than the filter init parameters.
|
||||||
|
*
|
||||||
|
* @param filterConfig filter configuration object
|
||||||
|
* @return the proxyuser configuration properties.
|
||||||
|
* @throws ServletException thrown if the configuration could not be created.
|
||||||
|
*/
|
||||||
|
protected Configuration getProxyuserConfiguration(FilterConfig filterConfig)
|
||||||
|
throws ServletException {
|
||||||
|
// this filter class gets the configuration from the filter configs, we are
|
||||||
|
// creating an empty configuration and injecting the proxyuser settings in
|
||||||
|
// it. In the initialization of the filter, the returned configuration is
|
||||||
|
// passed to the ProxyUsers which only looks for 'proxyusers.' properties.
|
||||||
|
Configuration conf = new Configuration(false);
|
||||||
|
Enumeration<?> names = filterConfig.getInitParameterNames();
|
||||||
|
while (names.hasMoreElements()) {
|
||||||
|
String name = (String) names.nextElement();
|
||||||
|
if (name.startsWith(PROXYUSER_PREFIX + ".")) {
|
||||||
|
String value = filterConfig.getInitParameter(name);
|
||||||
|
conf.set(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(FilterConfig filterConfig) throws ServletException {
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
super.init(filterConfig);
|
super.init(filterConfig);
|
||||||
|
AuthenticationHandler handler = getAuthenticationHandler();
|
||||||
AbstractDelegationTokenSecretManager dtSecretManager =
|
AbstractDelegationTokenSecretManager dtSecretManager =
|
||||||
(AbstractDelegationTokenSecretManager) filterConfig.getServletContext().
|
(AbstractDelegationTokenSecretManager) filterConfig.getServletContext().
|
||||||
getAttribute(DELEGATION_TOKEN_SECRET_MANAGER_ATTR);
|
getAttribute(DELEGATION_TOKEN_SECRET_MANAGER_ATTR);
|
||||||
if (dtSecretManager != null && getAuthenticationHandler()
|
if (dtSecretManager != null && handler
|
||||||
instanceof DelegationTokenAuthenticationHandler) {
|
instanceof DelegationTokenAuthenticationHandler) {
|
||||||
DelegationTokenAuthenticationHandler handler =
|
DelegationTokenAuthenticationHandler dtHandler =
|
||||||
(DelegationTokenAuthenticationHandler) getAuthenticationHandler();
|
(DelegationTokenAuthenticationHandler) getAuthenticationHandler();
|
||||||
handler.setExternalDelegationTokenSecretManager(dtSecretManager);
|
dtHandler.setExternalDelegationTokenSecretManager(dtSecretManager);
|
||||||
|
}
|
||||||
|
if (handler instanceof PseudoAuthenticationHandler ||
|
||||||
|
handler instanceof PseudoDelegationTokenAuthenticationHandler) {
|
||||||
|
setHandlerAuthMethod(SaslRpcServer.AuthMethod.SIMPLE);
|
||||||
|
}
|
||||||
|
if (handler instanceof KerberosAuthenticationHandler ||
|
||||||
|
handler instanceof KerberosDelegationTokenAuthenticationHandler) {
|
||||||
|
setHandlerAuthMethod(SaslRpcServer.AuthMethod.KERBEROS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// proxyuser configuration
|
||||||
|
Configuration conf = getProxyuserConfiguration(filterConfig);
|
||||||
|
ProxyUsers.refreshSuperUserGroupsConfiguration(conf, PROXYUSER_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setHandlerAuthMethod(SaslRpcServer.AuthMethod authMethod) {
|
||||||
|
this.handlerAuthMethod = authMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static String getDoAs(HttpServletRequest request) {
|
||||||
|
List<NameValuePair> list = URLEncodedUtils.parse(request.getQueryString(),
|
||||||
|
UTF8_CHARSET);
|
||||||
|
if (list != null) {
|
||||||
|
for (NameValuePair nv : list) {
|
||||||
|
if (DelegationTokenAuthenticatedURL.DO_AS.equals(nv.getName())) {
|
||||||
|
return nv.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UserGroupInformation getHttpUserGroupInformationInContext() {
|
||||||
|
return UGI_TL.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilter(FilterChain filterChain, HttpServletRequest request,
|
||||||
|
HttpServletResponse response) throws IOException, ServletException {
|
||||||
|
boolean requestCompleted = false;
|
||||||
|
UserGroupInformation ugi = null;
|
||||||
|
AuthenticationToken authToken = (AuthenticationToken)
|
||||||
|
request.getUserPrincipal();
|
||||||
|
if (authToken != null && authToken != AuthenticationToken.ANONYMOUS) {
|
||||||
|
// if the request was authenticated because of a delegation token,
|
||||||
|
// then we ignore proxyuser (this is the same as the RPC behavior).
|
||||||
|
ugi = (UserGroupInformation) request.getAttribute(
|
||||||
|
DelegationTokenAuthenticationHandler.DELEGATION_TOKEN_UGI_ATTRIBUTE);
|
||||||
|
if (ugi == null) {
|
||||||
|
String realUser = request.getUserPrincipal().getName();
|
||||||
|
ugi = UserGroupInformation.createRemoteUser(realUser,
|
||||||
|
handlerAuthMethod);
|
||||||
|
String doAsUser = getDoAs(request);
|
||||||
|
if (doAsUser != null) {
|
||||||
|
ugi = UserGroupInformation.createProxyUser(doAsUser, ugi);
|
||||||
|
try {
|
||||||
|
ProxyUsers.authorize(ugi, request.getRemoteHost());
|
||||||
|
} catch (AuthorizationException ex) {
|
||||||
|
String msg = String.format(
|
||||||
|
"User '%s' from host '%s' not allowed to impersonate user '%s'",
|
||||||
|
realUser, request.getRemoteHost(), doAsUser);
|
||||||
|
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
response.setContentType(APPLICATION_JSON_MIME);
|
||||||
|
Map<String, String> json = new HashMap<String, String>();
|
||||||
|
json.put(ERROR_EXCEPTION_JSON,
|
||||||
|
AuthorizationException.class.getName());
|
||||||
|
json.put(ERROR_MESSAGE_JSON, msg);
|
||||||
|
Writer writer = response.getWriter();
|
||||||
|
ObjectMapper jsonMapper = new ObjectMapper();
|
||||||
|
jsonMapper.writeValue(writer, json);
|
||||||
|
requestCompleted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UGI_TL.set(ugi);
|
||||||
|
}
|
||||||
|
if (!requestCompleted) {
|
||||||
|
final UserGroupInformation ugiF = ugi;
|
||||||
|
try {
|
||||||
|
request = new HttpServletRequestWrapper(request) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthType() {
|
||||||
|
return (ugiF != null) ? handlerAuthMethod.toString() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRemoteUser() {
|
||||||
|
return (ugiF != null) ? ugiF.getShortUserName() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Principal getUserPrincipal() {
|
||||||
|
return (ugiF != null) ? new Principal() {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return ugiF.getUserName();
|
||||||
|
}
|
||||||
|
} : null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
super.doFilter(filterChain, request, response);
|
||||||
|
} finally {
|
||||||
|
UGI_TL.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,9 @@ public abstract class DelegationTokenAuthenticationHandler
|
||||||
|
|
||||||
private static final Set<String> DELEGATION_TOKEN_OPS = new HashSet<String>();
|
private static final Set<String> DELEGATION_TOKEN_OPS = new HashSet<String>();
|
||||||
|
|
||||||
|
static final String DELEGATION_TOKEN_UGI_ATTRIBUTE =
|
||||||
|
"hadoop.security.delegation-token.ugi";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
DELEGATION_TOKEN_OPS.add(KerberosDelegationTokenAuthenticator.
|
DELEGATION_TOKEN_OPS.add(KerberosDelegationTokenAuthenticator.
|
||||||
DelegationTokenOperation.GETDELEGATIONTOKEN.toString());
|
DelegationTokenOperation.GETDELEGATIONTOKEN.toString());
|
||||||
|
@ -342,6 +345,7 @@ public abstract class DelegationTokenAuthenticationHandler
|
||||||
token = new AuthenticationToken(shortName, ugi.getUserName(),
|
token = new AuthenticationToken(shortName, ugi.getUserName(),
|
||||||
getType());
|
getType());
|
||||||
token.setExpires(0);
|
token.setExpires(0);
|
||||||
|
request.setAttribute(DELEGATION_TOKEN_UGI_ATTRIBUTE, ugi);
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
throw new AuthenticationException("Could not verify DelegationToken, " +
|
throw new AuthenticationException("Could not verify DelegationToken, " +
|
||||||
ex.toString(), ex);
|
ex.toString(), ex);
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* 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.token.delegation.web;
|
||||||
|
|
||||||
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Util class that returns the remote {@link UserGroupInformation} in scope
|
||||||
|
* for the HTTP request.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class HttpUserGroupInformation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the remote {@link UserGroupInformation} in context for the current
|
||||||
|
* HTTP request, taking into account proxy user requests.
|
||||||
|
*
|
||||||
|
* @return the remote {@link UserGroupInformation}, <code>NULL</code> if none.
|
||||||
|
*/
|
||||||
|
public static UserGroupInformation get() {
|
||||||
|
return DelegationTokenAuthenticationFilter.
|
||||||
|
getHttpUserGroupInformationInContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -72,6 +72,10 @@ import java.util.UUID;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
public class TestWebDelegationToken {
|
public class TestWebDelegationToken {
|
||||||
|
private static final String OK_USER = "ok-user";
|
||||||
|
private static final String FAIL_USER = "fail-user";
|
||||||
|
private static final String FOO_USER = "foo";
|
||||||
|
|
||||||
private Server jetty;
|
private Server jetty;
|
||||||
|
|
||||||
public static class DummyAuthenticationHandler
|
public static class DummyAuthenticationHandler
|
||||||
|
@ -330,13 +334,13 @@ public class TestWebDelegationToken {
|
||||||
new DelegationTokenAuthenticatedURL();
|
new DelegationTokenAuthenticatedURL();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
aUrl.getDelegationToken(nonAuthURL, token, "foo");
|
aUrl.getDelegationToken(nonAuthURL, token, FOO_USER);
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Assert.assertTrue(ex.getMessage().contains("401"));
|
Assert.assertTrue(ex.getMessage().contains("401"));
|
||||||
}
|
}
|
||||||
|
|
||||||
aUrl.getDelegationToken(authURL, token, "foo");
|
aUrl.getDelegationToken(authURL, token, FOO_USER);
|
||||||
Assert.assertNotNull(token.getDelegationToken());
|
Assert.assertNotNull(token.getDelegationToken());
|
||||||
Assert.assertEquals(new Text("token-kind"),
|
Assert.assertEquals(new Text("token-kind"),
|
||||||
token.getDelegationToken().getKind());
|
token.getDelegationToken().getKind());
|
||||||
|
@ -350,7 +354,7 @@ public class TestWebDelegationToken {
|
||||||
Assert.assertTrue(ex.getMessage().contains("401"));
|
Assert.assertTrue(ex.getMessage().contains("401"));
|
||||||
}
|
}
|
||||||
|
|
||||||
aUrl.getDelegationToken(authURL, token, "foo");
|
aUrl.getDelegationToken(authURL, token, FOO_USER);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
aUrl.renewDelegationToken(authURL2, token);
|
aUrl.renewDelegationToken(authURL2, token);
|
||||||
|
@ -359,15 +363,15 @@ public class TestWebDelegationToken {
|
||||||
Assert.assertTrue(ex.getMessage().contains("403"));
|
Assert.assertTrue(ex.getMessage().contains("403"));
|
||||||
}
|
}
|
||||||
|
|
||||||
aUrl.getDelegationToken(authURL, token, "foo");
|
aUrl.getDelegationToken(authURL, token, FOO_USER);
|
||||||
|
|
||||||
aUrl.cancelDelegationToken(authURL, token);
|
aUrl.cancelDelegationToken(authURL, token);
|
||||||
|
|
||||||
aUrl.getDelegationToken(authURL, token, "foo");
|
aUrl.getDelegationToken(authURL, token, FOO_USER);
|
||||||
|
|
||||||
aUrl.cancelDelegationToken(nonAuthURL, token);
|
aUrl.cancelDelegationToken(nonAuthURL, token);
|
||||||
|
|
||||||
aUrl.getDelegationToken(authURL, token, "foo");
|
aUrl.getDelegationToken(authURL, token, FOO_USER);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
aUrl.renewDelegationToken(nonAuthURL, token);
|
aUrl.renewDelegationToken(nonAuthURL, token);
|
||||||
|
@ -416,7 +420,7 @@ public class TestWebDelegationToken {
|
||||||
DelegationTokenAuthenticatedURL aUrl =
|
DelegationTokenAuthenticatedURL aUrl =
|
||||||
new DelegationTokenAuthenticatedURL();
|
new DelegationTokenAuthenticatedURL();
|
||||||
|
|
||||||
aUrl.getDelegationToken(authURL, token, "foo");
|
aUrl.getDelegationToken(authURL, token, FOO_USER);
|
||||||
Assert.assertNotNull(token.getDelegationToken());
|
Assert.assertNotNull(token.getDelegationToken());
|
||||||
Assert.assertEquals(new Text("fooKind"),
|
Assert.assertEquals(new Text("fooKind"),
|
||||||
token.getDelegationToken().getKind());
|
token.getDelegationToken().getKind());
|
||||||
|
@ -488,7 +492,7 @@ public class TestWebDelegationToken {
|
||||||
jetty.start();
|
jetty.start();
|
||||||
final URL url = new URL(getJettyURL() + "/foo/bar");
|
final URL url = new URL(getJettyURL() + "/foo/bar");
|
||||||
|
|
||||||
UserGroupInformation ugi = UserGroupInformation.createRemoteUser("foo");
|
UserGroupInformation ugi = UserGroupInformation.createRemoteUser(FOO_USER);
|
||||||
ugi.doAs(new PrivilegedExceptionAction<Void>() {
|
ugi.doAs(new PrivilegedExceptionAction<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void run() throws Exception {
|
public Void run() throws Exception {
|
||||||
|
@ -501,10 +505,10 @@ public class TestWebDelegationToken {
|
||||||
conn.getResponseCode());
|
conn.getResponseCode());
|
||||||
List<String> ret = IOUtils.readLines(conn.getInputStream());
|
List<String> ret = IOUtils.readLines(conn.getInputStream());
|
||||||
Assert.assertEquals(1, ret.size());
|
Assert.assertEquals(1, ret.size());
|
||||||
Assert.assertEquals("foo", ret.get(0));
|
Assert.assertEquals(FOO_USER, ret.get(0));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
aUrl.getDelegationToken(url, token, "foo");
|
aUrl.getDelegationToken(url, token, FOO_USER);
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
} catch (AuthenticationException ex) {
|
} catch (AuthenticationException ex) {
|
||||||
Assert.assertTrue(ex.getMessage().contains(
|
Assert.assertTrue(ex.getMessage().contains(
|
||||||
|
@ -531,6 +535,16 @@ public class TestWebDelegationToken {
|
||||||
"token-kind");
|
"token-kind");
|
||||||
return conf;
|
return conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected org.apache.hadoop.conf.Configuration getProxyuserConfiguration(
|
||||||
|
FilterConfig filterConfig) throws ServletException {
|
||||||
|
org.apache.hadoop.conf.Configuration conf =
|
||||||
|
new org.apache.hadoop.conf.Configuration(false);
|
||||||
|
conf.set("proxyuser.foo.users", OK_USER);
|
||||||
|
conf.set("proxyuser.foo.hosts", "localhost");
|
||||||
|
return conf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -547,7 +561,7 @@ public class TestWebDelegationToken {
|
||||||
jetty.start();
|
jetty.start();
|
||||||
final URL url = new URL(getJettyURL() + "/foo/bar");
|
final URL url = new URL(getJettyURL() + "/foo/bar");
|
||||||
|
|
||||||
UserGroupInformation ugi = UserGroupInformation.createRemoteUser("foo");
|
UserGroupInformation ugi = UserGroupInformation.createRemoteUser(FOO_USER);
|
||||||
ugi.doAs(new PrivilegedExceptionAction<Void>() {
|
ugi.doAs(new PrivilegedExceptionAction<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void run() throws Exception {
|
public Void run() throws Exception {
|
||||||
|
@ -560,9 +574,9 @@ public class TestWebDelegationToken {
|
||||||
conn.getResponseCode());
|
conn.getResponseCode());
|
||||||
List<String> ret = IOUtils.readLines(conn.getInputStream());
|
List<String> ret = IOUtils.readLines(conn.getInputStream());
|
||||||
Assert.assertEquals(1, ret.size());
|
Assert.assertEquals(1, ret.size());
|
||||||
Assert.assertEquals("foo", ret.get(0));
|
Assert.assertEquals(FOO_USER, ret.get(0));
|
||||||
|
|
||||||
aUrl.getDelegationToken(url, token, "foo");
|
aUrl.getDelegationToken(url, token, FOO_USER);
|
||||||
Assert.assertNotNull(token.getDelegationToken());
|
Assert.assertNotNull(token.getDelegationToken());
|
||||||
Assert.assertEquals(new Text("token-kind"),
|
Assert.assertEquals(new Text("token-kind"),
|
||||||
token.getDelegationToken().getKind());
|
token.getDelegationToken().getKind());
|
||||||
|
@ -684,7 +698,7 @@ public class TestWebDelegationToken {
|
||||||
final URL url = new URL(getJettyURL() + "/foo/bar");
|
final URL url = new URL(getJettyURL() + "/foo/bar");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
aUrl.getDelegationToken(url, token, "foo");
|
aUrl.getDelegationToken(url, token, FOO_USER);
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
} catch (AuthenticationException ex) {
|
} catch (AuthenticationException ex) {
|
||||||
Assert.assertTrue(ex.getMessage().contains("GSSException"));
|
Assert.assertTrue(ex.getMessage().contains("GSSException"));
|
||||||
|
@ -700,7 +714,7 @@ public class TestWebDelegationToken {
|
||||||
aUrl.renewDelegationToken(url, token);
|
aUrl.renewDelegationToken(url, token);
|
||||||
Assert.assertNotNull(token.getDelegationToken());
|
Assert.assertNotNull(token.getDelegationToken());
|
||||||
|
|
||||||
aUrl.getDelegationToken(url, token, "foo");
|
aUrl.getDelegationToken(url, token, FOO_USER);
|
||||||
Assert.assertNotNull(token.getDelegationToken());
|
Assert.assertNotNull(token.getDelegationToken());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -710,7 +724,7 @@ public class TestWebDelegationToken {
|
||||||
Assert.assertTrue(ex.getMessage().contains("403"));
|
Assert.assertTrue(ex.getMessage().contains("403"));
|
||||||
}
|
}
|
||||||
|
|
||||||
aUrl.getDelegationToken(url, token, "foo");
|
aUrl.getDelegationToken(url, token, FOO_USER);
|
||||||
|
|
||||||
aUrl.cancelDelegationToken(url, token);
|
aUrl.cancelDelegationToken(url, token);
|
||||||
Assert.assertNull(token.getDelegationToken());
|
Assert.assertNull(token.getDelegationToken());
|
||||||
|
@ -724,4 +738,132 @@ public class TestWebDelegationToken {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProxyUser() throws Exception {
|
||||||
|
final Server jetty = createJettyServer();
|
||||||
|
Context context = new Context();
|
||||||
|
context.setContextPath("/foo");
|
||||||
|
jetty.setHandler(context);
|
||||||
|
context.addFilter(new FilterHolder(PseudoDTAFilter.class), "/*", 0);
|
||||||
|
context.addServlet(new ServletHolder(UserServlet.class), "/bar");
|
||||||
|
|
||||||
|
try {
|
||||||
|
jetty.start();
|
||||||
|
final URL url = new URL(getJettyURL() + "/foo/bar");
|
||||||
|
|
||||||
|
UserGroupInformation ugi = UserGroupInformation.createRemoteUser(FOO_USER);
|
||||||
|
ugi.doAs(new PrivilegedExceptionAction<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void run() throws Exception {
|
||||||
|
DelegationTokenAuthenticatedURL.Token token =
|
||||||
|
new DelegationTokenAuthenticatedURL.Token();
|
||||||
|
DelegationTokenAuthenticatedURL aUrl =
|
||||||
|
new DelegationTokenAuthenticatedURL();
|
||||||
|
|
||||||
|
// proxyuser using authentication handler authentication
|
||||||
|
HttpURLConnection conn = aUrl.openConnection(url, token, OK_USER);
|
||||||
|
Assert.assertEquals(HttpURLConnection.HTTP_OK,
|
||||||
|
conn.getResponseCode());
|
||||||
|
List<String> ret = IOUtils.readLines(conn.getInputStream());
|
||||||
|
Assert.assertEquals(1, ret.size());
|
||||||
|
Assert.assertEquals(OK_USER, ret.get(0));
|
||||||
|
|
||||||
|
// unauthorized proxy user using authentication handler authentication
|
||||||
|
conn = aUrl.openConnection(url, token, FAIL_USER);
|
||||||
|
Assert.assertEquals(HttpURLConnection.HTTP_FORBIDDEN,
|
||||||
|
conn.getResponseCode());
|
||||||
|
|
||||||
|
// proxy using delegation token authentication
|
||||||
|
aUrl.getDelegationToken(url, token, FOO_USER);
|
||||||
|
|
||||||
|
UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
|
||||||
|
ugi.addToken(token.getDelegationToken());
|
||||||
|
token = new DelegationTokenAuthenticatedURL.Token();
|
||||||
|
|
||||||
|
// requests using delegation token as auth do not honor doAs
|
||||||
|
conn = aUrl.openConnection(url, token, OK_USER);
|
||||||
|
Assert.assertEquals(HttpURLConnection.HTTP_OK,
|
||||||
|
conn.getResponseCode());
|
||||||
|
ret = IOUtils.readLines(conn.getInputStream());
|
||||||
|
Assert.assertEquals(1, ret.size());
|
||||||
|
Assert.assertEquals(FOO_USER, ret.get(0));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
jetty.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class UGIServlet extends HttpServlet {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
UserGroupInformation ugi = HttpUserGroupInformation.get();
|
||||||
|
if (ugi != null) {
|
||||||
|
String ret = "remoteuser=" + req.getRemoteUser() + ":ugi=" +
|
||||||
|
ugi.getShortUserName();
|
||||||
|
if (ugi.getAuthenticationMethod() ==
|
||||||
|
UserGroupInformation.AuthenticationMethod.PROXY) {
|
||||||
|
ret = "realugi=" + ugi.getRealUser().getShortUserName() + ":" + ret;
|
||||||
|
}
|
||||||
|
resp.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
resp.getWriter().write(ret);
|
||||||
|
} else {
|
||||||
|
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHttpUGI() throws Exception {
|
||||||
|
final Server jetty = createJettyServer();
|
||||||
|
Context context = new Context();
|
||||||
|
context.setContextPath("/foo");
|
||||||
|
jetty.setHandler(context);
|
||||||
|
context.addFilter(new FilterHolder(PseudoDTAFilter.class), "/*", 0);
|
||||||
|
context.addServlet(new ServletHolder(UGIServlet.class), "/bar");
|
||||||
|
|
||||||
|
try {
|
||||||
|
jetty.start();
|
||||||
|
final URL url = new URL(getJettyURL() + "/foo/bar");
|
||||||
|
|
||||||
|
UserGroupInformation ugi = UserGroupInformation.createRemoteUser(FOO_USER);
|
||||||
|
ugi.doAs(new PrivilegedExceptionAction<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void run() throws Exception {
|
||||||
|
DelegationTokenAuthenticatedURL.Token token =
|
||||||
|
new DelegationTokenAuthenticatedURL.Token();
|
||||||
|
DelegationTokenAuthenticatedURL aUrl =
|
||||||
|
new DelegationTokenAuthenticatedURL();
|
||||||
|
|
||||||
|
// user foo
|
||||||
|
HttpURLConnection conn = aUrl.openConnection(url, token);
|
||||||
|
Assert.assertEquals(HttpURLConnection.HTTP_OK,
|
||||||
|
conn.getResponseCode());
|
||||||
|
List<String> ret = IOUtils.readLines(conn.getInputStream());
|
||||||
|
Assert.assertEquals(1, ret.size());
|
||||||
|
Assert.assertEquals("remoteuser=" + FOO_USER+ ":ugi=" + FOO_USER,
|
||||||
|
ret.get(0));
|
||||||
|
|
||||||
|
// user ok-user via proxyuser foo
|
||||||
|
conn = aUrl.openConnection(url, token, OK_USER);
|
||||||
|
Assert.assertEquals(HttpURLConnection.HTTP_OK,
|
||||||
|
conn.getResponseCode());
|
||||||
|
ret = IOUtils.readLines(conn.getInputStream());
|
||||||
|
Assert.assertEquals(1, ret.size());
|
||||||
|
Assert.assertEquals("realugi=" + FOO_USER +":remoteuser=" + OK_USER +
|
||||||
|
":ugi=" + OK_USER, ret.get(0));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
jetty.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue