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

(cherry picked from commit 1137557816)
This commit is contained in:
Vinod Kumar Vavilapalli 2014-10-17 15:56:07 -07:00
parent 0ee251ea26
commit 5e239640f9
5 changed files with 213 additions and 36 deletions

View File

@ -252,6 +252,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

View File

@ -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;
}

View File

@ -17,26 +17,6 @@
*/
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.Writer;
import java.text.MessageFormat;
@ -47,6 +27,30 @@ 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;
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
* for HTTP and supports Delegation Token functionality.
@ -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:

View File

@ -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("?")) ? "&" : "?";

View File

@ -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, doAsUser);
Assert.assertNotNull(token.getDelegationToken());
aUrl.renewDelegationToken(url, token);
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;