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:
parent
0ee251ea26
commit
5e239640f9
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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("?")) ? "&" : "?";
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue