Repository: hadoop Updated Branches: refs/heads/branch-2.6 821766529 -> db2bfed83
HADOOP-11207. Enhanced common DelegationTokenAuthenticationHandler to support proxy-users on Delegation-token management operations. Contributed by Zhijie Shen. (cherry picked from commit 11375578162d77b78cc3f7a82f2495b1e31a3656) Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/db2bfed8 Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/db2bfed8 Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/db2bfed8 Branch: refs/heads/branch-2.6 Commit: db2bfed8391bf7ff4fec443a691ef53e395c46d8 Parents: 8217665 Author: Vinod Kumar Vavilapalli <[email protected]> Authored: Fri Oct 17 15:56:07 2014 -0700 Committer: Vinod Kumar Vavilapalli <[email protected]> Committed: Fri Oct 17 15:57:39 2014 -0700 ---------------------------------------------------------------------- hadoop-common-project/hadoop-common/CHANGES.txt | 3 + .../web/DelegationTokenAuthenticatedURL.java | 58 +++++++++++++++- .../DelegationTokenAuthenticationHandler.java | 45 +++++++++---- .../web/DelegationTokenAuthenticator.java | 71 ++++++++++++++++++-- .../delegation/web/TestWebDelegationToken.java | 56 +++++++++++++-- 5 files changed, 205 insertions(+), 28 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hadoop/blob/db2bfed8/hadoop-common-project/hadoop-common/CHANGES.txt ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index d4262f5..12ae681 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -232,6 +232,9 @@ Release 2.6.0 - UNRELEASED HADOOP-11181. Generalized o.a.h.s.t.d.DelegationTokenManager to handle all sub-classes of AbstractDelegationTokenIdentifier. (zjshen) + HADOOP-11207. Enhanced common DelegationTokenAuthenticationHandler to support + proxy-users on Delegation-token management operations. (Zhijie Shen via + vinodkv) OPTIMIZATIONS http://git-wip-us.apache.org/repos/asf/hadoop/blob/db2bfed8/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticatedURL.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticatedURL.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticatedURL.java index 8c7cbdf..7ed0f26 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticatedURL.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticatedURL.java @@ -333,6 +333,7 @@ public class DelegationTokenAuthenticatedURL extends AuthenticatedURL { * supported. * @param token the authentication token being used for the user where the * Delegation token will be stored. + * @param renewer the renewer user. * @return a delegation token. * @throws IOException if an IO error occurred. * @throws AuthenticationException if an authentication exception occurred. @@ -340,12 +341,32 @@ public class DelegationTokenAuthenticatedURL extends AuthenticatedURL { public org.apache.hadoop.security.token.Token<AbstractDelegationTokenIdentifier> getDelegationToken(URL url, Token token, String renewer) throws IOException, AuthenticationException { + return getDelegationToken(url, token, renewer, null); + } + + /** + * Requests a delegation token using the configured <code>Authenticator</code> + * for authentication. + * + * @param url the URL to get the delegation token from. Only HTTP/S URLs are + * supported. + * @param token the authentication token being used for the user where the + * Delegation token will be stored. + * @param renewer the renewer user. + * @param doAsUser the user to do as, which will be the token owner. + * @return a delegation token. + * @throws IOException if an IO error occurred. + * @throws AuthenticationException if an authentication exception occurred. + */ + public org.apache.hadoop.security.token.Token<AbstractDelegationTokenIdentifier> + getDelegationToken(URL url, Token token, String renewer, String doAsUser) + throws IOException, AuthenticationException { Preconditions.checkNotNull(url, "url"); Preconditions.checkNotNull(token, "token"); try { token.delegationToken = ((KerberosDelegationTokenAuthenticator) getAuthenticator()). - getDelegationToken(url, token, renewer); + getDelegationToken(url, token, renewer, doAsUser); return token.delegationToken; } catch (IOException ex) { token.delegationToken = null; @@ -365,13 +386,29 @@ public class DelegationTokenAuthenticatedURL extends AuthenticatedURL { */ public long renewDelegationToken(URL url, Token token) throws IOException, AuthenticationException { + return renewDelegationToken(url, token, null); + } + + /** + * Renews a delegation token from the server end-point using the + * configured <code>Authenticator</code> for authentication. + * + * @param url the URL to renew the delegation token from. Only HTTP/S URLs are + * supported. + * @param token the authentication token with the Delegation Token to renew. + * @param doAsUser the user to do as, which will be the token owner. + * @throws IOException if an IO error occurred. + * @throws AuthenticationException if an authentication exception occurred. + */ + public long renewDelegationToken(URL url, Token token, String doAsUser) + throws IOException, AuthenticationException { Preconditions.checkNotNull(url, "url"); Preconditions.checkNotNull(token, "token"); Preconditions.checkNotNull(token.delegationToken, "No delegation token available"); try { return ((KerberosDelegationTokenAuthenticator) getAuthenticator()). - renewDelegationToken(url, token, token.delegationToken); + renewDelegationToken(url, token, token.delegationToken, doAsUser); } catch (IOException ex) { token.delegationToken = null; throw ex; @@ -389,13 +426,28 @@ public class DelegationTokenAuthenticatedURL extends AuthenticatedURL { */ public void cancelDelegationToken(URL url, Token token) throws IOException { + cancelDelegationToken(url, token, null); + } + + /** + * Cancels a delegation token from the server end-point. It does not require + * being authenticated by the configured <code>Authenticator</code>. + * + * @param url the URL to cancel the delegation token from. Only HTTP/S URLs + * are supported. + * @param token the authentication token with the Delegation Token to cancel. + * @param doAsUser the user to do as, which will be the token owner. + * @throws IOException if an IO error occurred. + */ + public void cancelDelegationToken(URL url, Token token, String doAsUser) + throws IOException { Preconditions.checkNotNull(url, "url"); Preconditions.checkNotNull(token, "token"); Preconditions.checkNotNull(token.delegationToken, "No delegation token available"); try { ((KerberosDelegationTokenAuthenticator) getAuthenticator()). - cancelDelegationToken(url, token, token.delegationToken); + cancelDelegationToken(url, token, token.delegationToken, doAsUser); } finally { token.delegationToken = null; } http://git-wip-us.apache.org/repos/asf/hadoop/blob/db2bfed8/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationHandler.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationHandler.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationHandler.java index 8321c5e..c18b5d3 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationHandler.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationHandler.java @@ -17,7 +17,21 @@ */ package org.apache.hadoop.security.token.delegation.web; -import com.google.common.annotations.VisibleForTesting; +import java.io.IOException; +import java.io.Writer; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.MediaType; + import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.conf.Configuration; @@ -27,25 +41,15 @@ import org.apache.hadoop.security.authentication.client.AuthenticationException; 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.authorize.AuthorizationException; +import org.apache.hadoop.security.authorize.ProxyUsers; import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.delegation.AbstractDelegationTokenIdentifier; import org.apache.hadoop.security.token.delegation.AbstractDelegationTokenSecretManager; import org.apache.hadoop.util.HttpExceptionUtils; import org.codehaus.jackson.map.ObjectMapper; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.core.MediaType; -import java.io.IOException; -import java.io.Writer; -import java.text.MessageFormat; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Properties; -import java.util.Set; +import com.google.common.annotations.VisibleForTesting; /** * An {@link AuthenticationHandler} that implements Kerberos SPNEGO mechanism @@ -188,6 +192,19 @@ public abstract class DelegationTokenAuthenticationHandler UserGroupInformation requestUgi = (token != null) ? UserGroupInformation.createRemoteUser(token.getUserName()) : null; + // Create the proxy user if doAsUser exists + String doAsUser = DelegationTokenAuthenticationFilter.getDoAs(request); + if (requestUgi != null && doAsUser != null) { + requestUgi = UserGroupInformation.createProxyUser( + doAsUser, requestUgi); + try { + ProxyUsers.authorize(requestUgi, request.getRemoteHost()); + } catch (AuthorizationException ex) { + HttpExceptionUtils.createServletExceptionResponse(response, + HttpServletResponse.SC_FORBIDDEN, ex); + return false; + } + } Map map = null; switch (dtOp) { case GETDELEGATIONTOKEN: http://git-wip-us.apache.org/repos/asf/hadoop/blob/db2bfed8/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticator.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticator.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticator.java index c614ee3..d93f7ac 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticator.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticator.java @@ -136,14 +136,35 @@ public abstract class DelegationTokenAuthenticator implements Authenticator { * supported. * @param token the authentication token being used for the user where the * Delegation token will be stored. + * @param renewer the renewer user. * @throws IOException if an IO error occurred. * @throws AuthenticationException if an authentication exception occurred. */ public Token<AbstractDelegationTokenIdentifier> getDelegationToken(URL url, AuthenticatedURL.Token token, String renewer) throws IOException, AuthenticationException { + return getDelegationToken(url, token, renewer, null); + } + + /** + * Requests a delegation token using the configured <code>Authenticator</code> + * for authentication. + * + * @param url the URL to get the delegation token from. Only HTTP/S URLs are + * supported. + * @param token the authentication token being used for the user where the + * Delegation token will be stored. + * @param renewer the renewer user. + * @param doAsUser the user to do as, which will be the token owner. + * @throws IOException if an IO error occurred. + * @throws AuthenticationException if an authentication exception occurred. + */ + public Token<AbstractDelegationTokenIdentifier> getDelegationToken(URL url, + AuthenticatedURL.Token token, String renewer, String doAsUser) + throws IOException, AuthenticationException { Map json = doDelegationTokenOperation(url, token, - DelegationTokenOperation.GETDELEGATIONTOKEN, renewer, null, true); + DelegationTokenOperation.GETDELEGATIONTOKEN, renewer, null, true, + doAsUser); json = (Map) json.get(DELEGATION_TOKEN_JSON); String tokenStr = (String) json.get(DELEGATION_TOKEN_URL_STRING_JSON); Token<AbstractDelegationTokenIdentifier> dToken = @@ -169,8 +190,27 @@ public abstract class DelegationTokenAuthenticator implements Authenticator { AuthenticatedURL.Token token, Token<AbstractDelegationTokenIdentifier> dToken) throws IOException, AuthenticationException { + return renewDelegationToken(url, token, dToken, null); + } + + /** + * Renews a delegation token from the server end-point using the + * configured <code>Authenticator</code> for authentication. + * + * @param url the URL to renew the delegation token from. Only HTTP/S URLs are + * supported. + * @param token the authentication token with the Delegation Token to renew. + * @param doAsUser the user to do as, which will be the token owner. + * @throws IOException if an IO error occurred. + * @throws AuthenticationException if an authentication exception occurred. + */ + public long renewDelegationToken(URL url, + AuthenticatedURL.Token token, + Token<AbstractDelegationTokenIdentifier> dToken, String doAsUser) + throws IOException, AuthenticationException { Map json = doDelegationTokenOperation(url, token, - DelegationTokenOperation.RENEWDELEGATIONTOKEN, null, dToken, true); + DelegationTokenOperation.RENEWDELEGATIONTOKEN, null, dToken, true, + doAsUser); return (Long) json.get(RENEW_DELEGATION_TOKEN_JSON); } @@ -187,9 +227,27 @@ public abstract class DelegationTokenAuthenticator implements Authenticator { AuthenticatedURL.Token token, Token<AbstractDelegationTokenIdentifier> dToken) throws IOException { + cancelDelegationToken(url, token, dToken, null); + } + + /** + * Cancels a delegation token from the server end-point. It does not require + * being authenticated by the configured <code>Authenticator</code>. + * + * @param url the URL to cancel the delegation token from. Only HTTP/S URLs + * are supported. + * @param token the authentication token with the Delegation Token to cancel. + * @param doAsUser the user to do as, which will be the token owner. + * @throws IOException if an IO error occurred. + */ + public void cancelDelegationToken(URL url, + AuthenticatedURL.Token token, + Token<AbstractDelegationTokenIdentifier> dToken, String doAsUser) + throws IOException { try { doDelegationTokenOperation(url, token, - DelegationTokenOperation.CANCELDELEGATIONTOKEN, null, dToken, false); + DelegationTokenOperation.CANCELDELEGATIONTOKEN, null, dToken, false, + doAsUser); } catch (AuthenticationException ex) { throw new IOException("This should not happen: " + ex.getMessage(), ex); } @@ -197,7 +255,7 @@ public abstract class DelegationTokenAuthenticator implements Authenticator { private Map doDelegationTokenOperation(URL url, AuthenticatedURL.Token token, DelegationTokenOperation operation, - String renewer, Token<?> dToken, boolean hasResponse) + String renewer, Token<?> dToken, boolean hasResponse, String doAsUser) throws IOException, AuthenticationException { Map ret = null; Map<String, String> params = new HashMap<String, String>(); @@ -208,6 +266,11 @@ public abstract class DelegationTokenAuthenticator implements Authenticator { if (dToken != null) { params.put(TOKEN_PARAM, dToken.encodeToUrlString()); } + // proxyuser + if (doAsUser != null) { + params.put(DelegationTokenAuthenticatedURL.DO_AS, + URLEncoder.encode(doAsUser, "UTF-8")); + } String urlStr = url.toExternalForm(); StringBuilder sb = new StringBuilder(urlStr); String separator = (urlStr.contains("?")) ? "&" : "?"; http://git-wip-us.apache.org/repos/asf/hadoop/blob/db2bfed8/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/token/delegation/web/TestWebDelegationToken.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/token/delegation/web/TestWebDelegationToken.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/token/delegation/web/TestWebDelegationToken.java index 189a334..87c3105 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/token/delegation/web/TestWebDelegationToken.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/token/delegation/web/TestWebDelegationToken.java @@ -52,6 +52,9 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.io.Writer; @@ -648,6 +651,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.client.users", OK_USER); + conf.set("proxyuser.client.hosts", "localhost"); + return conf; + } } private static class KerberosConfiguration extends Configuration { @@ -713,6 +726,19 @@ public class TestWebDelegationToken { @Test public void testKerberosDelegationTokenAuthenticator() throws Exception { + testKerberosDelegationTokenAuthenticator(false); + } + + @Test + public void testKerberosDelegationTokenAuthenticatorWithDoAs() + throws Exception { + testKerberosDelegationTokenAuthenticator(true); + } + + private void testKerberosDelegationTokenAuthenticator( + final boolean doAs) throws Exception { + final String doAsUser = doAs ? OK_USER : null; + // setting hadoop security to kerberos org.apache.hadoop.conf.Configuration conf = new org.apache.hadoop.conf.Configuration(); @@ -742,7 +768,7 @@ public class TestWebDelegationToken { final URL url = new URL(getJettyURL() + "/foo/bar"); try { - aUrl.getDelegationToken(url, token, FOO_USER); + aUrl.getDelegationToken(url, token, FOO_USER, doAsUser); Assert.fail(); } catch (AuthenticationException ex) { Assert.assertTrue(ex.getMessage().contains("GSSException")); @@ -752,25 +778,41 @@ public class TestWebDelegationToken { new Callable<Void>() { @Override public Void call() throws Exception { - aUrl.getDelegationToken(url, token, "client"); + aUrl.getDelegationToken( + url, token, doAs ? doAsUser : "client", doAsUser); Assert.assertNotNull(token.getDelegationToken()); + Assert.assertEquals(new Text("token-kind"), + token.getDelegationToken().getKind()); + // Make sure the token belongs to the right owner + ByteArrayInputStream buf = new ByteArrayInputStream( + token.getDelegationToken().getIdentifier()); + DataInputStream dis = new DataInputStream(buf); + DelegationTokenIdentifier id = + new DelegationTokenIdentifier(new Text("token-kind")); + id.readFields(dis); + dis.close(); + Assert.assertEquals( + doAs ? new Text(OK_USER) : new Text("client"), id.getOwner()); + if (doAs) { + Assert.assertEquals(new Text("client"), id.getRealUser()); + } - aUrl.renewDelegationToken(url, token); + aUrl.renewDelegationToken(url, token, doAsUser); Assert.assertNotNull(token.getDelegationToken()); - aUrl.getDelegationToken(url, token, FOO_USER); + aUrl.getDelegationToken(url, token, FOO_USER, doAsUser); Assert.assertNotNull(token.getDelegationToken()); try { - aUrl.renewDelegationToken(url, token); + aUrl.renewDelegationToken(url, token, doAsUser); Assert.fail(); } catch (Exception ex) { Assert.assertTrue(ex.getMessage().contains("403")); } - aUrl.getDelegationToken(url, token, FOO_USER); + aUrl.getDelegationToken(url, token, FOO_USER, doAsUser); - aUrl.cancelDelegationToken(url, token); + aUrl.cancelDelegationToken(url, token, doAsUser); Assert.assertNull(token.getDelegationToken()); return null;
