YARN-2676. Enhanced Timeline auth-filter to support proxy users. Contributed by Zhijie Shen.
This commit is contained in:
parent
2e140523d3
commit
39063cd36f
|
@ -367,6 +367,9 @@ Release 2.6.0 - UNRELEASED
|
||||||
YARN-1879. Marked Idempotent/AtMostOnce annotations to ApplicationMasterProtocol
|
YARN-1879. Marked Idempotent/AtMostOnce annotations to ApplicationMasterProtocol
|
||||||
for RM fail over. (Tsuyoshi OZAWA via jianhe)
|
for RM fail over. (Tsuyoshi OZAWA via jianhe)
|
||||||
|
|
||||||
|
YARN-2676. Enhanced Timeline auth-filter to support proxy users. (Zhijie Shen
|
||||||
|
via vinodkv)
|
||||||
|
|
||||||
OPTIMIZATIONS
|
OPTIMIZATIONS
|
||||||
|
|
||||||
BUG FIXES
|
BUG FIXES
|
||||||
|
|
|
@ -1,269 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.client.api.impl;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.text.MessageFormat;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.hadoop.classification.InterfaceAudience.Private;
|
|
||||||
import org.apache.hadoop.classification.InterfaceStability.Unstable;
|
|
||||||
import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
|
|
||||||
import org.apache.hadoop.security.authentication.client.AuthenticationException;
|
|
||||||
import org.apache.hadoop.security.authentication.client.Authenticator;
|
|
||||||
import org.apache.hadoop.security.authentication.client.ConnectionConfigurator;
|
|
||||||
import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
|
|
||||||
import org.apache.hadoop.security.token.Token;
|
|
||||||
import org.apache.hadoop.yarn.api.records.timeline.TimelineDelegationTokenResponse;
|
|
||||||
import org.apache.hadoop.yarn.security.client.TimelineAuthenticationConsts;
|
|
||||||
import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenIdentifier;
|
|
||||||
import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenOperation;
|
|
||||||
import org.apache.hadoop.yarn.webapp.YarnJacksonJaxbJsonProvider;
|
|
||||||
import org.codehaus.jackson.JsonNode;
|
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A <code>KerberosAuthenticator</code> subclass that fallback to
|
|
||||||
* {@link TimelineAuthenticationConsts}.
|
|
||||||
*/
|
|
||||||
@Private
|
|
||||||
@Unstable
|
|
||||||
public class TimelineAuthenticator extends KerberosAuthenticator {
|
|
||||||
|
|
||||||
private static ObjectMapper mapper;
|
|
||||||
private static TimelineAuthenticator authenticator;
|
|
||||||
private static ConnectionConfigurator connConfigurator;
|
|
||||||
|
|
||||||
static {
|
|
||||||
mapper = new ObjectMapper();
|
|
||||||
YarnJacksonJaxbJsonProvider.configObjectMapper(mapper);
|
|
||||||
authenticator = new TimelineAuthenticator();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the fallback authenticator if the server does not use Kerberos
|
|
||||||
* SPNEGO HTTP authentication.
|
|
||||||
*
|
|
||||||
* @return a {@link TimelineAuthenticationConsts} instance.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Authenticator getFallBackAuthenticator() {
|
|
||||||
return new TimelineAuthenticator();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void injectDelegationToken(Map<String, String> params,
|
|
||||||
Token<?> dtToken)
|
|
||||||
throws IOException {
|
|
||||||
if (dtToken != null) {
|
|
||||||
params.put(TimelineAuthenticationConsts.DELEGATION_PARAM,
|
|
||||||
dtToken.encodeToUrlString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Private
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean hasDelegationToken(URL url) {
|
|
||||||
if (url.getQuery() == null) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return url.getQuery().contains(
|
|
||||||
TimelineAuthenticationConsts.DELEGATION_PARAM + "=");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void authenticate(URL url, AuthenticatedURL.Token token)
|
|
||||||
throws IOException, AuthenticationException {
|
|
||||||
if (!hasDelegationToken(url)) {
|
|
||||||
super.authenticate(url, token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setStaticConnectionConfigurator(
|
|
||||||
ConnectionConfigurator connConfigurator) {
|
|
||||||
TimelineAuthenticator.connConfigurator = connConfigurator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Token<TimelineDelegationTokenIdentifier> getDelegationToken(
|
|
||||||
URL url, AuthenticatedURL.Token token, String renewer) throws IOException {
|
|
||||||
TimelineDelegationTokenOperation op =
|
|
||||||
TimelineDelegationTokenOperation.GETDELEGATIONTOKEN;
|
|
||||||
Map<String, String> params = new HashMap<String, String>();
|
|
||||||
params.put(TimelineAuthenticationConsts.OP_PARAM, op.toString());
|
|
||||||
params.put(TimelineAuthenticationConsts.RENEWER_PARAM, renewer);
|
|
||||||
url = appendParams(url, params);
|
|
||||||
AuthenticatedURL aUrl =
|
|
||||||
new AuthenticatedURL(authenticator, connConfigurator);
|
|
||||||
try {
|
|
||||||
HttpURLConnection conn = aUrl.openConnection(url, token);
|
|
||||||
conn.setRequestMethod(op.getHttpMethod());
|
|
||||||
TimelineDelegationTokenResponse dtRes = validateAndParseResponse(conn);
|
|
||||||
if (!dtRes.getType().equals(
|
|
||||||
TimelineAuthenticationConsts.DELEGATION_TOKEN_URL)) {
|
|
||||||
throw new IOException("The response content is not expected: "
|
|
||||||
+ dtRes.getContent());
|
|
||||||
}
|
|
||||||
String tokenStr = dtRes.getContent().toString();
|
|
||||||
Token<TimelineDelegationTokenIdentifier> dToken =
|
|
||||||
new Token<TimelineDelegationTokenIdentifier>();
|
|
||||||
dToken.decodeFromUrlString(tokenStr);
|
|
||||||
return dToken;
|
|
||||||
} catch (AuthenticationException ex) {
|
|
||||||
throw new IOException(ex.toString(), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long renewDelegationToken(URL url,
|
|
||||||
AuthenticatedURL.Token token,
|
|
||||||
Token<TimelineDelegationTokenIdentifier> dToken) throws IOException {
|
|
||||||
Map<String, String> params = new HashMap<String, String>();
|
|
||||||
params.put(TimelineAuthenticationConsts.OP_PARAM,
|
|
||||||
TimelineDelegationTokenOperation.RENEWDELEGATIONTOKEN.toString());
|
|
||||||
params.put(TimelineAuthenticationConsts.TOKEN_PARAM,
|
|
||||||
dToken.encodeToUrlString());
|
|
||||||
url = appendParams(url, params);
|
|
||||||
AuthenticatedURL aUrl =
|
|
||||||
new AuthenticatedURL(authenticator, connConfigurator);
|
|
||||||
try {
|
|
||||||
HttpURLConnection conn = aUrl.openConnection(url, token);
|
|
||||||
conn.setRequestMethod(
|
|
||||||
TimelineDelegationTokenOperation.RENEWDELEGATIONTOKEN.getHttpMethod());
|
|
||||||
TimelineDelegationTokenResponse dtRes = validateAndParseResponse(conn);
|
|
||||||
if (!dtRes.getType().equals(
|
|
||||||
TimelineAuthenticationConsts.DELEGATION_TOKEN_EXPIRATION_TIME)) {
|
|
||||||
throw new IOException("The response content is not expected: "
|
|
||||||
+ dtRes.getContent());
|
|
||||||
}
|
|
||||||
return Long.valueOf(dtRes.getContent().toString());
|
|
||||||
} catch (AuthenticationException ex) {
|
|
||||||
throw new IOException(ex.toString(), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void cancelDelegationToken(URL url,
|
|
||||||
AuthenticatedURL.Token token,
|
|
||||||
Token<TimelineDelegationTokenIdentifier> dToken) throws IOException {
|
|
||||||
Map<String, String> params = new HashMap<String, String>();
|
|
||||||
params.put(TimelineAuthenticationConsts.OP_PARAM,
|
|
||||||
TimelineDelegationTokenOperation.CANCELDELEGATIONTOKEN.toString());
|
|
||||||
params.put(TimelineAuthenticationConsts.TOKEN_PARAM,
|
|
||||||
dToken.encodeToUrlString());
|
|
||||||
url = appendParams(url, params);
|
|
||||||
AuthenticatedURL aUrl =
|
|
||||||
new AuthenticatedURL(authenticator, connConfigurator);
|
|
||||||
try {
|
|
||||||
HttpURLConnection conn = aUrl.openConnection(url, token);
|
|
||||||
conn.setRequestMethod(TimelineDelegationTokenOperation.CANCELDELEGATIONTOKEN
|
|
||||||
.getHttpMethod());
|
|
||||||
validateAndParseResponse(conn);
|
|
||||||
} catch (AuthenticationException ex) {
|
|
||||||
throw new IOException(ex.toString(), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience method that appends parameters an HTTP <code>URL</code>.
|
|
||||||
*
|
|
||||||
* @param url
|
|
||||||
* the url.
|
|
||||||
* @param params
|
|
||||||
* the query string parameters.
|
|
||||||
*
|
|
||||||
* @return a <code>URL</code>
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
* thrown if an IO error occurs.
|
|
||||||
*/
|
|
||||||
public static URL appendParams(URL url, Map<String, String> params)
|
|
||||||
throws IOException {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append(url);
|
|
||||||
String separator = url.toString().contains("?") ? "&" : "?";
|
|
||||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
|
||||||
sb.append(separator).append(entry.getKey()).append("=").
|
|
||||||
append(URLEncoder.encode(entry.getValue(), "UTF8"));
|
|
||||||
separator = "&";
|
|
||||||
}
|
|
||||||
return new URL(sb.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the response of an <code>HttpURLConnection</code>. If the current
|
|
||||||
* status code is not 200, it will throw an exception with a detail message
|
|
||||||
* using Server side error messages if available. Otherwise,
|
|
||||||
* {@link TimelineDelegationTokenResponse} will be parsed and returned.
|
|
||||||
*
|
|
||||||
* @param conn
|
|
||||||
* the <code>HttpURLConnection</code>.
|
|
||||||
* @return
|
|
||||||
* @throws IOException
|
|
||||||
* thrown if the current status code is not 200 or the JSON response
|
|
||||||
* cannot be parsed correctly
|
|
||||||
*/
|
|
||||||
private static TimelineDelegationTokenResponse validateAndParseResponse(
|
|
||||||
HttpURLConnection conn) throws IOException {
|
|
||||||
int status = conn.getResponseCode();
|
|
||||||
JsonNode json = mapper.readTree(conn.getInputStream());
|
|
||||||
if (status == HttpURLConnection.HTTP_OK) {
|
|
||||||
return mapper.readValue(json, TimelineDelegationTokenResponse.class);
|
|
||||||
} else {
|
|
||||||
// If the status code is not 200, some thing wrong should happen at the
|
|
||||||
// server side, the JSON content is going to contain exception details.
|
|
||||||
// We can use the JSON content to reconstruct the exception object.
|
|
||||||
try {
|
|
||||||
String message =
|
|
||||||
json.get(TimelineAuthenticationConsts.ERROR_MESSAGE_JSON)
|
|
||||||
.getTextValue();
|
|
||||||
String exception =
|
|
||||||
json.get(TimelineAuthenticationConsts.ERROR_EXCEPTION_JSON)
|
|
||||||
.getTextValue();
|
|
||||||
String className =
|
|
||||||
json.get(TimelineAuthenticationConsts.ERROR_CLASSNAME_JSON)
|
|
||||||
.getTextValue();
|
|
||||||
|
|
||||||
try {
|
|
||||||
ClassLoader cl = TimelineAuthenticator.class.getClassLoader();
|
|
||||||
Class<?> klass = cl.loadClass(className);
|
|
||||||
Constructor<?> constr = klass.getConstructor(String.class);
|
|
||||||
throw (IOException) constr.newInstance(message);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw ex;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new IOException(MessageFormat.format("{0} - {1}", exception,
|
|
||||||
message));
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
if (ex.getCause() instanceof IOException) {
|
|
||||||
throw (IOException) ex.getCause();
|
|
||||||
}
|
|
||||||
throw new IOException(
|
|
||||||
MessageFormat.format("HTTP status [{0}], {1}",
|
|
||||||
status, conn.getResponseMessage()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -20,14 +20,14 @@ package org.apache.hadoop.yarn.client.api.impl;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.PrivilegedExceptionAction;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.net.ssl.HostnameVerifier;
|
import javax.net.ssl.HostnameVerifier;
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
@ -43,13 +43,14 @@ import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.hadoop.classification.InterfaceAudience.Private;
|
import org.apache.hadoop.classification.InterfaceAudience.Private;
|
||||||
import org.apache.hadoop.classification.InterfaceStability.Unstable;
|
import org.apache.hadoop.classification.InterfaceStability.Unstable;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.io.Text;
|
|
||||||
import org.apache.hadoop.security.UserGroupInformation;
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
|
|
||||||
import org.apache.hadoop.security.authentication.client.AuthenticationException;
|
|
||||||
import org.apache.hadoop.security.authentication.client.ConnectionConfigurator;
|
import org.apache.hadoop.security.authentication.client.ConnectionConfigurator;
|
||||||
import org.apache.hadoop.security.ssl.SSLFactory;
|
import org.apache.hadoop.security.ssl.SSLFactory;
|
||||||
import org.apache.hadoop.security.token.Token;
|
import org.apache.hadoop.security.token.Token;
|
||||||
|
import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticatedURL;
|
||||||
|
import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticator;
|
||||||
|
import org.apache.hadoop.security.token.delegation.web.KerberosDelegationTokenAuthenticator;
|
||||||
|
import org.apache.hadoop.security.token.delegation.web.PseudoDelegationTokenAuthenticator;
|
||||||
import org.apache.hadoop.yarn.api.records.timeline.TimelineDomain;
|
import org.apache.hadoop.yarn.api.records.timeline.TimelineDomain;
|
||||||
import org.apache.hadoop.yarn.api.records.timeline.TimelineDomains;
|
import org.apache.hadoop.yarn.api.records.timeline.TimelineDomains;
|
||||||
import org.apache.hadoop.yarn.api.records.timeline.TimelineEntities;
|
import org.apache.hadoop.yarn.api.records.timeline.TimelineEntities;
|
||||||
|
@ -60,8 +61,6 @@ import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
|
import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
|
||||||
import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenIdentifier;
|
import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenIdentifier;
|
||||||
import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenSelector;
|
|
||||||
import org.apache.hadoop.yarn.util.timeline.TimelineUtils;
|
|
||||||
import org.apache.hadoop.yarn.webapp.YarnJacksonJaxbJsonProvider;
|
import org.apache.hadoop.yarn.webapp.YarnJacksonJaxbJsonProvider;
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
|
|
||||||
|
@ -81,7 +80,6 @@ public class TimelineClientImpl extends TimelineClient {
|
||||||
|
|
||||||
private static final Log LOG = LogFactory.getLog(TimelineClientImpl.class);
|
private static final Log LOG = LogFactory.getLog(TimelineClientImpl.class);
|
||||||
private static final String RESOURCE_URI_STR = "/ws/v1/timeline/";
|
private static final String RESOURCE_URI_STR = "/ws/v1/timeline/";
|
||||||
private static final String URL_PARAM_USER_NAME = "user.name";
|
|
||||||
private static final Joiner JOINER = Joiner.on("");
|
private static final Joiner JOINER = Joiner.on("");
|
||||||
public final static int DEFAULT_SOCKET_TIMEOUT = 1 * 60 * 1000; // 1 minute
|
public final static int DEFAULT_SOCKET_TIMEOUT = 1 * 60 * 1000; // 1 minute
|
||||||
|
|
||||||
|
@ -99,9 +97,11 @@ public class TimelineClientImpl extends TimelineClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Client client;
|
private Client client;
|
||||||
|
private ConnectionConfigurator connConfigurator;
|
||||||
|
private DelegationTokenAuthenticator authenticator;
|
||||||
|
private DelegationTokenAuthenticatedURL.Token token;
|
||||||
private URI resURI;
|
private URI resURI;
|
||||||
private boolean isEnabled;
|
private boolean isEnabled;
|
||||||
private KerberosAuthenticatedURLConnectionFactory urlFactory;
|
|
||||||
|
|
||||||
public TimelineClientImpl() {
|
public TimelineClientImpl() {
|
||||||
super(TimelineClientImpl.class.getName());
|
super(TimelineClientImpl.class.getName());
|
||||||
|
@ -116,15 +116,17 @@ public class TimelineClientImpl extends TimelineClient {
|
||||||
} else {
|
} else {
|
||||||
ClientConfig cc = new DefaultClientConfig();
|
ClientConfig cc = new DefaultClientConfig();
|
||||||
cc.getClasses().add(YarnJacksonJaxbJsonProvider.class);
|
cc.getClasses().add(YarnJacksonJaxbJsonProvider.class);
|
||||||
ConnectionConfigurator connConfigurator = newConnConfigurator(conf);
|
connConfigurator = newConnConfigurator(conf);
|
||||||
if (UserGroupInformation.isSecurityEnabled()) {
|
if (UserGroupInformation.isSecurityEnabled()) {
|
||||||
TimelineAuthenticator.setStaticConnectionConfigurator(connConfigurator);
|
authenticator = new KerberosDelegationTokenAuthenticator();
|
||||||
urlFactory = new KerberosAuthenticatedURLConnectionFactory(connConfigurator);
|
|
||||||
client = new Client(new URLConnectionClientHandler(urlFactory), cc);
|
|
||||||
} else {
|
} else {
|
||||||
client = new Client(new URLConnectionClientHandler(
|
authenticator = new PseudoDelegationTokenAuthenticator();
|
||||||
new PseudoAuthenticatedURLConnectionFactory(connConfigurator)), cc);
|
|
||||||
}
|
}
|
||||||
|
authenticator.setConnectionConfigurator(connConfigurator);
|
||||||
|
client = new Client(new URLConnectionClientHandler(
|
||||||
|
new TimelineURLConnectionFactory()), cc);
|
||||||
|
token = new DelegationTokenAuthenticatedURL.Token();
|
||||||
|
|
||||||
if (YarnConfiguration.useHttps(conf)) {
|
if (YarnConfiguration.useHttps(conf)) {
|
||||||
resURI = URI
|
resURI = URI
|
||||||
.create(JOINER.join("https://", conf.get(
|
.create(JOINER.join("https://", conf.get(
|
||||||
|
@ -137,9 +139,6 @@ public class TimelineClientImpl extends TimelineClient {
|
||||||
YarnConfiguration.DEFAULT_TIMELINE_SERVICE_WEBAPP_ADDRESS),
|
YarnConfiguration.DEFAULT_TIMELINE_SERVICE_WEBAPP_ADDRESS),
|
||||||
RESOURCE_URI_STR));
|
RESOURCE_URI_STR));
|
||||||
}
|
}
|
||||||
if (UserGroupInformation.isSecurityEnabled()) {
|
|
||||||
urlFactory.setService(TimelineUtils.buildTimelineTokenService(conf));
|
|
||||||
}
|
|
||||||
LOG.info("Timeline service address: " + resURI);
|
LOG.info("Timeline service address: " + resURI);
|
||||||
}
|
}
|
||||||
super.serviceInit(conf);
|
super.serviceInit(conf);
|
||||||
|
@ -199,11 +198,34 @@ public class TimelineClientImpl extends TimelineClient {
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public Token<TimelineDelegationTokenIdentifier> getDelegationToken(
|
public Token<TimelineDelegationTokenIdentifier> getDelegationToken(
|
||||||
String renewer) throws IOException, YarnException {
|
final String renewer) throws IOException, YarnException {
|
||||||
return TimelineAuthenticator.getDelegationToken(resURI.toURL(),
|
boolean isProxyAccess =
|
||||||
urlFactory.token, renewer);
|
UserGroupInformation.getCurrentUser().getAuthenticationMethod()
|
||||||
|
== UserGroupInformation.AuthenticationMethod.PROXY;
|
||||||
|
UserGroupInformation callerUGI = isProxyAccess ?
|
||||||
|
UserGroupInformation.getCurrentUser().getRealUser()
|
||||||
|
: UserGroupInformation.getCurrentUser();
|
||||||
|
final String doAsUser = isProxyAccess ?
|
||||||
|
UserGroupInformation.getCurrentUser().getShortUserName() : null;
|
||||||
|
try {
|
||||||
|
return callerUGI.doAs(
|
||||||
|
new PrivilegedExceptionAction<Token<TimelineDelegationTokenIdentifier>>() {
|
||||||
|
@Override
|
||||||
|
public Token<TimelineDelegationTokenIdentifier> run() throws Exception {
|
||||||
|
DelegationTokenAuthenticatedURL authUrl =
|
||||||
|
new DelegationTokenAuthenticatedURL(authenticator, connConfigurator);
|
||||||
|
return (Token) authUrl.getDelegationToken(
|
||||||
|
resURI.toURL(), token, renewer, doAsUser);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (UndeclaredThrowableException e) {
|
||||||
|
throw new IOException(e.getCause());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Private
|
@Private
|
||||||
|
@ -223,91 +245,35 @@ public class TimelineClientImpl extends TimelineClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PseudoAuthenticatedURLConnectionFactory
|
private class TimelineURLConnectionFactory
|
||||||
implements HttpURLConnectionFactory {
|
implements HttpURLConnectionFactory {
|
||||||
|
|
||||||
private ConnectionConfigurator connConfigurator;
|
|
||||||
|
|
||||||
public PseudoAuthenticatedURLConnectionFactory(
|
|
||||||
ConnectionConfigurator connConfigurator) {
|
|
||||||
this.connConfigurator = connConfigurator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpURLConnection getHttpURLConnection(URL url) throws IOException {
|
public HttpURLConnection getHttpURLConnection(final URL url) throws IOException {
|
||||||
Map<String, String> params = new HashMap<String, String>();
|
boolean isProxyAccess =
|
||||||
params.put(URL_PARAM_USER_NAME,
|
UserGroupInformation.getCurrentUser().getAuthenticationMethod()
|
||||||
UserGroupInformation.getCurrentUser().getShortUserName());
|
== UserGroupInformation.AuthenticationMethod.PROXY;
|
||||||
url = TimelineAuthenticator.appendParams(url, params);
|
UserGroupInformation callerUGI = isProxyAccess ?
|
||||||
if (LOG.isDebugEnabled()) {
|
UserGroupInformation.getCurrentUser().getRealUser()
|
||||||
LOG.debug("URL with delegation token: " + url);
|
: UserGroupInformation.getCurrentUser();
|
||||||
}
|
final String doAsUser = isProxyAccess ?
|
||||||
return connConfigurator.configure((HttpURLConnection) url.openConnection());
|
UserGroupInformation.getCurrentUser().getShortUserName() : null;
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
private static class KerberosAuthenticatedURLConnectionFactory
|
|
||||||
implements HttpURLConnectionFactory {
|
|
||||||
|
|
||||||
private AuthenticatedURL.Token token;
|
|
||||||
private TimelineAuthenticator authenticator;
|
|
||||||
private Token<TimelineDelegationTokenIdentifier> dToken;
|
|
||||||
private Text service;
|
|
||||||
private ConnectionConfigurator connConfigurator;
|
|
||||||
|
|
||||||
public KerberosAuthenticatedURLConnectionFactory(
|
|
||||||
ConnectionConfigurator connConfigurator) {
|
|
||||||
token = new AuthenticatedURL.Token();
|
|
||||||
authenticator = new TimelineAuthenticator();
|
|
||||||
this.connConfigurator = connConfigurator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HttpURLConnection getHttpURLConnection(URL url) throws IOException {
|
|
||||||
try {
|
try {
|
||||||
if (dToken == null) {
|
return callerUGI.doAs(new PrivilegedExceptionAction<HttpURLConnection>() {
|
||||||
//TODO: need to take care of the renew case
|
@Override
|
||||||
dToken = selectToken();
|
public HttpURLConnection run() throws Exception {
|
||||||
if (LOG.isDebugEnabled()) {
|
return new DelegationTokenAuthenticatedURL(
|
||||||
LOG.debug("Timeline delegation token: " + dToken.toString());
|
authenticator, connConfigurator).openConnection(url, token,
|
||||||
|
doAsUser);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
if (dToken != null) {
|
} catch (UndeclaredThrowableException e) {
|
||||||
Map<String, String> params = new HashMap<String, String>();
|
throw new IOException(e.getCause());
|
||||||
TimelineAuthenticator.injectDelegationToken(params, dToken);
|
} catch (InterruptedException e) {
|
||||||
url = TimelineAuthenticator.appendParams(url, params);
|
|
||||||
if (LOG.isDebugEnabled()) {
|
|
||||||
LOG.debug("URL with delegation token: " + url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new AuthenticatedURL(
|
|
||||||
authenticator, connConfigurator).openConnection(url, token);
|
|
||||||
} catch (AuthenticationException e) {
|
|
||||||
LOG.error("Authentication failed when openning connection [" + url
|
|
||||||
+ "] with token [" + token + "].", e);
|
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Token<TimelineDelegationTokenIdentifier> selectToken() {
|
|
||||||
UserGroupInformation ugi;
|
|
||||||
try {
|
|
||||||
ugi = UserGroupInformation.getCurrentUser();
|
|
||||||
} catch (IOException e) {
|
|
||||||
String msg = "Error when getting the current user";
|
|
||||||
LOG.error(msg, e);
|
|
||||||
throw new YarnRuntimeException(msg, e);
|
|
||||||
}
|
|
||||||
TimelineDelegationTokenSelector tokenSelector =
|
|
||||||
new TimelineDelegationTokenSelector();
|
|
||||||
return tokenSelector.selectToken(
|
|
||||||
service, ugi.getCredentials().getAllTokens());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setService(Text service) {
|
|
||||||
this.service = service;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConnectionConfigurator newConnConfigurator(Configuration conf) {
|
private static ConnectionConfigurator newConnConfigurator(Configuration conf) {
|
||||||
|
|
|
@ -38,22 +38,9 @@ public abstract class YARNDelegationTokenIdentifier extends
|
||||||
public YARNDelegationTokenIdentifier() {}
|
public YARNDelegationTokenIdentifier() {}
|
||||||
|
|
||||||
public YARNDelegationTokenIdentifier(Text owner, Text renewer, Text realUser) {
|
public YARNDelegationTokenIdentifier(Text owner, Text renewer, Text realUser) {
|
||||||
if (owner != null) {
|
setOwner(owner);
|
||||||
builder.setOwner(owner.toString());
|
setRenewer(renewer);
|
||||||
}
|
setRealUser(realUser);
|
||||||
|
|
||||||
if (renewer != null) {
|
|
||||||
HadoopKerberosName renewerKrbName = new HadoopKerberosName(renewer.toString());
|
|
||||||
try {
|
|
||||||
builder.setRenewer(renewerKrbName.getShortName());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (realUser != null) {
|
|
||||||
builder.setRealUser(realUser.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,6 +77,13 @@ public abstract class YARNDelegationTokenIdentifier extends
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOwner(Text owner) {
|
||||||
|
if (builder != null && owner != null) {
|
||||||
|
builder.setOwner(owner.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Text getRenewer() {
|
public Text getRenewer() {
|
||||||
String renewer = builder.getRenewer();
|
String renewer = builder.getRenewer();
|
||||||
if (renewer == null) {
|
if (renewer == null) {
|
||||||
|
@ -99,6 +93,18 @@ public abstract class YARNDelegationTokenIdentifier extends
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRenewer(Text renewer) {
|
||||||
|
if (builder != null && renewer != null) {
|
||||||
|
HadoopKerberosName renewerKrbName = new HadoopKerberosName(renewer.toString());
|
||||||
|
try {
|
||||||
|
builder.setRenewer(renewerKrbName.getShortName());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Text getRealUser() {
|
public Text getRealUser() {
|
||||||
String realUser = builder.getRealUser();
|
String realUser = builder.getRealUser();
|
||||||
if (realUser == null) {
|
if (realUser == null) {
|
||||||
|
@ -108,6 +114,13 @@ public abstract class YARNDelegationTokenIdentifier extends
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRealUser(Text realUser) {
|
||||||
|
if (builder != null && realUser != null) {
|
||||||
|
builder.setRealUser(realUser.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setIssueDate(long issueDate) {
|
public void setIssueDate(long issueDate) {
|
||||||
builder.setIssueDate(issueDate);
|
builder.setIssueDate(issueDate);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.client.api.impl;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class TestTimelineAuthenticator {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHasDelegationTokens() throws Exception {
|
|
||||||
TimelineAuthenticator authenticator = new TimelineAuthenticator();
|
|
||||||
Assert.assertFalse(authenticator.hasDelegationToken(new URL(
|
|
||||||
"http://localhost:8/resource")));
|
|
||||||
Assert.assertFalse(authenticator.hasDelegationToken(new URL(
|
|
||||||
"http://localhost:8/resource?other=xxxx")));
|
|
||||||
Assert.assertTrue(authenticator.hasDelegationToken(new URL(
|
|
||||||
"http://localhost:8/resource?delegation=yyyy")));
|
|
||||||
Assert.assertTrue(authenticator.hasDelegationToken(new URL(
|
|
||||||
"http://localhost:8/resource?other=xxxx&delegation=yyyy")));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -160,5 +160,18 @@
|
||||||
<artifactId>leveldbjni-all</artifactId>
|
<artifactId>leveldbjni-all</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 'mvn dependency:analyze' fails to detect use of this dependency -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.hadoop</groupId>
|
||||||
|
<artifactId>hadoop-minikdc</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.hadoop</groupId>
|
||||||
|
<artifactId>hadoop-auth</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
<type>test-jar</type>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.apache.hadoop.yarn.server.timeline.LeveldbTimelineStore;
|
||||||
import org.apache.hadoop.yarn.server.timeline.TimelineDataManager;
|
import org.apache.hadoop.yarn.server.timeline.TimelineDataManager;
|
||||||
import org.apache.hadoop.yarn.server.timeline.TimelineStore;
|
import org.apache.hadoop.yarn.server.timeline.TimelineStore;
|
||||||
import org.apache.hadoop.yarn.server.timeline.security.TimelineACLsManager;
|
import org.apache.hadoop.yarn.server.timeline.security.TimelineACLsManager;
|
||||||
|
import org.apache.hadoop.yarn.server.timeline.security.TimelineAuthenticationFilter;
|
||||||
import org.apache.hadoop.yarn.server.timeline.security.TimelineAuthenticationFilterInitializer;
|
import org.apache.hadoop.yarn.server.timeline.security.TimelineAuthenticationFilterInitializer;
|
||||||
import org.apache.hadoop.yarn.server.timeline.security.TimelineDelegationTokenSecretManagerService;
|
import org.apache.hadoop.yarn.server.timeline.security.TimelineDelegationTokenSecretManagerService;
|
||||||
import org.apache.hadoop.yarn.server.timeline.webapp.CrossOriginFilterInitializer;
|
import org.apache.hadoop.yarn.server.timeline.webapp.CrossOriginFilterInitializer;
|
||||||
|
@ -213,6 +214,8 @@ public class ApplicationHistoryServer extends CompositeService {
|
||||||
|
|
||||||
private void startWebApp() {
|
private void startWebApp() {
|
||||||
Configuration conf = getConfig();
|
Configuration conf = getConfig();
|
||||||
|
TimelineAuthenticationFilter.setTimelineDelegationTokenSecretManager(
|
||||||
|
secretManagerService.getTimelineDelegationTokenSecretManager());
|
||||||
// Always load pseudo authentication filter to parse "user.name" in an URL
|
// Always load pseudo authentication filter to parse "user.name" in an URL
|
||||||
// to identify a HTTP request's user in insecure mode.
|
// to identify a HTTP request's user in insecure mode.
|
||||||
// When Kerberos authentication type is set (i.e., secure mode is turned on),
|
// When Kerberos authentication type is set (i.e., secure mode is turned on),
|
||||||
|
@ -263,15 +266,12 @@ public class ApplicationHistoryServer extends CompositeService {
|
||||||
WebAppUtils.getAHSWebAppURLWithoutScheme(conf));
|
WebAppUtils.getAHSWebAppURLWithoutScheme(conf));
|
||||||
LOG.info("Instantiating AHSWebApp at " + bindAddress);
|
LOG.info("Instantiating AHSWebApp at " + bindAddress);
|
||||||
try {
|
try {
|
||||||
AHSWebApp ahsWebApp = AHSWebApp.getInstance();
|
|
||||||
ahsWebApp.setApplicationHistoryManager(historyManager);
|
|
||||||
ahsWebApp.setTimelineDelegationTokenSecretManagerService(secretManagerService);
|
|
||||||
ahsWebApp.setTimelineDataManager(timelineDataManager);
|
|
||||||
webApp =
|
webApp =
|
||||||
WebApps
|
WebApps
|
||||||
.$for("applicationhistory", ApplicationHistoryClientService.class,
|
.$for("applicationhistory", ApplicationHistoryClientService.class,
|
||||||
ahsClientService, "ws")
|
ahsClientService, "ws")
|
||||||
.with(conf).at(bindAddress).start(ahsWebApp);
|
.with(conf).at(bindAddress).start(
|
||||||
|
new AHSWebApp(timelineDataManager, historyManager));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
String msg = "AHSWebApp failed to start.";
|
String msg = "AHSWebApp failed to start.";
|
||||||
LOG.error(msg, e);
|
LOG.error(msg, e);
|
||||||
|
|
|
@ -19,71 +19,34 @@ package org.apache.hadoop.yarn.server.applicationhistoryservice.webapp;
|
||||||
|
|
||||||
import static org.apache.hadoop.yarn.util.StringHelper.pajoin;
|
import static org.apache.hadoop.yarn.util.StringHelper.pajoin;
|
||||||
|
|
||||||
import org.apache.hadoop.classification.InterfaceAudience.Private;
|
|
||||||
import org.apache.hadoop.yarn.server.api.ApplicationContext;
|
import org.apache.hadoop.yarn.server.api.ApplicationContext;
|
||||||
import org.apache.hadoop.yarn.server.applicationhistoryservice.ApplicationHistoryManager;
|
import org.apache.hadoop.yarn.server.applicationhistoryservice.ApplicationHistoryManager;
|
||||||
import org.apache.hadoop.yarn.server.timeline.TimelineDataManager;
|
import org.apache.hadoop.yarn.server.timeline.TimelineDataManager;
|
||||||
import org.apache.hadoop.yarn.server.timeline.security.TimelineDelegationTokenSecretManagerService;
|
|
||||||
import org.apache.hadoop.yarn.server.timeline.webapp.TimelineWebServices;
|
import org.apache.hadoop.yarn.server.timeline.webapp.TimelineWebServices;
|
||||||
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
|
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
|
||||||
import org.apache.hadoop.yarn.webapp.WebApp;
|
import org.apache.hadoop.yarn.webapp.WebApp;
|
||||||
import org.apache.hadoop.yarn.webapp.YarnJacksonJaxbJsonProvider;
|
import org.apache.hadoop.yarn.webapp.YarnJacksonJaxbJsonProvider;
|
||||||
import org.apache.hadoop.yarn.webapp.YarnWebParams;
|
import org.apache.hadoop.yarn.webapp.YarnWebParams;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
public class AHSWebApp extends WebApp implements YarnWebParams {
|
public class AHSWebApp extends WebApp implements YarnWebParams {
|
||||||
|
|
||||||
private ApplicationHistoryManager applicationHistoryManager;
|
private ApplicationHistoryManager applicationHistoryManager;
|
||||||
private TimelineDelegationTokenSecretManagerService secretManagerService;
|
|
||||||
private TimelineDataManager timelineDataManager;
|
private TimelineDataManager timelineDataManager;
|
||||||
|
|
||||||
private static AHSWebApp instance = null;
|
public AHSWebApp(TimelineDataManager timelineDataManager,
|
||||||
|
ApplicationHistoryManager applicationHistoryManager) {
|
||||||
public static AHSWebApp getInstance() {
|
this.timelineDataManager = timelineDataManager;
|
||||||
if (instance == null) {
|
this.applicationHistoryManager = applicationHistoryManager;
|
||||||
instance = new AHSWebApp();
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Private
|
|
||||||
@VisibleForTesting
|
|
||||||
public static void resetInstance() {
|
|
||||||
instance = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AHSWebApp() {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ApplicationHistoryManager getApplicationHistoryManager() {
|
public ApplicationHistoryManager getApplicationHistoryManager() {
|
||||||
return applicationHistoryManager;
|
return applicationHistoryManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setApplicationHistoryManager(
|
|
||||||
ApplicationHistoryManager applicationHistoryManager) {
|
|
||||||
this.applicationHistoryManager = applicationHistoryManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TimelineDelegationTokenSecretManagerService
|
|
||||||
getTimelineDelegationTokenSecretManagerService() {
|
|
||||||
return secretManagerService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTimelineDelegationTokenSecretManagerService(
|
|
||||||
TimelineDelegationTokenSecretManagerService secretManagerService) {
|
|
||||||
this.secretManagerService = secretManagerService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TimelineDataManager getTimelineDataManager() {
|
public TimelineDataManager getTimelineDataManager() {
|
||||||
return timelineDataManager;
|
return timelineDataManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimelineDataManager(TimelineDataManager timelineDataManager) {
|
|
||||||
this.timelineDataManager = timelineDataManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setup() {
|
public void setup() {
|
||||||
bind(YarnJacksonJaxbJsonProvider.class);
|
bind(YarnJacksonJaxbJsonProvider.class);
|
||||||
|
@ -91,8 +54,6 @@ public class AHSWebApp extends WebApp implements YarnWebParams {
|
||||||
bind(TimelineWebServices.class);
|
bind(TimelineWebServices.class);
|
||||||
bind(GenericExceptionHandler.class);
|
bind(GenericExceptionHandler.class);
|
||||||
bind(ApplicationContext.class).toInstance(applicationHistoryManager);
|
bind(ApplicationContext.class).toInstance(applicationHistoryManager);
|
||||||
bind(TimelineDelegationTokenSecretManagerService.class).toInstance(
|
|
||||||
secretManagerService);
|
|
||||||
bind(TimelineDataManager.class).toInstance(timelineDataManager);
|
bind(TimelineDataManager.class).toInstance(timelineDataManager);
|
||||||
route("/", AHSController.class);
|
route("/", AHSController.class);
|
||||||
route(pajoin("/apps", APP_STATE), AHSController.class);
|
route(pajoin("/apps", APP_STATE), AHSController.class);
|
||||||
|
|
|
@ -18,32 +18,32 @@
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.timeline.security;
|
package org.apache.hadoop.yarn.server.timeline.security;
|
||||||
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import javax.servlet.FilterConfig;
|
import javax.servlet.FilterConfig;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
|
||||||
import org.apache.hadoop.classification.InterfaceAudience.Private;
|
import org.apache.hadoop.classification.InterfaceAudience.Private;
|
||||||
import org.apache.hadoop.classification.InterfaceStability.Unstable;
|
import org.apache.hadoop.classification.InterfaceStability.Unstable;
|
||||||
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticationFilter;
|
||||||
|
import org.apache.hadoop.yarn.server.timeline.security.TimelineDelegationTokenSecretManagerService.TimelineDelegationTokenSecretManager;
|
||||||
|
|
||||||
@Private
|
@Private
|
||||||
@Unstable
|
@Unstable
|
||||||
public class TimelineAuthenticationFilter extends AuthenticationFilter {
|
public class TimelineAuthenticationFilter
|
||||||
|
extends DelegationTokenAuthenticationFilter {
|
||||||
|
|
||||||
|
private static TimelineDelegationTokenSecretManager secretManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Properties getConfiguration(String configPrefix,
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
FilterConfig filterConfig) throws ServletException {
|
filterConfig.getServletContext().setAttribute(
|
||||||
// In yarn-site.xml, we can simply set type to "kerberos". However, we need
|
DelegationTokenAuthenticationFilter.DELEGATION_TOKEN_SECRET_MANAGER_ATTR,
|
||||||
// to replace the name here to use the customized Kerberos + DT service
|
secretManager);
|
||||||
// instead of the standard Kerberos handler.
|
super.init(filterConfig);
|
||||||
Properties properties = super.getConfiguration(configPrefix, filterConfig);
|
|
||||||
String authType = properties.getProperty(AUTH_TYPE);
|
|
||||||
if (authType != null && authType.equals("kerberos")) {
|
|
||||||
properties.setProperty(
|
|
||||||
AUTH_TYPE, TimelineClientAuthenticationService.class.getName());
|
|
||||||
}
|
}
|
||||||
return properties;
|
|
||||||
|
public static void setTimelineDelegationTokenSecretManager(
|
||||||
|
TimelineDelegationTokenSecretManager secretManager) {
|
||||||
|
TimelineAuthenticationFilter.secretManager = secretManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,16 @@ import org.apache.hadoop.http.FilterInitializer;
|
||||||
import org.apache.hadoop.http.HttpServer2;
|
import org.apache.hadoop.http.HttpServer2;
|
||||||
import org.apache.hadoop.io.IOUtils;
|
import org.apache.hadoop.io.IOUtils;
|
||||||
import org.apache.hadoop.security.SecurityUtil;
|
import org.apache.hadoop.security.SecurityUtil;
|
||||||
|
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
||||||
|
import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
|
||||||
|
import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler;
|
||||||
|
import org.apache.hadoop.security.authorize.ProxyUsers;
|
||||||
|
import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticationHandler;
|
||||||
|
import org.apache.hadoop.security.token.delegation.web.KerberosDelegationTokenAuthenticationHandler;
|
||||||
|
import org.apache.hadoop.security.token.delegation.web.PseudoDelegationTokenAuthenticationHandler;
|
||||||
|
import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenIdentifier;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -54,6 +64,9 @@ public class TimelineAuthenticationFilterInitializer extends FilterInitializer {
|
||||||
private static final String SIGNATURE_SECRET_FILE =
|
private static final String SIGNATURE_SECRET_FILE =
|
||||||
TimelineAuthenticationFilter.SIGNATURE_SECRET + ".file";
|
TimelineAuthenticationFilter.SIGNATURE_SECRET + ".file";
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
Map<String, String> filterConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Initializes {@link TimelineAuthenticationFilter}
|
* Initializes {@link TimelineAuthenticationFilter}
|
||||||
|
@ -71,14 +84,24 @@ public class TimelineAuthenticationFilterInitializer extends FilterInitializer {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void initFilter(FilterContainer container, Configuration conf) {
|
public void initFilter(FilterContainer container, Configuration conf) {
|
||||||
Map<String, String> filterConfig = new HashMap<String, String>();
|
filterConfig = new HashMap<String, String>();
|
||||||
|
|
||||||
// setting the cookie path to root '/' so it is used for all resources.
|
// setting the cookie path to root '/' so it is used for all resources.
|
||||||
filterConfig.put(TimelineAuthenticationFilter.COOKIE_PATH, "/");
|
filterConfig.put(TimelineAuthenticationFilter.COOKIE_PATH, "/");
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> entry : conf) {
|
||||||
|
String name = entry.getKey();
|
||||||
|
if (name.startsWith(ProxyUsers.CONF_HADOOP_PROXYUSER)) {
|
||||||
|
String value = conf.get(name);
|
||||||
|
name = name.substring("hadoop.".length());
|
||||||
|
filterConfig.put(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (Map.Entry<String, String> entry : conf) {
|
for (Map.Entry<String, String> entry : conf) {
|
||||||
String name = entry.getKey();
|
String name = entry.getKey();
|
||||||
if (name.startsWith(PREFIX)) {
|
if (name.startsWith(PREFIX)) {
|
||||||
|
// yarn.timeline-service.http-authentication.proxyuser will override
|
||||||
|
// hadoop.proxyuser
|
||||||
String value = conf.get(name);
|
String value = conf.get(name);
|
||||||
name = name.substring(PREFIX.length());
|
name = name.substring(PREFIX.length());
|
||||||
filterConfig.put(name, value);
|
filterConfig.put(name, value);
|
||||||
|
@ -108,10 +131,18 @@ public class TimelineAuthenticationFilterInitializer extends FilterInitializer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String authType = filterConfig.get(AuthenticationFilter.AUTH_TYPE);
|
||||||
|
if (authType.equals(PseudoAuthenticationHandler.TYPE)) {
|
||||||
|
filterConfig.put(AuthenticationFilter.AUTH_TYPE,
|
||||||
|
PseudoDelegationTokenAuthenticationHandler.class.getName());
|
||||||
|
} else if (authType.equals(KerberosAuthenticationHandler.TYPE)) {
|
||||||
|
filterConfig.put(AuthenticationFilter.AUTH_TYPE,
|
||||||
|
KerberosDelegationTokenAuthenticationHandler.class.getName());
|
||||||
|
|
||||||
// Resolve _HOST into bind address
|
// Resolve _HOST into bind address
|
||||||
String bindAddress = conf.get(HttpServer2.BIND_ADDRESS);
|
String bindAddress = conf.get(HttpServer2.BIND_ADDRESS);
|
||||||
String principal =
|
String principal =
|
||||||
filterConfig.get(TimelineClientAuthenticationService.PRINCIPAL);
|
filterConfig.get(KerberosAuthenticationHandler.PRINCIPAL);
|
||||||
if (principal != null) {
|
if (principal != null) {
|
||||||
try {
|
try {
|
||||||
principal = SecurityUtil.getServerPrincipal(principal, bindAddress);
|
principal = SecurityUtil.getServerPrincipal(principal, bindAddress);
|
||||||
|
@ -119,9 +150,13 @@ public class TimelineAuthenticationFilterInitializer extends FilterInitializer {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"Could not resolve Kerberos principal name: " + ex.toString(), ex);
|
"Could not resolve Kerberos principal name: " + ex.toString(), ex);
|
||||||
}
|
}
|
||||||
filterConfig.put(TimelineClientAuthenticationService.PRINCIPAL,
|
filterConfig.put(KerberosAuthenticationHandler.PRINCIPAL,
|
||||||
principal);
|
principal);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filterConfig.put(DelegationTokenAuthenticationHandler.TOKEN_KIND,
|
||||||
|
TimelineDelegationTokenIdentifier.KIND_NAME.toString());
|
||||||
|
|
||||||
container.addGlobalFilter("Timeline Authentication Filter",
|
container.addGlobalFilter("Timeline Authentication Filter",
|
||||||
TimelineAuthenticationFilter.class.getName(),
|
TimelineAuthenticationFilter.class.getName(),
|
||||||
|
|
|
@ -1,236 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.timeline.security;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.text.MessageFormat;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.apache.hadoop.classification.InterfaceAudience.Private;
|
|
||||||
import org.apache.hadoop.classification.InterfaceStability.Unstable;
|
|
||||||
import org.apache.hadoop.security.UserGroupInformation;
|
|
||||||
import org.apache.hadoop.security.authentication.client.AuthenticationException;
|
|
||||||
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.yarn.api.records.timeline.TimelineDelegationTokenResponse;
|
|
||||||
import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenIdentifier;
|
|
||||||
import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenOperation;
|
|
||||||
import org.apache.hadoop.yarn.security.client.TimelineAuthenticationConsts;
|
|
||||||
import org.apache.hadoop.yarn.server.applicationhistoryservice.webapp.AHSWebApp;
|
|
||||||
import org.apache.hadoop.yarn.webapp.YarnJacksonJaxbJsonProvider;
|
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Server side <code>AuthenticationHandler</code> that authenticates requests
|
|
||||||
* using the incoming delegation token as a 'delegation' query string parameter.
|
|
||||||
* <p/>
|
|
||||||
* If not delegation token is present in the request it delegates to the
|
|
||||||
* {@link KerberosAuthenticationHandler}
|
|
||||||
*/
|
|
||||||
@Private
|
|
||||||
@Unstable
|
|
||||||
public class TimelineClientAuthenticationService
|
|
||||||
extends KerberosAuthenticationHandler {
|
|
||||||
|
|
||||||
public static final String TYPE = "kerberos-dt";
|
|
||||||
private static final Set<String> DELEGATION_TOKEN_OPS = new HashSet<String>();
|
|
||||||
private static final String OP_PARAM = "op";
|
|
||||||
private static final String ENTER = System.getProperty("line.separator");
|
|
||||||
|
|
||||||
private ObjectMapper mapper;
|
|
||||||
|
|
||||||
static {
|
|
||||||
DELEGATION_TOKEN_OPS.add(
|
|
||||||
TimelineDelegationTokenOperation.GETDELEGATIONTOKEN.toString());
|
|
||||||
DELEGATION_TOKEN_OPS.add(
|
|
||||||
TimelineDelegationTokenOperation.RENEWDELEGATIONTOKEN.toString());
|
|
||||||
DELEGATION_TOKEN_OPS.add(
|
|
||||||
TimelineDelegationTokenOperation.CANCELDELEGATIONTOKEN.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public TimelineClientAuthenticationService() {
|
|
||||||
super();
|
|
||||||
mapper = new ObjectMapper();
|
|
||||||
YarnJacksonJaxbJsonProvider.configObjectMapper(mapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns authentication type of the handler.
|
|
||||||
*
|
|
||||||
* @return <code>delegationtoken-kerberos</code>
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String getType() {
|
|
||||||
return TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean managementOperation(AuthenticationToken token,
|
|
||||||
HttpServletRequest request, HttpServletResponse response)
|
|
||||||
throws IOException, AuthenticationException {
|
|
||||||
boolean requestContinues = true;
|
|
||||||
String op = request.getParameter(OP_PARAM);
|
|
||||||
op = (op != null) ? op.toUpperCase() : null;
|
|
||||||
if (DELEGATION_TOKEN_OPS.contains(op) &&
|
|
||||||
!request.getMethod().equals("OPTIONS")) {
|
|
||||||
TimelineDelegationTokenOperation dtOp =
|
|
||||||
TimelineDelegationTokenOperation.valueOf(op);
|
|
||||||
if (dtOp.getHttpMethod().equals(request.getMethod())) {
|
|
||||||
if (dtOp.requiresKerberosCredentials() && token == null) {
|
|
||||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
|
|
||||||
MessageFormat.format(
|
|
||||||
"Operation [{0}] requires SPNEGO authentication established",
|
|
||||||
dtOp));
|
|
||||||
requestContinues = false;
|
|
||||||
} else {
|
|
||||||
TimelineDelegationTokenSecretManagerService secretManager =
|
|
||||||
AHSWebApp.getInstance()
|
|
||||||
.getTimelineDelegationTokenSecretManagerService();
|
|
||||||
try {
|
|
||||||
TimelineDelegationTokenResponse res = null;
|
|
||||||
switch (dtOp) {
|
|
||||||
case GETDELEGATIONTOKEN:
|
|
||||||
UserGroupInformation ownerUGI =
|
|
||||||
UserGroupInformation.createRemoteUser(token.getUserName());
|
|
||||||
String renewerParam =
|
|
||||||
request
|
|
||||||
.getParameter(TimelineAuthenticationConsts.RENEWER_PARAM);
|
|
||||||
if (renewerParam == null) {
|
|
||||||
renewerParam = token.getUserName();
|
|
||||||
}
|
|
||||||
Token<?> dToken =
|
|
||||||
secretManager.createToken(ownerUGI, renewerParam);
|
|
||||||
res = new TimelineDelegationTokenResponse();
|
|
||||||
res.setType(TimelineAuthenticationConsts.DELEGATION_TOKEN_URL);
|
|
||||||
res.setContent(dToken.encodeToUrlString());
|
|
||||||
break;
|
|
||||||
case RENEWDELEGATIONTOKEN:
|
|
||||||
case CANCELDELEGATIONTOKEN:
|
|
||||||
String tokenParam =
|
|
||||||
request
|
|
||||||
.getParameter(TimelineAuthenticationConsts.TOKEN_PARAM);
|
|
||||||
if (tokenParam == null) {
|
|
||||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
|
|
||||||
MessageFormat
|
|
||||||
.format(
|
|
||||||
"Operation [{0}] requires the parameter [{1}]",
|
|
||||||
dtOp,
|
|
||||||
TimelineAuthenticationConsts.TOKEN_PARAM));
|
|
||||||
requestContinues = false;
|
|
||||||
} else {
|
|
||||||
if (dtOp == TimelineDelegationTokenOperation.CANCELDELEGATIONTOKEN) {
|
|
||||||
Token<TimelineDelegationTokenIdentifier> dt =
|
|
||||||
new Token<TimelineDelegationTokenIdentifier>();
|
|
||||||
dt.decodeFromUrlString(tokenParam);
|
|
||||||
secretManager.cancelToken(dt, token.getUserName());
|
|
||||||
} else {
|
|
||||||
Token<TimelineDelegationTokenIdentifier> dt =
|
|
||||||
new Token<TimelineDelegationTokenIdentifier>();
|
|
||||||
dt.decodeFromUrlString(tokenParam);
|
|
||||||
long expirationTime =
|
|
||||||
secretManager.renewToken(dt, token.getUserName());
|
|
||||||
res = new TimelineDelegationTokenResponse();
|
|
||||||
res.setType(TimelineAuthenticationConsts.DELEGATION_TOKEN_EXPIRATION_TIME);
|
|
||||||
res.setContent(expirationTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (requestContinues) {
|
|
||||||
response.setStatus(HttpServletResponse.SC_OK);
|
|
||||||
if (res != null) {
|
|
||||||
response.setContentType(MediaType.APPLICATION_JSON);
|
|
||||||
Writer writer = response.getWriter();
|
|
||||||
mapper.writeValue(writer, res);
|
|
||||||
writer.write(ENTER);
|
|
||||||
writer.flush();
|
|
||||||
|
|
||||||
}
|
|
||||||
requestContinues = false;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new AuthenticationException(e.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
response
|
|
||||||
.sendError(
|
|
||||||
HttpServletResponse.SC_BAD_REQUEST,
|
|
||||||
MessageFormat
|
|
||||||
.format(
|
|
||||||
"Wrong HTTP method [{0}] for operation [{1}], it should be [{2}]",
|
|
||||||
request.getMethod(), dtOp, dtOp.getHttpMethod()));
|
|
||||||
requestContinues = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return requestContinues;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Authenticates a request looking for the <code>delegation</code>
|
|
||||||
* query-string parameter and verifying it is a valid token. If there is not
|
|
||||||
* <code>delegation</code> query-string parameter, it delegates the
|
|
||||||
* authentication to the {@link KerberosAuthenticationHandler} unless it is
|
|
||||||
* disabled.
|
|
||||||
*
|
|
||||||
* @param request
|
|
||||||
* the HTTP client request.
|
|
||||||
* @param response
|
|
||||||
* the HTTP client response.
|
|
||||||
*
|
|
||||||
* @return the authentication token for the authenticated request.
|
|
||||||
* @throws IOException
|
|
||||||
* thrown if an IO error occurred.
|
|
||||||
* @throws AuthenticationException
|
|
||||||
* thrown if the authentication failed.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public AuthenticationToken authenticate(HttpServletRequest request,
|
|
||||||
HttpServletResponse response)
|
|
||||||
throws IOException, AuthenticationException {
|
|
||||||
AuthenticationToken token;
|
|
||||||
String delegationParam =
|
|
||||||
request
|
|
||||||
.getParameter(TimelineAuthenticationConsts.DELEGATION_PARAM);
|
|
||||||
if (delegationParam != null) {
|
|
||||||
Token<TimelineDelegationTokenIdentifier> dt =
|
|
||||||
new Token<TimelineDelegationTokenIdentifier>();
|
|
||||||
dt.decodeFromUrlString(delegationParam);
|
|
||||||
TimelineDelegationTokenSecretManagerService secretManager =
|
|
||||||
AHSWebApp.getInstance()
|
|
||||||
.getTimelineDelegationTokenSecretManagerService();
|
|
||||||
UserGroupInformation ugi = secretManager.verifyToken(dt);
|
|
||||||
final String shortName = ugi.getShortUserName();
|
|
||||||
// creating a ephemeral token
|
|
||||||
token = new AuthenticationToken(shortName, ugi.getUserName(), getType());
|
|
||||||
token.setExpires(0);
|
|
||||||
} else {
|
|
||||||
token = super.authenticate(request, response);
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -77,73 +77,11 @@ public class TimelineDelegationTokenSecretManagerService extends AbstractService
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a delegation token.
|
* Ge the instance of {link #TimelineDelegationTokenSecretManager}
|
||||||
*
|
* @return the instance of {link #TimelineDelegationTokenSecretManager}
|
||||||
* @param ugi UGI creating the token.
|
|
||||||
* @param renewer token renewer.
|
|
||||||
* @return new delegation token.
|
|
||||||
* @throws IOException thrown if the token could not be created.
|
|
||||||
*/
|
*/
|
||||||
public Token<TimelineDelegationTokenIdentifier> createToken(
|
public TimelineDelegationTokenSecretManager getTimelineDelegationTokenSecretManager() {
|
||||||
UserGroupInformation ugi, String renewer) throws IOException {
|
return secretManager;
|
||||||
renewer = (renewer == null) ? ugi.getShortUserName() : renewer;
|
|
||||||
String user = ugi.getUserName();
|
|
||||||
Text owner = new Text(user);
|
|
||||||
Text realUser = null;
|
|
||||||
if (ugi.getRealUser() != null) {
|
|
||||||
realUser = new Text(ugi.getRealUser().getUserName());
|
|
||||||
}
|
|
||||||
TimelineDelegationTokenIdentifier tokenIdentifier =
|
|
||||||
new TimelineDelegationTokenIdentifier(owner, new Text(renewer), realUser);
|
|
||||||
Token<TimelineDelegationTokenIdentifier> token =
|
|
||||||
new Token<TimelineDelegationTokenIdentifier>(tokenIdentifier, secretManager);
|
|
||||||
SecurityUtil.setTokenService(token, serviceAddr);
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renews a delegation token.
|
|
||||||
*
|
|
||||||
* @param token delegation token to renew.
|
|
||||||
* @param renewer token renewer.
|
|
||||||
* @throws IOException thrown if the token could not be renewed.
|
|
||||||
*/
|
|
||||||
public long renewToken(Token<TimelineDelegationTokenIdentifier> token,
|
|
||||||
String renewer) throws IOException {
|
|
||||||
return secretManager.renewToken(token, renewer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels a delegation token.
|
|
||||||
*
|
|
||||||
* @param token delegation token to cancel.
|
|
||||||
* @param canceler token canceler.
|
|
||||||
* @throws IOException thrown if the token could not be canceled.
|
|
||||||
*/
|
|
||||||
public void cancelToken(Token<TimelineDelegationTokenIdentifier> token,
|
|
||||||
String canceler) throws IOException {
|
|
||||||
secretManager.cancelToken(token, canceler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies a delegation token.
|
|
||||||
*
|
|
||||||
* @param token delegation token to verify.
|
|
||||||
* @return the UGI for the token.
|
|
||||||
* @throws IOException thrown if the token could not be verified.
|
|
||||||
*/
|
|
||||||
public UserGroupInformation verifyToken(Token<TimelineDelegationTokenIdentifier> token)
|
|
||||||
throws IOException {
|
|
||||||
ByteArrayInputStream buf = new ByteArrayInputStream(token.getIdentifier());
|
|
||||||
DataInputStream dis = new DataInputStream(buf);
|
|
||||||
TimelineDelegationTokenIdentifier id = new TimelineDelegationTokenIdentifier();
|
|
||||||
try {
|
|
||||||
id.readFields(dis);
|
|
||||||
secretManager.verifyToken(id, token.getPassword());
|
|
||||||
} finally {
|
|
||||||
dis.close();
|
|
||||||
}
|
|
||||||
return id.getUser();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -76,7 +76,6 @@ public class TestApplicationHistoryClientService extends
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
AHSWebApp.resetInstance();
|
|
||||||
historyServer.stop();
|
historyServer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,6 @@ public class TestApplicationHistoryServer {
|
||||||
Configuration tmp = historyServer.getConfig();
|
Configuration tmp = historyServer.getConfig();
|
||||||
assertEquals(expectedValue, tmp.get("hadoop.http.filter.initializers"));
|
assertEquals(expectedValue, tmp.get("hadoop.http.filter.initializers"));
|
||||||
historyServer.stop();
|
historyServer.stop();
|
||||||
AHSWebApp.resetInstance();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +113,5 @@ public class TestApplicationHistoryServer {
|
||||||
if (historyServer != null) {
|
if (historyServer != null) {
|
||||||
historyServer.stop();
|
historyServer.stop();
|
||||||
}
|
}
|
||||||
AHSWebApp.resetInstance();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.yarn.server.timeline.security;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.security.PrivilegedExceptionAction;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
|
||||||
|
import org.apache.hadoop.io.Text;
|
||||||
|
import org.apache.hadoop.minikdc.MiniKdc;
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
|
import org.apache.hadoop.security.authentication.KerberosTestUtils;
|
||||||
|
import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
|
||||||
|
import org.apache.hadoop.security.authorize.AuthorizationException;
|
||||||
|
import org.apache.hadoop.security.token.Token;
|
||||||
|
import org.apache.hadoop.yarn.api.records.timeline.TimelineEntity;
|
||||||
|
import org.apache.hadoop.yarn.api.records.timeline.TimelinePutResponse;
|
||||||
|
import org.apache.hadoop.yarn.client.api.TimelineClient;
|
||||||
|
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||||
|
import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenIdentifier;
|
||||||
|
import org.apache.hadoop.yarn.server.applicationhistoryservice.ApplicationHistoryServer;
|
||||||
|
import org.apache.hadoop.yarn.server.timeline.MemoryTimelineStore;
|
||||||
|
import org.apache.hadoop.yarn.server.timeline.TimelineStore;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class TestTimelineAuthenticationFilter {
|
||||||
|
|
||||||
|
private static final String FOO_USER = "foo";
|
||||||
|
private static final String BAR_USER = "bar";
|
||||||
|
private static final String HTTP_USER = "HTTP";
|
||||||
|
|
||||||
|
private static final File testRootDir = new File("target",
|
||||||
|
TestTimelineAuthenticationFilter.class.getName() + "-root");
|
||||||
|
private static File httpSpnegoKeytabFile = new File(
|
||||||
|
KerberosTestUtils.getKeytabFile());
|
||||||
|
private static String httpSpnegoPrincipal =
|
||||||
|
KerberosTestUtils.getServerPrincipal();
|
||||||
|
private static MiniKdc testMiniKDC;
|
||||||
|
private static ApplicationHistoryServer testTimelineServer;
|
||||||
|
private static Configuration conf;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setupClass() {
|
||||||
|
try {
|
||||||
|
testMiniKDC = new MiniKdc(MiniKdc.createConf(), testRootDir);
|
||||||
|
testMiniKDC.start();
|
||||||
|
testMiniKDC.createPrincipal(
|
||||||
|
httpSpnegoKeytabFile, HTTP_USER + "/localhost");
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertTrue("Couldn't setup MiniKDC", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
testTimelineServer = new ApplicationHistoryServer();
|
||||||
|
conf = new YarnConfiguration();
|
||||||
|
conf.setStrings(TimelineAuthenticationFilterInitializer.PREFIX + "type",
|
||||||
|
"kerberos");
|
||||||
|
conf.set(TimelineAuthenticationFilterInitializer.PREFIX +
|
||||||
|
KerberosAuthenticationHandler.PRINCIPAL, httpSpnegoPrincipal);
|
||||||
|
conf.set(TimelineAuthenticationFilterInitializer.PREFIX +
|
||||||
|
KerberosAuthenticationHandler.KEYTAB,
|
||||||
|
httpSpnegoKeytabFile.getAbsolutePath());
|
||||||
|
conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION,
|
||||||
|
"kerberos");
|
||||||
|
conf.set(YarnConfiguration.TIMELINE_SERVICE_PRINCIPAL,
|
||||||
|
httpSpnegoPrincipal);
|
||||||
|
conf.set(YarnConfiguration.TIMELINE_SERVICE_KEYTAB,
|
||||||
|
httpSpnegoKeytabFile.getAbsolutePath());
|
||||||
|
conf.setBoolean(YarnConfiguration.TIMELINE_SERVICE_ENABLED, true);
|
||||||
|
conf.setClass(YarnConfiguration.TIMELINE_SERVICE_STORE,
|
||||||
|
MemoryTimelineStore.class, TimelineStore.class);
|
||||||
|
conf.set(YarnConfiguration.TIMELINE_SERVICE_ADDRESS,
|
||||||
|
"localhost:10200");
|
||||||
|
conf.set(YarnConfiguration.TIMELINE_SERVICE_WEBAPP_ADDRESS,
|
||||||
|
"localhost:8188");
|
||||||
|
conf.set("hadoop.proxyuser.HTTP.hosts", "*");
|
||||||
|
conf.set("hadoop.proxyuser.HTTP.users", FOO_USER);
|
||||||
|
UserGroupInformation.setConfiguration(conf);
|
||||||
|
testTimelineServer.init(conf);
|
||||||
|
testTimelineServer.start();
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertTrue("Couldn't setup TimelineServer", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDownClass() {
|
||||||
|
if (testMiniKDC != null) {
|
||||||
|
testMiniKDC.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testTimelineServer != null) {
|
||||||
|
testTimelineServer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimelineClient client;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws Exception {
|
||||||
|
client = TimelineClient.createTimelineClient();
|
||||||
|
client.init(conf);
|
||||||
|
client.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
if (client != null) {
|
||||||
|
client.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPutTimelineEntities() throws Exception {
|
||||||
|
KerberosTestUtils.doAs(HTTP_USER + "/localhost", new Callable<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void call() throws Exception {
|
||||||
|
TimelineEntity entityToStore = new TimelineEntity();
|
||||||
|
entityToStore.setEntityType("TestTimelineAuthenticationFilter");
|
||||||
|
entityToStore.setEntityId("entity1");
|
||||||
|
entityToStore.setStartTime(0L);
|
||||||
|
TimelinePutResponse putResponse = client.putEntities(entityToStore);
|
||||||
|
Assert.assertEquals(0, putResponse.getErrors().size());
|
||||||
|
TimelineEntity entityToRead =
|
||||||
|
testTimelineServer.getTimelineStore().getEntity(
|
||||||
|
"entity1", "TestTimelineAuthenticationFilter", null);
|
||||||
|
Assert.assertNotNull(entityToRead);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetDelegationToken() throws Exception {
|
||||||
|
KerberosTestUtils.doAs(HTTP_USER + "/localhost", new Callable<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void call() throws Exception {
|
||||||
|
// Let HTTP user to get the delegation for itself
|
||||||
|
Token<TimelineDelegationTokenIdentifier> token =
|
||||||
|
client.getDelegationToken(
|
||||||
|
UserGroupInformation.getCurrentUser().getShortUserName());
|
||||||
|
Assert.assertNotNull(token);
|
||||||
|
TimelineDelegationTokenIdentifier tDT = token.decodeIdentifier();
|
||||||
|
Assert.assertNotNull(tDT);
|
||||||
|
Assert.assertEquals(new Text(HTTP_USER), tDT.getOwner());
|
||||||
|
|
||||||
|
// Let HTTP user to get the delegation token for FOO user
|
||||||
|
UserGroupInformation fooUgi = UserGroupInformation.createProxyUser(
|
||||||
|
FOO_USER, UserGroupInformation.getCurrentUser());
|
||||||
|
token = fooUgi.doAs(
|
||||||
|
new PrivilegedExceptionAction<Token<TimelineDelegationTokenIdentifier>>() {
|
||||||
|
@Override
|
||||||
|
public Token<TimelineDelegationTokenIdentifier> run()
|
||||||
|
throws Exception {
|
||||||
|
return client.getDelegationToken(
|
||||||
|
UserGroupInformation.getCurrentUser().getShortUserName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertNotNull(token);
|
||||||
|
tDT = token.decodeIdentifier();
|
||||||
|
Assert.assertNotNull(tDT);
|
||||||
|
Assert.assertEquals(new Text(FOO_USER), tDT.getOwner());
|
||||||
|
Assert.assertEquals(new Text(HTTP_USER), tDT.getRealUser());
|
||||||
|
|
||||||
|
// Let HTTP user to get the delegation token for BAR user
|
||||||
|
UserGroupInformation barUgi = UserGroupInformation.createProxyUser(
|
||||||
|
BAR_USER, UserGroupInformation.getCurrentUser());
|
||||||
|
token = barUgi.doAs(
|
||||||
|
new PrivilegedExceptionAction<Token<TimelineDelegationTokenIdentifier>>() {
|
||||||
|
@Override
|
||||||
|
public Token<TimelineDelegationTokenIdentifier> run()
|
||||||
|
throws Exception {
|
||||||
|
try {
|
||||||
|
Token<TimelineDelegationTokenIdentifier> token =
|
||||||
|
client.getDelegationToken(
|
||||||
|
UserGroupInformation.getCurrentUser().getShortUserName());
|
||||||
|
Assert.fail();
|
||||||
|
return token;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Assert.assertTrue(e instanceof AuthorizationException);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.yarn.server.timeline.security;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.http.FilterContainer;
|
||||||
|
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
|
||||||
|
public class TestTimelineAuthenticationFilterInitializer {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProxyUserConfiguration() {
|
||||||
|
FilterContainer container = Mockito.mock(FilterContainer.class);
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
Configuration conf = new YarnConfiguration();
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
// hadoop.proxyuser prefix
|
||||||
|
conf.set("hadoop.proxyuser.foo.hosts", "*");
|
||||||
|
conf.set("hadoop.proxyuser.foo.users", "*");
|
||||||
|
conf.set("hadoop.proxyuser.foo.groups", "*");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// yarn.timeline-service.http-authentication.proxyuser prefix
|
||||||
|
conf.set("yarn.timeline-service.http-authentication.proxyuser.foo.hosts",
|
||||||
|
"*");
|
||||||
|
conf.set("yarn.timeline-service.http-authentication.proxyuser.foo.users",
|
||||||
|
"*");
|
||||||
|
conf.set("yarn.timeline-service.http-authentication.proxyuser.foo.groups",
|
||||||
|
"*");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// hadoop.proxyuser prefix has been overwritten by
|
||||||
|
// yarn.timeline-service.http-authentication.proxyuser prefix
|
||||||
|
conf.set("hadoop.proxyuser.foo.hosts", "bar");
|
||||||
|
conf.set("hadoop.proxyuser.foo.users", "bar");
|
||||||
|
conf.set("hadoop.proxyuser.foo.groups", "bar");
|
||||||
|
conf.set("yarn.timeline-service.http-authentication.proxyuser.foo.hosts",
|
||||||
|
"*");
|
||||||
|
conf.set("yarn.timeline-service.http-authentication.proxyuser.foo.users",
|
||||||
|
"*");
|
||||||
|
conf.set("yarn.timeline-service.http-authentication.proxyuser.foo.groups",
|
||||||
|
"*");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimelineAuthenticationFilterInitializer initializer =
|
||||||
|
new TimelineAuthenticationFilterInitializer();
|
||||||
|
initializer.initFilter(container, conf);
|
||||||
|
Assert.assertEquals(
|
||||||
|
"*", initializer.filterConfig.get("proxyuser.foo.hosts"));
|
||||||
|
Assert.assertEquals(
|
||||||
|
"*", initializer.filterConfig.get("proxyuser.foo.users"));
|
||||||
|
Assert.assertEquals(
|
||||||
|
"*", initializer.filterConfig.get("proxyuser.foo.groups"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,6 +42,7 @@ import javax.ws.rs.core.Response.Status;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
||||||
import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler;
|
import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler;
|
||||||
|
import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticationHandler;
|
||||||
import org.apache.hadoop.yarn.api.records.timeline.TimelineEntities;
|
import org.apache.hadoop.yarn.api.records.timeline.TimelineEntities;
|
||||||
import org.apache.hadoop.yarn.api.records.timeline.TimelineEntity;
|
import org.apache.hadoop.yarn.api.records.timeline.TimelineEntity;
|
||||||
import org.apache.hadoop.yarn.api.records.timeline.TimelineEvent;
|
import org.apache.hadoop.yarn.api.records.timeline.TimelineEvent;
|
||||||
|
@ -52,6 +53,7 @@ import org.apache.hadoop.yarn.api.records.timeline.TimelinePutResponse;
|
||||||
import org.apache.hadoop.yarn.api.records.timeline.TimelinePutResponse.TimelinePutError;
|
import org.apache.hadoop.yarn.api.records.timeline.TimelinePutResponse.TimelinePutError;
|
||||||
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||||
import org.apache.hadoop.yarn.security.AdminACLsManager;
|
import org.apache.hadoop.yarn.security.AdminACLsManager;
|
||||||
|
import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenIdentifier;
|
||||||
import org.apache.hadoop.yarn.server.timeline.TestMemoryTimelineStore;
|
import org.apache.hadoop.yarn.server.timeline.TestMemoryTimelineStore;
|
||||||
import org.apache.hadoop.yarn.server.timeline.TimelineDataManager;
|
import org.apache.hadoop.yarn.server.timeline.TimelineDataManager;
|
||||||
import org.apache.hadoop.yarn.server.timeline.TimelineStore;
|
import org.apache.hadoop.yarn.server.timeline.TimelineStore;
|
||||||
|
@ -118,11 +120,15 @@ public class TestTimelineWebServices extends JerseyTest {
|
||||||
ServletContext context = mock(ServletContext.class);
|
ServletContext context = mock(ServletContext.class);
|
||||||
when(filterConfig.getServletContext()).thenReturn(context);
|
when(filterConfig.getServletContext()).thenReturn(context);
|
||||||
Enumeration<Object> names = mock(Enumeration.class);
|
Enumeration<Object> names = mock(Enumeration.class);
|
||||||
when(names.hasMoreElements()).thenReturn(true, true, false);
|
when(names.hasMoreElements()).thenReturn(true, true, true, false);
|
||||||
when(names.nextElement()).thenReturn(
|
when(names.nextElement()).thenReturn(
|
||||||
AuthenticationFilter.AUTH_TYPE,
|
AuthenticationFilter.AUTH_TYPE,
|
||||||
PseudoAuthenticationHandler.ANONYMOUS_ALLOWED);
|
PseudoAuthenticationHandler.ANONYMOUS_ALLOWED,
|
||||||
|
DelegationTokenAuthenticationHandler.TOKEN_KIND);
|
||||||
when(filterConfig.getInitParameterNames()).thenReturn(names);
|
when(filterConfig.getInitParameterNames()).thenReturn(names);
|
||||||
|
when(filterConfig.getInitParameter(
|
||||||
|
DelegationTokenAuthenticationHandler.TOKEN_KIND)).thenReturn(
|
||||||
|
TimelineDelegationTokenIdentifier.KIND_NAME.toString());
|
||||||
try {
|
try {
|
||||||
taFilter.init(filterConfig);
|
taFilter.init(filterConfig);
|
||||||
} catch (ServletException e) {
|
} catch (ServletException e) {
|
||||||
|
|
|
@ -83,7 +83,6 @@ public class TestTimelineWebServicesWithSSL {
|
||||||
if (timelineServer != null) {
|
if (timelineServer != null) {
|
||||||
timelineServer.stop();
|
timelineServer.stop();
|
||||||
}
|
}
|
||||||
AHSWebApp.resetInstance();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -90,7 +90,6 @@ public class TestSystemMetricsPublisher {
|
||||||
if (timelineServer != null) {
|
if (timelineServer != null) {
|
||||||
timelineServer.stop();
|
timelineServer.stop();
|
||||||
}
|
}
|
||||||
AHSWebApp.resetInstance();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout = 10000)
|
@Test(timeout = 10000)
|
||||||
|
|
|
@ -721,8 +721,6 @@ public class MiniYARNCluster extends CompositeService {
|
||||||
if (appHistoryServer != null) {
|
if (appHistoryServer != null) {
|
||||||
appHistoryServer.stop();
|
appHistoryServer.stop();
|
||||||
}
|
}
|
||||||
AHSWebApp.resetInstance();
|
|
||||||
super.serviceStop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue