HADOOP-6581. Add authenticated TokenIdentifiers to UGI so that they can be used for authorization. Kan Zhang and Jitendra Pandey via jghoman.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@948573 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
e346c2f4e0
commit
24a2f1fafe
|
@ -2,6 +2,11 @@ Hadoop Change Log
|
||||||
|
|
||||||
Trunk (unreleased changes)
|
Trunk (unreleased changes)
|
||||||
|
|
||||||
|
NEW FEATURES
|
||||||
|
HADOOP-6581. Add authenticated TokenIdentifiers to UGI so that
|
||||||
|
they can be used for authorization (Kan Zhang and Jitendra Pandey
|
||||||
|
via jghoman)
|
||||||
|
|
||||||
IMPROVEMENTS
|
IMPROVEMENTS
|
||||||
HADOOP-6644. util.Shell getGROUPS_FOR_USER_COMMAND method name
|
HADOOP-6644. util.Shell getGROUPS_FOR_USER_COMMAND method name
|
||||||
- should use common naming convention (boryas)
|
- should use common naming convention (boryas)
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
apacheant.version=1.7.1
|
apacheant.version=1.7.1
|
||||||
ant-task.version=2.0.10
|
ant-task.version=2.0.10
|
||||||
|
|
||||||
avro.version=1.3.0
|
avro.version=1.3.2
|
||||||
|
|
||||||
checkstyle.version=4.2
|
checkstyle.version=4.2
|
||||||
|
|
||||||
|
|
|
@ -923,7 +923,13 @@ public abstract class Server {
|
||||||
if (authMethod == SaslRpcServer.AuthMethod.DIGEST) {
|
if (authMethod == SaslRpcServer.AuthMethod.DIGEST) {
|
||||||
TokenIdentifier tokenId = SaslRpcServer.getIdentifier(authorizedId,
|
TokenIdentifier tokenId = SaslRpcServer.getIdentifier(authorizedId,
|
||||||
secretManager);
|
secretManager);
|
||||||
return tokenId.getUser();
|
UserGroupInformation ugi = tokenId.getUser();
|
||||||
|
if (ugi == null) {
|
||||||
|
throw new AccessControlException(
|
||||||
|
"Can't retrieve username from tokenIdentifier.");
|
||||||
|
}
|
||||||
|
ugi.addTokenIdentifier(tokenId);
|
||||||
|
return ugi;
|
||||||
} else {
|
} else {
|
||||||
return UserGroupInformation.createRemoteUser(authorizedId);
|
return UserGroupInformation.createRemoteUser(authorizedId);
|
||||||
}
|
}
|
||||||
|
@ -1531,7 +1537,7 @@ public abstract class Server {
|
||||||
public void setSocketSendBufSize(int size) { this.socketSendBufferSize = size; }
|
public void setSocketSendBufSize(int size) { this.socketSendBufferSize = size; }
|
||||||
|
|
||||||
/** Starts the service. Must be called before any calls will be handled. */
|
/** Starts the service. Must be called before any calls will be handled. */
|
||||||
public synchronized void start() throws IOException {
|
public synchronized void start() {
|
||||||
responder.start();
|
responder.start();
|
||||||
listener.start();
|
listener.start();
|
||||||
handlers = new Handler[handlerCount];
|
handlers = new Handler[handlerCount];
|
||||||
|
|
|
@ -68,10 +68,10 @@ public class SaslRpcServer {
|
||||||
return Base64.decodeBase64(identifier.getBytes());
|
return Base64.decodeBase64(identifier.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TokenIdentifier getIdentifier(String id,
|
public static <T extends TokenIdentifier> T getIdentifier(String id,
|
||||||
SecretManager<TokenIdentifier> secretManager) throws InvalidToken {
|
SecretManager<T> secretManager) throws InvalidToken {
|
||||||
byte[] tokenId = decodeIdentifier(id);
|
byte[] tokenId = decodeIdentifier(id);
|
||||||
TokenIdentifier tokenIdentifier = secretManager.createIdentifier();
|
T tokenIdentifier = secretManager.createIdentifier();
|
||||||
try {
|
try {
|
||||||
tokenIdentifier.readFields(new DataInputStream(new ByteArrayInputStream(
|
tokenIdentifier.readFields(new DataInputStream(new ByteArrayInputStream(
|
||||||
tokenId)));
|
tokenId)));
|
||||||
|
@ -202,11 +202,12 @@ public class SaslRpcServer {
|
||||||
ac.setAuthorized(false);
|
ac.setAuthorized(false);
|
||||||
}
|
}
|
||||||
if (ac.isAuthorized()) {
|
if (ac.isAuthorized()) {
|
||||||
String username = getIdentifier(authzid, secretManager).getUser()
|
if (LOG.isDebugEnabled()) {
|
||||||
.getUserName().toString();
|
String username = getIdentifier(authzid, secretManager).getUser()
|
||||||
if (LOG.isDebugEnabled())
|
.getUserName().toString();
|
||||||
LOG.debug("SASL server DIGEST-MD5 callback: setting "
|
LOG.debug("SASL server DIGEST-MD5 callback: setting "
|
||||||
+ "canonicalized client ID: " + username);
|
+ "canonicalized client ID: " + username);
|
||||||
|
}
|
||||||
ac.setAuthorizedID(authzid);
|
ac.setAuthorizedID(authzid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -610,6 +610,28 @@ public class UserGroupInformation {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a TokenIdentifier to this UGI. The TokenIdentifier has typically been
|
||||||
|
* authenticated by the RPC layer as belonging to the user represented by this
|
||||||
|
* UGI.
|
||||||
|
*
|
||||||
|
* @param tokenId
|
||||||
|
* tokenIdentifier to be added
|
||||||
|
* @return true on successful add of new tokenIdentifier
|
||||||
|
*/
|
||||||
|
public synchronized boolean addTokenIdentifier(TokenIdentifier tokenId) {
|
||||||
|
return subject.getPublicCredentials().add(tokenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the set of TokenIdentifiers belonging to this UGI
|
||||||
|
*
|
||||||
|
* @return the set of TokenIdentifiers belonging to this UGI
|
||||||
|
*/
|
||||||
|
public synchronized Set<TokenIdentifier> getTokenIdentifiers() {
|
||||||
|
return subject.getPublicCredentials(TokenIdentifier.class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a token to this UGI
|
* Add a token to this UGI
|
||||||
*
|
*
|
||||||
|
|
|
@ -27,6 +27,7 @@ import javax.crypto.SecretKey;
|
||||||
|
|
||||||
import org.apache.hadoop.io.Writable;
|
import org.apache.hadoop.io.Writable;
|
||||||
import org.apache.hadoop.io.WritableUtils;
|
import org.apache.hadoop.io.WritableUtils;
|
||||||
|
import org.apache.avro.reflect.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key used for generating and verifying delegation tokens
|
* Key used for generating and verifying delegation tokens
|
||||||
|
@ -35,7 +36,8 @@ import org.apache.hadoop.io.WritableUtils;
|
||||||
public class DelegationKey implements Writable {
|
public class DelegationKey implements Writable {
|
||||||
private int keyId;
|
private int keyId;
|
||||||
private long expiryDate;
|
private long expiryDate;
|
||||||
private SecretKey key;
|
@Nullable
|
||||||
|
private byte[] keyBytes = null;
|
||||||
|
|
||||||
public DelegationKey() {
|
public DelegationKey() {
|
||||||
this(0, 0L, null);
|
this(0, 0L, null);
|
||||||
|
@ -44,7 +46,9 @@ public class DelegationKey implements Writable {
|
||||||
public DelegationKey(int keyId, long expiryDate, SecretKey key) {
|
public DelegationKey(int keyId, long expiryDate, SecretKey key) {
|
||||||
this.keyId = keyId;
|
this.keyId = keyId;
|
||||||
this.expiryDate = expiryDate;
|
this.expiryDate = expiryDate;
|
||||||
this.key = key;
|
if (key!=null) {
|
||||||
|
this.keyBytes = key.getEncoded();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getKeyId() {
|
public int getKeyId() {
|
||||||
|
@ -56,7 +60,12 @@ public class DelegationKey implements Writable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SecretKey getKey() {
|
public SecretKey getKey() {
|
||||||
return key;
|
if (keyBytes == null || keyBytes.length == 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
SecretKey key = AbstractDelegationTokenSecretManager.createSecretKey(keyBytes);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExpiryDate(long expiryDate) {
|
public void setExpiryDate(long expiryDate) {
|
||||||
|
@ -68,9 +77,12 @@ public class DelegationKey implements Writable {
|
||||||
public void write(DataOutput out) throws IOException {
|
public void write(DataOutput out) throws IOException {
|
||||||
WritableUtils.writeVInt(out, keyId);
|
WritableUtils.writeVInt(out, keyId);
|
||||||
WritableUtils.writeVLong(out, expiryDate);
|
WritableUtils.writeVLong(out, expiryDate);
|
||||||
byte[] keyBytes = key.getEncoded();
|
if (keyBytes == null) {
|
||||||
WritableUtils.writeVInt(out, keyBytes.length);
|
WritableUtils.writeVInt(out, -1);
|
||||||
out.write(keyBytes);
|
} else {
|
||||||
|
WritableUtils.writeVInt(out, keyBytes.length);
|
||||||
|
out.write(keyBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,8 +91,11 @@ public class DelegationKey implements Writable {
|
||||||
keyId = WritableUtils.readVInt(in);
|
keyId = WritableUtils.readVInt(in);
|
||||||
expiryDate = WritableUtils.readVLong(in);
|
expiryDate = WritableUtils.readVLong(in);
|
||||||
int len = WritableUtils.readVInt(in);
|
int len = WritableUtils.readVInt(in);
|
||||||
byte[] keyBytes = new byte[len];
|
if (len == -1) {
|
||||||
in.readFully(keyBytes);
|
keyBytes = null;
|
||||||
key = AbstractDelegationTokenSecretManager.createSecretKey(keyBytes);
|
} else {
|
||||||
|
keyBytes = new byte[len];
|
||||||
|
in.readFully(keyBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
package org.apache.hadoop.ipc;
|
package org.apache.hadoop.ipc;
|
||||||
|
|
||||||
import org.apache.avro.ipc.AvroRemoteException;
|
import org.apache.avro.ipc.AvroRemoteException;
|
||||||
import org.apache.avro.util.Utf8;
|
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public interface AvroTestProtocol {
|
public interface AvroTestProtocol {
|
||||||
|
@ -27,7 +26,7 @@ public interface AvroTestProtocol {
|
||||||
public Problem() {}
|
public Problem() {}
|
||||||
}
|
}
|
||||||
void ping();
|
void ping();
|
||||||
Utf8 echo(Utf8 value);
|
String echo(String value);
|
||||||
int add(int v1, int v2);
|
int add(int v1, int v2);
|
||||||
int error() throws Problem;
|
int error() throws Problem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.net.NetUtils;
|
import org.apache.hadoop.net.NetUtils;
|
||||||
|
|
||||||
import org.apache.avro.ipc.AvroRemoteException;
|
import org.apache.avro.ipc.AvroRemoteException;
|
||||||
import org.apache.avro.util.Utf8;
|
|
||||||
|
|
||||||
/** Unit tests for AvroRpc. */
|
/** Unit tests for AvroRpc. */
|
||||||
public class TestAvroRpc extends TestCase {
|
public class TestAvroRpc extends TestCase {
|
||||||
|
@ -50,7 +49,7 @@ public class TestAvroRpc extends TestCase {
|
||||||
|
|
||||||
public void ping() {}
|
public void ping() {}
|
||||||
|
|
||||||
public Utf8 echo(Utf8 value) { return value; }
|
public String echo(String value) { return value; }
|
||||||
|
|
||||||
public int add(int v1, int v2) { return v1 + v2; }
|
public int add(int v1, int v2) { return v1 + v2; }
|
||||||
|
|
||||||
|
@ -74,8 +73,8 @@ public class TestAvroRpc extends TestCase {
|
||||||
|
|
||||||
proxy.ping();
|
proxy.ping();
|
||||||
|
|
||||||
Utf8 utf8Result = proxy.echo(new Utf8("hello world"));
|
String echo = proxy.echo("hello world");
|
||||||
assertEquals(new Utf8("hello world"), utf8Result);
|
assertEquals("hello world", echo);
|
||||||
|
|
||||||
int intResult = proxy.add(1, 2);
|
int intResult = proxy.add(1, 2);
|
||||||
assertEquals(3, intResult);
|
assertEquals(3, intResult);
|
||||||
|
|
|
@ -34,7 +34,6 @@ import java.util.List;
|
||||||
|
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
|
|
||||||
import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
|
|
||||||
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
|
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
|
||||||
import org.apache.hadoop.security.token.Token;
|
import org.apache.hadoop.security.token.Token;
|
||||||
import org.apache.hadoop.security.token.TokenIdentifier;
|
import org.apache.hadoop.security.token.TokenIdentifier;
|
||||||
|
@ -215,6 +214,33 @@ public class TestUserGroupInformation {
|
||||||
assertTrue(otherSet.contains(t2));
|
assertTrue(otherSet.contains(t2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTokenIdentifiers() throws Exception {
|
||||||
|
UserGroupInformation ugi = UserGroupInformation.createUserForTesting(
|
||||||
|
"TheDoctor", new String[] { "TheTARDIS" });
|
||||||
|
TokenIdentifier t1 = mock(TokenIdentifier.class);
|
||||||
|
TokenIdentifier t2 = mock(TokenIdentifier.class);
|
||||||
|
|
||||||
|
ugi.addTokenIdentifier(t1);
|
||||||
|
ugi.addTokenIdentifier(t2);
|
||||||
|
|
||||||
|
Collection<TokenIdentifier> z = ugi.getTokenIdentifiers();
|
||||||
|
assertTrue(z.contains(t1));
|
||||||
|
assertTrue(z.contains(t2));
|
||||||
|
assertEquals(2, z.size());
|
||||||
|
|
||||||
|
// ensure that the token identifiers are passed through doAs
|
||||||
|
Collection<TokenIdentifier> otherSet = ugi
|
||||||
|
.doAs(new PrivilegedExceptionAction<Collection<TokenIdentifier>>() {
|
||||||
|
public Collection<TokenIdentifier> run() throws IOException {
|
||||||
|
return UserGroupInformation.getCurrentUser().getTokenIdentifiers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertTrue(otherSet.contains(t1));
|
||||||
|
assertTrue(otherSet.contains(t2));
|
||||||
|
assertEquals(2, otherSet.size());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUGIAuthMethod() throws Exception {
|
public void testUGIAuthMethod() throws Exception {
|
||||||
final UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
|
final UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
|
||||||
|
|
Loading…
Reference in New Issue