HADOOP-15808. Harden Token service loader use.

Contributed by Steve Loughran.
This commit is contained in:
Steve Loughran 2018-12-11 17:31:17 +00:00
parent 1a25bbe9ec
commit 202926ac33
No known key found for this signature in database
GPG Key ID: D22CF846DBB162A0
4 changed files with 65 additions and 30 deletions

View File

@ -24,6 +24,8 @@
import java.text.DateFormat; import java.text.DateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.Iterator;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -130,7 +132,7 @@ public static void printTokenFile(
* @param creds the Credentials object to be printed out. * @param creds the Credentials object to be printed out.
* @param alias print only tokens matching alias (null matches all). * @param alias print only tokens matching alias (null matches all).
* @param out print to this stream. * @param out print to this stream.
* @throws IOException * @throws IOException failure to unmarshall a token identifier.
*/ */
public static void printCredentials( public static void printCredentials(
Credentials creds, Text alias, PrintStream out) Credentials creds, Text alias, PrintStream out)
@ -145,8 +147,13 @@ public static void printCredentials(
out.println(StringUtils.repeat("-", 80)); out.println(StringUtils.repeat("-", 80));
tokenHeader = false; tokenHeader = false;
} }
AbstractDelegationTokenIdentifier id = AbstractDelegationTokenIdentifier id;
(AbstractDelegationTokenIdentifier) token.decodeIdentifier(); try {
id = (AbstractDelegationTokenIdentifier) token.decodeIdentifier();
} catch (IllegalStateException e) {
LOG.debug("Failed to decode token identifier", e);
id = null;
}
out.printf(fmt, token.getKind(), token.getService(), out.printf(fmt, token.getKind(), token.getService(),
(id != null) ? id.getRenewer() : NA_STRING, (id != null) ? id.getRenewer() : NA_STRING,
(id != null) ? formatDate(id.getMaxDate()) : NA_STRING, (id != null) ? formatDate(id.getMaxDate()) : NA_STRING,
@ -172,7 +179,17 @@ public static void getTokenFile(File tokenFile, String fileFormat,
Credentials creds = tokenFile.exists() ? Credentials creds = tokenFile.exists() ?
Credentials.readTokenStorageFile(tokenFile, conf) : new Credentials(); Credentials.readTokenStorageFile(tokenFile, conf) : new Credentials();
ServiceLoader<DtFetcher> loader = ServiceLoader.load(DtFetcher.class); ServiceLoader<DtFetcher> loader = ServiceLoader.load(DtFetcher.class);
for (DtFetcher fetcher : loader) { Iterator<DtFetcher> iterator = loader.iterator();
while (iterator.hasNext()) {
DtFetcher fetcher;
try {
fetcher = iterator.next();
} catch (ServiceConfigurationError e) {
// failure to load a token implementation
// log at debug and continue.
LOG.debug("Failed to load token fetcher implementation", e);
continue;
}
if (matchService(fetcher, service, url)) { if (matchService(fetcher, service, url)) {
if (!fetcher.isTokenRequired()) { if (!fetcher.isTokenRequired()) {
String message = "DtFetcher for service '" + service + String message = "DtFetcher for service '" + service +

View File

@ -35,7 +35,9 @@
import java.io.*; import java.io.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.UUID; import java.util.UUID;
@ -147,14 +149,25 @@ public byte[] getIdentifier() {
synchronized (Token.class) { synchronized (Token.class) {
if (tokenKindMap == null) { if (tokenKindMap == null) {
tokenKindMap = Maps.newHashMap(); tokenKindMap = Maps.newHashMap();
for (TokenIdentifier id : ServiceLoader.load(TokenIdentifier.class)) { // start the service load process; it's only in the "next()" calls
tokenKindMap.put(id.getKind(), id.getClass()); // where implementations are loaded.
final Iterator<TokenIdentifier> tokenIdentifiers =
ServiceLoader.load(TokenIdentifier.class).iterator();
while (tokenIdentifiers.hasNext()) {
try {
TokenIdentifier id = tokenIdentifiers.next();
tokenKindMap.put(id.getKind(), id.getClass());
} catch (ServiceConfigurationError e) {
// failure to load a token implementation
// log at debug and continue.
LOG.debug("Failed to load token identifier implementation", e);
}
} }
} }
cls = tokenKindMap.get(kind); cls = tokenKindMap.get(kind);
} }
if (cls == null) { if (cls == null) {
LOG.debug("Cannot find class for token kind " + kind); LOG.debug("Cannot find class for token kind {}", kind);
return null; return null;
} }
return cls; return cls;
@ -163,8 +176,9 @@ public byte[] getIdentifier() {
/** /**
* Get the token identifier object, or null if it could not be constructed * Get the token identifier object, or null if it could not be constructed
* (because the class could not be loaded, for example). * (because the class could not be loaded, for example).
* @return the token identifier, or null * @return the token identifier, or null if there was no class found for it
* @throws IOException * @throws IOException failure to unmarshall the data
* @throws RuntimeException if the token class could not be instantiated.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public T decodeIdentifier() throws IOException { public T decodeIdentifier() throws IOException {
@ -263,7 +277,7 @@ static class PrivateToken<T extends TokenIdentifier> extends Token<T> {
assert !publicToken.isPrivate(); assert !publicToken.isPrivate();
publicService = publicToken.service; publicService = publicToken.service;
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("Cloned private token " + this + " from " + publicToken); LOG.debug("Cloned private token {} from {}", this, publicToken);
} }
} }
@ -465,14 +479,22 @@ private synchronized TokenRenewer getRenewer() throws IOException {
} }
renewer = TRIVIAL_RENEWER; renewer = TRIVIAL_RENEWER;
synchronized (renewers) { synchronized (renewers) {
for (TokenRenewer canidate : renewers) { Iterator<TokenRenewer> it = renewers.iterator();
if (canidate.handleKind(this.kind)) { while (it.hasNext()) {
renewer = canidate; try {
return renewer; TokenRenewer candidate = it.next();
if (candidate.handleKind(this.kind)) {
renewer = candidate;
return renewer;
}
} catch (ServiceConfigurationError e) {
// failure to load a token implementation
// log at debug and continue.
LOG.debug("Failed to load token renewer implementation", e);
} }
} }
} }
LOG.warn("No TokenRenewer defined for token kind " + this.kind); LOG.warn("No TokenRenewer defined for token kind {}", kind);
return renewer; return renewer;
} }

View File

@ -926,7 +926,9 @@ private static void assertAuthEquals(AuthMethod expect,
private static void assertAuthEquals(Pattern expect, String actual) { private static void assertAuthEquals(Pattern expect, String actual) {
// this allows us to see the regexp and the value it didn't match // this allows us to see the regexp and the value it didn't match
if (!expect.matcher(actual).matches()) { if (!expect.matcher(actual).matches()) {
fail(); // it failed // it failed
fail(String.format("\"%s\" did not match pattern %s",
actual, expect));
} }
} }

View File

@ -28,6 +28,7 @@
import org.apache.hadoop.security.token.delegation.TestDelegationToken.TestDelegationTokenSecretManager; import org.apache.hadoop.security.token.delegation.TestDelegationToken.TestDelegationTokenSecretManager;
import org.junit.Test; import org.junit.Test;
import static org.apache.hadoop.test.LambdaTestUtils.intercept;
import static org.junit.Assert.*; import static org.junit.Assert.*;
/** Unit tests for Token */ /** Unit tests for Token */
@ -86,13 +87,12 @@ public void testEncodeWritable() throws Exception {
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLM" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLM" +
"NOPQRSTUVWXYZ01234567890!@#$%^&*()-=_+[]{}|;':,./<>?"}; "NOPQRSTUVWXYZ01234567890!@#$%^&*()-=_+[]{}|;':,./<>?"};
Token<AbstractDelegationTokenIdentifier> orig; Token<AbstractDelegationTokenIdentifier> orig;
Token<AbstractDelegationTokenIdentifier> copy = Token<AbstractDelegationTokenIdentifier> copy = new Token<>();
new Token<AbstractDelegationTokenIdentifier>();
// ensure that for each string the input and output values match // ensure that for each string the input and output values match
for(int i=0; i< values.length; ++i) { for(int i=0; i< values.length; ++i) {
String val = values[i]; String val = values[i];
System.out.println("Input = " + val); Token.LOG.info("Input = {}", val);
orig = new Token<AbstractDelegationTokenIdentifier>(val.getBytes(), orig = new Token<>(val.getBytes(),
val.getBytes(), new Text(val), new Text(val)); val.getBytes(), new Text(val), new Text(val));
String encode = orig.encodeToUrlString(); String encode = orig.encodeToUrlString();
copy.decodeFromUrlString(encode); copy.decodeFromUrlString(encode);
@ -107,15 +107,9 @@ public void testEncodeWritable() throws Exception {
*/ */
@Test @Test
public void testDecodeWritableArgSanityCheck() throws Exception { public void testDecodeWritableArgSanityCheck() throws Exception {
Token<AbstractDelegationTokenIdentifier> token = Token<AbstractDelegationTokenIdentifier> token = new Token<>();
new Token<AbstractDelegationTokenIdentifier>(); intercept(HadoopIllegalArgumentException.class,
try { () -> token.decodeFromUrlString(null));
token.decodeFromUrlString(null);
fail("Should have thrown HadoopIllegalArgumentException");
}
catch (HadoopIllegalArgumentException e) {
Token.LOG.info("Test decodeWritable() sanity check success.");
}
} }
@Test @Test
@ -127,7 +121,7 @@ public void testDecodeIdentifier() throws IOException {
new Text("owner"), new Text("renewer"), new Text("realUser")); new Text("owner"), new Text("renewer"), new Text("realUser"));
Token<TestDelegationTokenIdentifier> token = Token<TestDelegationTokenIdentifier> token =
new Token<TestDelegationTokenIdentifier>(id, secretManager); new Token<>(id, secretManager);
TokenIdentifier idCopy = token.decodeIdentifier(); TokenIdentifier idCopy = token.decodeIdentifier();
assertNotSame(id, idCopy); assertNotSame(id, idCopy);