HADOOP-11207. Enhanced common DelegationTokenAuthenticationHandler to support proxy-users on Delegation-token management operations. Contributed by Zhijie Shen.

This commit is contained in:
Vinod Kumar Vavilapalli 2014-10-17 15:56:07 -07:00
parent 209b1699fc
commit 1137557816
5 changed files with 213 additions and 36 deletions

View File

@ -597,6 +597,9 @@ Release 2.6.0 - UNRELEASED
HADOOP-11181. Generalized o.a.h.s.t.d.DelegationTokenManager to handle all HADOOP-11181. Generalized o.a.h.s.t.d.DelegationTokenManager to handle all
sub-classes of AbstractDelegationTokenIdentifier. (zjshen) sub-classes of AbstractDelegationTokenIdentifier. (zjshen)
HADOOP-11207. Enhanced common DelegationTokenAuthenticationHandler to support
proxy-users on Delegation-token management operations. (Zhijie Shen via
vinodkv)
OPTIMIZATIONS OPTIMIZATIONS

View File

@ -333,6 +333,7 @@ public class DelegationTokenAuthenticatedURL extends AuthenticatedURL {
* supported. * supported.
* @param token the authentication token being used for the user where the * @param token the authentication token being used for the user where the
* Delegation token will be stored. * Delegation token will be stored.
* @param renewer the renewer user.
* @return a delegation token. * @return a delegation token.
* @throws IOException if an IO error occurred. * @throws IOException if an IO error occurred.
* @throws AuthenticationException if an authentication exception 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> public org.apache.hadoop.security.token.Token<AbstractDelegationTokenIdentifier>
getDelegationToken(URL url, Token token, String renewer) getDelegationToken(URL url, Token token, String renewer)
throws IOException, AuthenticationException { 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(url, "url");
Preconditions.checkNotNull(token, "token"); Preconditions.checkNotNull(token, "token");
try { try {
token.delegationToken = token.delegationToken =
((KerberosDelegationTokenAuthenticator) getAuthenticator()). ((KerberosDelegationTokenAuthenticator) getAuthenticator()).
getDelegationToken(url, token, renewer); getDelegationToken(url, token, renewer, doAsUser);
return token.delegationToken; return token.delegationToken;
} catch (IOException ex) { } catch (IOException ex) {
token.delegationToken = null; token.delegationToken = null;
@ -365,13 +386,29 @@ public class DelegationTokenAuthenticatedURL extends AuthenticatedURL {
*/ */
public long renewDelegationToken(URL url, Token token) public long renewDelegationToken(URL url, Token token)
throws IOException, AuthenticationException { 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(url, "url");
Preconditions.checkNotNull(token, "token"); Preconditions.checkNotNull(token, "token");
Preconditions.checkNotNull(token.delegationToken, Preconditions.checkNotNull(token.delegationToken,
"No delegation token available"); "No delegation token available");
try { try {
return ((KerberosDelegationTokenAuthenticator) getAuthenticator()). return ((KerberosDelegationTokenAuthenticator) getAuthenticator()).
renewDelegationToken(url, token, token.delegationToken); renewDelegationToken(url, token, token.delegationToken, doAsUser);
} catch (IOException ex) { } catch (IOException ex) {
token.delegationToken = null; token.delegationToken = null;
throw ex; throw ex;
@ -389,13 +426,28 @@ public class DelegationTokenAuthenticatedURL extends AuthenticatedURL {
*/ */
public void cancelDelegationToken(URL url, Token token) public void cancelDelegationToken(URL url, Token token)
throws IOException { 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(url, "url");
Preconditions.checkNotNull(token, "token"); Preconditions.checkNotNull(token, "token");
Preconditions.checkNotNull(token.delegationToken, Preconditions.checkNotNull(token.delegationToken,
"No delegation token available"); "No delegation token available");
try { try {
((KerberosDelegationTokenAuthenticator) getAuthenticator()). ((KerberosDelegationTokenAuthenticator) getAuthenticator()).
cancelDelegationToken(url, token, token.delegationToken); cancelDelegationToken(url, token, token.delegationToken, doAsUser);
} finally { } finally {
token.delegationToken = null; token.delegationToken = null;
} }

View File

@ -17,26 +17,6 @@
*/ */
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.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.security.UserGroupInformation;
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.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.IOException;
import java.io.Writer; import java.io.Writer;
import java.text.MessageFormat; import java.text.MessageFormat;
@ -47,6 +27,30 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; 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;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.security.UserGroupInformation;
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 com.google.common.annotations.VisibleForTesting;
/** /**
* An {@link AuthenticationHandler} that implements Kerberos SPNEGO mechanism * An {@link AuthenticationHandler} that implements Kerberos SPNEGO mechanism
* for HTTP and supports Delegation Token functionality. * for HTTP and supports Delegation Token functionality.
@ -188,6 +192,19 @@ public abstract class DelegationTokenAuthenticationHandler
UserGroupInformation requestUgi = (token != null) UserGroupInformation requestUgi = (token != null)
? UserGroupInformation.createRemoteUser(token.getUserName()) ? UserGroupInformation.createRemoteUser(token.getUserName())
: null; : 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; Map map = null;
switch (dtOp) { switch (dtOp) {
case GETDELEGATIONTOKEN: case GETDELEGATIONTOKEN:

View File

@ -136,14 +136,35 @@ public abstract class DelegationTokenAuthenticator implements Authenticator {
* supported. * supported.
* @param token the authentication token being used for the user where the * @param token the authentication token being used for the user where the
* Delegation token will be stored. * Delegation token will be stored.
* @param renewer the renewer user.
* @throws IOException if an IO error occurred. * @throws IOException if an IO error occurred.
* @throws AuthenticationException if an authentication exception occurred. * @throws AuthenticationException if an authentication exception occurred.
*/ */
public Token<AbstractDelegationTokenIdentifier> getDelegationToken(URL url, public Token<AbstractDelegationTokenIdentifier> getDelegationToken(URL url,
AuthenticatedURL.Token token, String renewer) AuthenticatedURL.Token token, String renewer)
throws IOException, AuthenticationException { 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, Map json = doDelegationTokenOperation(url, token,
DelegationTokenOperation.GETDELEGATIONTOKEN, renewer, null, true); DelegationTokenOperation.GETDELEGATIONTOKEN, renewer, null, true,
doAsUser);
json = (Map) json.get(DELEGATION_TOKEN_JSON); json = (Map) json.get(DELEGATION_TOKEN_JSON);
String tokenStr = (String) json.get(DELEGATION_TOKEN_URL_STRING_JSON); String tokenStr = (String) json.get(DELEGATION_TOKEN_URL_STRING_JSON);
Token<AbstractDelegationTokenIdentifier> dToken = Token<AbstractDelegationTokenIdentifier> dToken =
@ -169,8 +190,27 @@ public abstract class DelegationTokenAuthenticator implements Authenticator {
AuthenticatedURL.Token token, AuthenticatedURL.Token token,
Token<AbstractDelegationTokenIdentifier> dToken) Token<AbstractDelegationTokenIdentifier> dToken)
throws IOException, AuthenticationException { 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, Map json = doDelegationTokenOperation(url, token,
DelegationTokenOperation.RENEWDELEGATIONTOKEN, null, dToken, true); DelegationTokenOperation.RENEWDELEGATIONTOKEN, null, dToken, true,
doAsUser);
return (Long) json.get(RENEW_DELEGATION_TOKEN_JSON); return (Long) json.get(RENEW_DELEGATION_TOKEN_JSON);
} }
@ -187,9 +227,27 @@ public abstract class DelegationTokenAuthenticator implements Authenticator {
AuthenticatedURL.Token token, AuthenticatedURL.Token token,
Token<AbstractDelegationTokenIdentifier> dToken) Token<AbstractDelegationTokenIdentifier> dToken)
throws IOException { 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 { try {
doDelegationTokenOperation(url, token, doDelegationTokenOperation(url, token,
DelegationTokenOperation.CANCELDELEGATIONTOKEN, null, dToken, false); DelegationTokenOperation.CANCELDELEGATIONTOKEN, null, dToken, false,
doAsUser);
} catch (AuthenticationException ex) { } catch (AuthenticationException ex) {
throw new IOException("This should not happen: " + ex.getMessage(), 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, private Map doDelegationTokenOperation(URL url,
AuthenticatedURL.Token token, DelegationTokenOperation operation, AuthenticatedURL.Token token, DelegationTokenOperation operation,
String renewer, Token<?> dToken, boolean hasResponse) String renewer, Token<?> dToken, boolean hasResponse, String doAsUser)
throws IOException, AuthenticationException { throws IOException, AuthenticationException {
Map ret = null; Map ret = null;
Map<String, String> params = new HashMap<String, String>(); Map<String, String> params = new HashMap<String, String>();
@ -208,6 +266,11 @@ public abstract class DelegationTokenAuthenticator implements Authenticator {
if (dToken != null) { if (dToken != null) {
params.put(TOKEN_PARAM, dToken.encodeToUrlString()); params.put(TOKEN_PARAM, dToken.encodeToUrlString());
} }
// proxyuser
if (doAsUser != null) {
params.put(DelegationTokenAuthenticatedURL.DO_AS,
URLEncoder.encode(doAsUser, "UTF-8"));
}
String urlStr = url.toExternalForm(); String urlStr = url.toExternalForm();
StringBuilder sb = new StringBuilder(urlStr); StringBuilder sb = new StringBuilder(urlStr);
String separator = (urlStr.contains("?")) ? "&" : "?"; String separator = (urlStr.contains("?")) ? "&" : "?";

View File

@ -52,6 +52,9 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;
@ -648,6 +651,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.client.users", OK_USER);
conf.set("proxyuser.client.hosts", "localhost");
return conf;
}
} }
private static class KerberosConfiguration extends Configuration { private static class KerberosConfiguration extends Configuration {
@ -713,6 +726,19 @@ public class TestWebDelegationToken {
@Test @Test
public void testKerberosDelegationTokenAuthenticator() throws Exception { 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 // setting hadoop security to kerberos
org.apache.hadoop.conf.Configuration conf = org.apache.hadoop.conf.Configuration conf =
new org.apache.hadoop.conf.Configuration(); new org.apache.hadoop.conf.Configuration();
@ -742,7 +768,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_USER); aUrl.getDelegationToken(url, token, FOO_USER, doAsUser);
Assert.fail(); Assert.fail();
} catch (AuthenticationException ex) { } catch (AuthenticationException ex) {
Assert.assertTrue(ex.getMessage().contains("GSSException")); Assert.assertTrue(ex.getMessage().contains("GSSException"));
@ -752,25 +778,41 @@ public class TestWebDelegationToken {
new Callable<Void>() { new Callable<Void>() {
@Override @Override
public Void call() throws Exception { 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, doAsUser);
Assert.assertNotNull(token.getDelegationToken()); Assert.assertNotNull(token.getDelegationToken());
aUrl.renewDelegationToken(url, token); aUrl.getDelegationToken(url, token, FOO_USER, doAsUser);
Assert.assertNotNull(token.getDelegationToken());
aUrl.getDelegationToken(url, token, FOO_USER);
Assert.assertNotNull(token.getDelegationToken()); Assert.assertNotNull(token.getDelegationToken());
try { try {
aUrl.renewDelegationToken(url, token); aUrl.renewDelegationToken(url, token, doAsUser);
Assert.fail(); Assert.fail();
} catch (Exception ex) { } catch (Exception ex) {
Assert.assertTrue(ex.getMessage().contains("403")); 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()); Assert.assertNull(token.getDelegationToken());
return null; return null;