Return-Path: X-Original-To: apmail-hadoop-common-commits-archive@www.apache.org Delivered-To: apmail-hadoop-common-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 4586211CB4 for ; Tue, 12 Aug 2014 00:10:40 +0000 (UTC) Received: (qmail 47237 invoked by uid 500); 12 Aug 2014 00:10:40 -0000 Delivered-To: apmail-hadoop-common-commits-archive@hadoop.apache.org Received: (qmail 47171 invoked by uid 500); 12 Aug 2014 00:10:40 -0000 Mailing-List: contact common-commits-help@hadoop.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: common-dev@hadoop.apache.org Delivered-To: mailing list common-commits@hadoop.apache.org Received: (qmail 47160 invoked by uid 99); 12 Aug 2014 00:10:40 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 12 Aug 2014 00:10:40 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 12 Aug 2014 00:10:37 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 14BBE2388860; Tue, 12 Aug 2014 00:10:17 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1617384 - in /hadoop/common/trunk/hadoop-common-project: hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/ hadoop-common/ hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/ hadoop-common/... Date: Tue, 12 Aug 2014 00:10:16 -0000 To: common-commits@hadoop.apache.org From: tucu@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20140812001017.14BBE2388860@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: tucu Date: Tue Aug 12 00:10:15 2014 New Revision: 1617384 URL: http://svn.apache.org/r1617384 Log: HADOOP-10835. Implement HTTP proxyuser support in HTTP authentication client/server libraries. (tucu) Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/HttpUserGroupInformation.java Modified: hadoop/common/trunk/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java hadoop/common/trunk/hadoop-common-project/hadoop-common/CHANGES.txt hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticatedURL.java hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationFilter.java hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationHandler.java hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/token/delegation/web/TestWebDelegationToken.java Modified: hadoop/common/trunk/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java?rev=1617384&r1=1617383&r2=1617384&view=diff ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java (original) +++ hadoop/common/trunk/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java Tue Aug 12 00:10:15 2014 @@ -127,6 +127,7 @@ public class AuthenticationFilter implem public static final String SIGNATURE_PROVIDER_ATTRIBUTE = "org.apache.hadoop.security.authentication.util.SignerSecretProvider"; + private Properties config; private Signer signer; private SignerSecretProvider secretProvider; private AuthenticationHandler authHandler; @@ -150,7 +151,7 @@ public class AuthenticationFilter implem public void init(FilterConfig filterConfig) throws ServletException { String configPrefix = filterConfig.getInitParameter(CONFIG_PREFIX); configPrefix = (configPrefix != null) ? configPrefix + "." : ""; - Properties config = getConfiguration(configPrefix, filterConfig); + config = getConfiguration(configPrefix, filterConfig); String authHandlerName = config.getProperty(AUTH_TYPE, null); String authHandlerClassName; if (authHandlerName == null) { @@ -225,6 +226,17 @@ public class AuthenticationFilter implem } /** + * 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. * * @return the authentication handler being used. @@ -457,7 +469,7 @@ public class AuthenticationFilter implem createAuthCookie(httpResponse, signedToken, getCookieDomain(), getCookiePath(), token.getExpires(), isHttps); } - filterChain.doFilter(httpRequest, httpResponse); + doFilter(filterChain, httpRequest, httpResponse); } } else { unauthorizedResponse = false; @@ -482,6 +494,15 @@ public class AuthenticationFilter implem } /** + * 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. * * @param token authentication token for the cookie. Modified: hadoop/common/trunk/hadoop-common-project/hadoop-common/CHANGES.txt URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/CHANGES.txt?rev=1617384&r1=1617383&r2=1617384&view=diff ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/CHANGES.txt (original) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/CHANGES.txt Tue Aug 12 00:10:15 2014 @@ -496,7 +496,11 @@ Release 2.6.0 - UNRELEASED HADOOP-10791. AuthenticationFilter should support externalizing the 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 Modified: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticatedURL.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticatedURL.java?rev=1617384&r1=1617383&r2=1617384&view=diff ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticatedURL.java (original) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticatedURL.java Tue Aug 12 00:10:15 2014 @@ -34,6 +34,7 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.URL; +import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; @@ -61,6 +62,13 @@ import java.util.Map; public class DelegationTokenAuthenticatedURL extends AuthenticatedURL { /** + * Constant used in URL's query string to perform a proxy user request, the + * value of the DO_AS 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. */ @InterfaceAudience.Public @@ -247,6 +255,11 @@ public class DelegationTokenAuthenticate } } + // proxyuser + if (doAs != null) { + extraParams.put(DO_AS, URLEncoder.encode(doAs, "UTF-8")); + } + url = augmentURL(url, extraParams); return super.openConnection(url, token); } Modified: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationFilter.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationFilter.java?rev=1617384&r1=1617383&r2=1617384&view=diff ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationFilter.java (original) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationFilter.java Tue Aug 12 00:10:15 2014 @@ -17,17 +17,39 @@ */ 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.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.AuthenticationHandler; +import org.apache.hadoop.security.authentication.server.AuthenticationToken; import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler; 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.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.ServletContext; 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; /** @@ -49,6 +71,10 @@ import java.util.Properties; public class DelegationTokenAuthenticationFilter 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 DelegationTokenSecretManager instance to * manage creation and verification of Delegation Tokens. @@ -60,6 +86,14 @@ public class DelegationTokenAuthenticati public static final String DELEGATION_TOKEN_SECRET_MANAGER_ATTR = "hadoop.http.delegation-token-secret-manager"; + private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); + + private static final ThreadLocal UGI_TL = + new ThreadLocal(); + public static final String PROXYUSER_PREFIX = "proxyuser"; + + private SaslRpcServer.AuthMethod handlerAuthMethod; + /** * It delegates to * {@link AuthenticationFilter#getConfiguration(String, FilterConfig)} and @@ -86,17 +120,155 @@ public class DelegationTokenAuthenticati return props; } + /** + * Returns the proxyuser configuration. All returned properties must start + * with proxyuser.' + *

+ * 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 public void init(FilterConfig filterConfig) throws ServletException { super.init(filterConfig); + AuthenticationHandler handler = getAuthenticationHandler(); AbstractDelegationTokenSecretManager dtSecretManager = (AbstractDelegationTokenSecretManager) filterConfig.getServletContext(). getAttribute(DELEGATION_TOKEN_SECRET_MANAGER_ATTR); - if (dtSecretManager != null && getAuthenticationHandler() + if (dtSecretManager != null && handler instanceof DelegationTokenAuthenticationHandler) { - DelegationTokenAuthenticationHandler handler = + DelegationTokenAuthenticationHandler dtHandler = (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 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 json = new HashMap(); + 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(); + } + } + } + } Modified: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationHandler.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationHandler.java?rev=1617384&r1=1617383&r2=1617384&view=diff ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationHandler.java (original) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationHandler.java Tue Aug 12 00:10:15 2014 @@ -92,6 +92,9 @@ public abstract class DelegationTokenAut private static final Set DELEGATION_TOKEN_OPS = new HashSet(); + static final String DELEGATION_TOKEN_UGI_ATTRIBUTE = + "hadoop.security.delegation-token.ugi"; + static { DELEGATION_TOKEN_OPS.add(KerberosDelegationTokenAuthenticator. DelegationTokenOperation.GETDELEGATIONTOKEN.toString()); @@ -342,6 +345,7 @@ public abstract class DelegationTokenAut token = new AuthenticationToken(shortName, ugi.getUserName(), getType()); token.setExpires(0); + request.setAttribute(DELEGATION_TOKEN_UGI_ATTRIBUTE, ugi); } catch (Throwable ex) { throw new AuthenticationException("Could not verify DelegationToken, " + ex.toString(), ex); Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/HttpUserGroupInformation.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/HttpUserGroupInformation.java?rev=1617384&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/HttpUserGroupInformation.java (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/HttpUserGroupInformation.java Tue Aug 12 00:10:15 2014 @@ -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}, NULL if none. + */ + public static UserGroupInformation get() { + return DelegationTokenAuthenticationFilter. + getHttpUserGroupInformationInContext(); + } + +} Modified: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/token/delegation/web/TestWebDelegationToken.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/token/delegation/web/TestWebDelegationToken.java?rev=1617384&r1=1617383&r2=1617384&view=diff ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/token/delegation/web/TestWebDelegationToken.java (original) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/token/delegation/web/TestWebDelegationToken.java Tue Aug 12 00:10:15 2014 @@ -72,6 +72,10 @@ import java.util.UUID; import java.util.concurrent.Callable; 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; public static class DummyAuthenticationHandler @@ -330,13 +334,13 @@ public class TestWebDelegationToken { new DelegationTokenAuthenticatedURL(); try { - aUrl.getDelegationToken(nonAuthURL, token, "foo"); + aUrl.getDelegationToken(nonAuthURL, token, FOO_USER); Assert.fail(); } catch (Exception ex) { Assert.assertTrue(ex.getMessage().contains("401")); } - aUrl.getDelegationToken(authURL, token, "foo"); + aUrl.getDelegationToken(authURL, token, FOO_USER); Assert.assertNotNull(token.getDelegationToken()); Assert.assertEquals(new Text("token-kind"), token.getDelegationToken().getKind()); @@ -350,7 +354,7 @@ public class TestWebDelegationToken { Assert.assertTrue(ex.getMessage().contains("401")); } - aUrl.getDelegationToken(authURL, token, "foo"); + aUrl.getDelegationToken(authURL, token, FOO_USER); try { aUrl.renewDelegationToken(authURL2, token); @@ -359,15 +363,15 @@ public class TestWebDelegationToken { Assert.assertTrue(ex.getMessage().contains("403")); } - aUrl.getDelegationToken(authURL, token, "foo"); + aUrl.getDelegationToken(authURL, token, FOO_USER); aUrl.cancelDelegationToken(authURL, token); - aUrl.getDelegationToken(authURL, token, "foo"); + aUrl.getDelegationToken(authURL, token, FOO_USER); aUrl.cancelDelegationToken(nonAuthURL, token); - aUrl.getDelegationToken(authURL, token, "foo"); + aUrl.getDelegationToken(authURL, token, FOO_USER); try { aUrl.renewDelegationToken(nonAuthURL, token); @@ -416,7 +420,7 @@ public class TestWebDelegationToken { DelegationTokenAuthenticatedURL aUrl = new DelegationTokenAuthenticatedURL(); - aUrl.getDelegationToken(authURL, token, "foo"); + aUrl.getDelegationToken(authURL, token, FOO_USER); Assert.assertNotNull(token.getDelegationToken()); Assert.assertEquals(new Text("fooKind"), token.getDelegationToken().getKind()); @@ -488,7 +492,7 @@ public class TestWebDelegationToken { jetty.start(); final URL url = new URL(getJettyURL() + "/foo/bar"); - UserGroupInformation ugi = UserGroupInformation.createRemoteUser("foo"); + UserGroupInformation ugi = UserGroupInformation.createRemoteUser(FOO_USER); ugi.doAs(new PrivilegedExceptionAction() { @Override public Void run() throws Exception { @@ -501,10 +505,10 @@ public class TestWebDelegationToken { conn.getResponseCode()); List ret = IOUtils.readLines(conn.getInputStream()); Assert.assertEquals(1, ret.size()); - Assert.assertEquals("foo", ret.get(0)); + Assert.assertEquals(FOO_USER, ret.get(0)); try { - aUrl.getDelegationToken(url, token, "foo"); + aUrl.getDelegationToken(url, token, FOO_USER); Assert.fail(); } catch (AuthenticationException ex) { Assert.assertTrue(ex.getMessage().contains( @@ -531,6 +535,16 @@ public class TestWebDelegationToken { "token-kind"); 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 @@ -547,7 +561,7 @@ public class TestWebDelegationToken { jetty.start(); final URL url = new URL(getJettyURL() + "/foo/bar"); - UserGroupInformation ugi = UserGroupInformation.createRemoteUser("foo"); + UserGroupInformation ugi = UserGroupInformation.createRemoteUser(FOO_USER); ugi.doAs(new PrivilegedExceptionAction() { @Override public Void run() throws Exception { @@ -560,9 +574,9 @@ public class TestWebDelegationToken { conn.getResponseCode()); List ret = IOUtils.readLines(conn.getInputStream()); 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.assertEquals(new Text("token-kind"), token.getDelegationToken().getKind()); @@ -684,7 +698,7 @@ public class TestWebDelegationToken { final URL url = new URL(getJettyURL() + "/foo/bar"); try { - aUrl.getDelegationToken(url, token, "foo"); + aUrl.getDelegationToken(url, token, FOO_USER); Assert.fail(); } catch (AuthenticationException ex) { Assert.assertTrue(ex.getMessage().contains("GSSException")); @@ -700,7 +714,7 @@ public class TestWebDelegationToken { aUrl.renewDelegationToken(url, token); Assert.assertNotNull(token.getDelegationToken()); - aUrl.getDelegationToken(url, token, "foo"); + aUrl.getDelegationToken(url, token, FOO_USER); Assert.assertNotNull(token.getDelegationToken()); try { @@ -710,7 +724,7 @@ public class TestWebDelegationToken { Assert.assertTrue(ex.getMessage().contains("403")); } - aUrl.getDelegationToken(url, token, "foo"); + aUrl.getDelegationToken(url, token, FOO_USER); aUrl.cancelDelegationToken(url, token); 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() { + @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 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() { + @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 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(); + } + } + }