HADOOP-13081. add the ability to create multiple UGIs/subjects from one kerberos login. Contributed by Sergey Shelukhin.

This commit is contained in:
Chris Nauroth 2016-08-02 12:43:30 -07:00
parent 3818393297
commit 0458a2af6e
2 changed files with 55 additions and 1 deletions

View File

@ -38,6 +38,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
@ -637,7 +638,33 @@ private void setLogin(LoginContext login) {
this.isKeytab = KerberosUtil.hasKerberosKeyTab(subject);
this.isKrbTkt = KerberosUtil.hasKerberosTicket(subject);
}
/**
* Copies the Subject of this UGI and creates a new UGI with the new subject.
* This can be used to add credentials (e.g. tokens) to different copies of
* the same UGI, allowing multiple users with different tokens to reuse the
* UGI without re-authenticating with Kerberos.
* @return clone of the UGI with a new subject.
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public UserGroupInformation copySubjectAndUgi() {
Subject subj = getSubject();
// The ctor will set other fields automatically from the principals.
return new UserGroupInformation(new Subject(false, subj.getPrincipals(),
cloneCredentials(subj.getPublicCredentials()),
cloneCredentials(subj.getPrivateCredentials())));
}
private static Set<Object> cloneCredentials(Set<Object> old) {
Set<Object> set = new HashSet<>();
// Make sure Hadoop credentials objects do not reuse the maps.
for (Object o : old) {
set.add(o instanceof Credentials ? new Credentials((Credentials)o) : o);
}
return set;
}
/**
* checks if logged in using kerberos
* @return true if the subject logged via keytab or has a Kerberos TGT

View File

@ -44,6 +44,7 @@
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import static org.apache.hadoop.fs.CommonConfigurationKeys.HADOOP_USER_GROUP_METRICS_PERCENTILES_INTERVALS;
@ -877,6 +878,32 @@ public void testPrivateTokenExclusion() throws Exception {
assertEquals(1, tokens.size());
}
@Test(timeout = 30000)
public void testCopySubjectAndUgi() throws IOException {
SecurityUtil.setAuthenticationMethod(AuthenticationMethod.SIMPLE, conf);
UserGroupInformation.setConfiguration(conf);
UserGroupInformation u1 = UserGroupInformation.getLoginUser();
assertNotNull(u1);
@SuppressWarnings("unchecked")
Token<? extends TokenIdentifier> tmpToken = mock(Token.class);
u1.addToken(tmpToken);
UserGroupInformation u2 = u1.copySubjectAndUgi();
assertEquals(u1.getAuthenticationMethod(), u2.getAuthenticationMethod());
assertNotSame(u1.getSubject(), u2.getSubject());
Credentials c1 = u1.getCredentials(), c2 = u2.getCredentials();
List<Text> sc1 = c1.getAllSecretKeys(), sc2 = c2.getAllSecretKeys();
assertArrayEquals(sc1.toArray(new Text[0]), sc2.toArray(new Text[0]));
Collection<Token<? extends TokenIdentifier>> ts1 = c1.getAllTokens(),
ts2 = c2.getAllTokens();
assertArrayEquals(ts1.toArray(new Token[0]), ts2.toArray(new Token[0]));
@SuppressWarnings("unchecked")
Token<? extends TokenIdentifier> token = mock(Token.class);
u2.addToken(token);
assertTrue(u2.getCredentials().getAllTokens().contains(token));
assertFalse(u1.getCredentials().getAllTokens().contains(token));
}
/**
* This test checks a race condition between getting and adding tokens for
* the current user. Calling UserGroupInformation.getCurrentUser() returns