org.apache.oozie.client.AuthOozieClient Maven / Gradle / Ivy
/**
* 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.oozie.client;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
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.KerberosAuthenticator;
import org.apache.hadoop.security.authentication.client.PseudoAuthenticator;
/**
* This subclass of {@link XOozieClient} supports Kerberos HTTP SPNEGO and simple authentication.
*/
public class AuthOozieClient extends XOozieClient {
/**
* Java system property to specify a custom Authenticator implementation.
*/
public static final String AUTHENTICATOR_CLASS_SYS_PROP = "authenticator.class";
/**
* Java system property that, if set the authentication token will be cached in the user home directory in a hidden
* file .oozie-auth-token
with user read/write permissions only.
*/
public static final String USE_AUTH_TOKEN_CACHE_SYS_PROP = "oozie.auth.token.cache";
/**
* File constant that defines the location of the authentication token cache file.
*
* It resolves to ${user.home}/.oozie-auth-token
.
*/
public static final File AUTH_TOKEN_CACHE_FILE = new File(System.getProperty("user.home"), ".oozie-auth-token");
public static enum AuthType {
KERBEROS, SIMPLE
}
private String authOption = null;
/**
* Create an instance of the AuthOozieClient.
*
* @param oozieUrl the Oozie URL
*/
public AuthOozieClient(String oozieUrl) {
this(oozieUrl, null);
}
/**
* Create an instance of the AuthOozieClient.
*
* @param oozieUrl the Oozie URL
* @param authOption the auth option
*/
public AuthOozieClient(String oozieUrl, String authOption) {
super(oozieUrl);
this.authOption = authOption;
}
/**
* Create an authenticated connection to the Oozie server.
*
* It uses Hadoop-auth client authentication which by default supports
* Kerberos HTTP SPNEGO, Pseudo/Simple and anonymous.
*
* if the Java system property {@link #USE_AUTH_TOKEN_CACHE_SYS_PROP} is set to true Hadoop-auth
* authentication token will be cached/used in/from the '.oozie-auth-token' file in the user
* home directory.
*
* @param url the URL to open a HTTP connection to.
* @param method the HTTP method for the HTTP connection.
* @return an authenticated connection to the Oozie server.
* @throws IOException if an IO error occurred.
* @throws OozieClientException if an oozie client error occurred.
*/
@Override
protected HttpURLConnection createConnection(URL url, String method) throws IOException, OozieClientException {
boolean useAuthFile = System.getProperty(USE_AUTH_TOKEN_CACHE_SYS_PROP, "false").equalsIgnoreCase("true");
AuthenticatedURL.Token readToken = new AuthenticatedURL.Token();
AuthenticatedURL.Token currentToken = new AuthenticatedURL.Token();
if (useAuthFile) {
readToken = readAuthToken();
if (readToken != null) {
currentToken = new AuthenticatedURL.Token(readToken.toString());
}
}
if (currentToken.isSet()) {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("OPTIONS");
AuthenticatedURL.injectToken(conn, currentToken);
if (conn.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
AUTH_TOKEN_CACHE_FILE.delete();
currentToken = new AuthenticatedURL.Token();
}
}
if (!currentToken.isSet()) {
Authenticator authenticator = getAuthenticator();
try {
new AuthenticatedURL(authenticator).openConnection(url, currentToken);
}
catch (AuthenticationException ex) {
AUTH_TOKEN_CACHE_FILE.delete();
throw new OozieClientException(OozieClientException.AUTHENTICATION,
"Could not authenticate, " + ex.getMessage(), ex);
}
}
if (useAuthFile && currentToken.isSet() && !currentToken.equals(readToken)) {
writeAuthToken(currentToken);
}
HttpURLConnection conn = super.createConnection(url, method);
AuthenticatedURL.injectToken(conn, currentToken);
return conn;
}
/**
* Read a authentication token cached in the user home directory.
*
*
* @return the authentication token cached in the user home directory, NULL if none.
*/
protected AuthenticatedURL.Token readAuthToken() {
AuthenticatedURL.Token authToken = null;
if (AUTH_TOKEN_CACHE_FILE.exists()) {
try {
BufferedReader reader = new BufferedReader(new FileReader(AUTH_TOKEN_CACHE_FILE));
String line = reader.readLine();
reader.close();
if (line != null) {
authToken = new AuthenticatedURL.Token(line);
}
}
catch (IOException ex) {
//NOP
}
}
return authToken;
}
/**
* Write the current authentication token to the user home directory.authOption
*
* The file is written with user only read/write permissions.
*
* If the file cannot be updated or the user only ready/write permissions cannot be set the file is deleted.
*
* @param authToken the authentication token to cache.
*/
protected void writeAuthToken(AuthenticatedURL.Token authToken) {
try {
Writer writer = new FileWriter(AUTH_TOKEN_CACHE_FILE);
writer.write(authToken.toString());
writer.close();
// sets read-write permissions to owner only
AUTH_TOKEN_CACHE_FILE.setReadable(false, false);
AUTH_TOKEN_CACHE_FILE.setReadable(true, true);
AUTH_TOKEN_CACHE_FILE.setWritable(true, true);
}
catch (IOException ioe) {
// if case of any error we just delete the cache, if user-only
// write permissions are not properly set a security exception
// is thrown and the file will be deleted.
AUTH_TOKEN_CACHE_FILE.delete();
}
}
/**
* Return the Hadoop-auth Authenticator to use.
*
* It first looks for value of command line option 'auth', if not set it continues to check
* {@link #AUTHENTICATOR_CLASS_SYS_PROP} Java system property for Authenticator.
*
* It the value of the {@link #AUTHENTICATOR_CLASS_SYS_PROP} is not set it uses
* Hadoop-auth KerberosAuthenticator
which supports both Kerberos HTTP SPNEGO and Pseudo/simple
* authentication.
*
* @return the Authenticator to use, NULL
if none.
*
* @throws OozieClientException thrown if the authenticator could not be instantiated.
*/
protected Authenticator getAuthenticator() throws OozieClientException {
if (authOption != null) {
try {
Class extends Authenticator> authClass = getAuthenticators().get(authOption.toUpperCase());
if (authClass == null) {
throw new OozieClientException(OozieClientException.AUTHENTICATION,
"Authenticator class not found [" + authClass + "]");
}
return authClass.newInstance();
}
catch (IllegalArgumentException iae) {
throw new OozieClientException(OozieClientException.AUTHENTICATION, "Invalid options provided for auth: " + authOption
+ ", (" + AuthType.KERBEROS + " or " + AuthType.SIMPLE + " expected.)");
}
catch (InstantiationException ex) {
throw new OozieClientException(OozieClientException.AUTHENTICATION,
"Could not instantiate Authenticator for option [" + authOption + "], " +
ex.getMessage(), ex);
}
catch (IllegalAccessException ex) {
throw new OozieClientException(OozieClientException.AUTHENTICATION,
"Could not instantiate Authenticator for option [" + authOption + "], " +
ex.getMessage(), ex);
}
}
String className = System.getProperty(AUTHENTICATOR_CLASS_SYS_PROP, KerberosAuthenticator.class.getName());
if (className != null) {
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class extends Object> klass = (cl != null) ? cl.loadClass(className) :
getClass().getClassLoader().loadClass(className);
if (klass == null) {
throw new OozieClientException(OozieClientException.AUTHENTICATION,
"Authenticator class not found [" + className + "]");
}
return (Authenticator) klass.newInstance();
}
catch (Exception ex) {
throw new OozieClientException(OozieClientException.AUTHENTICATION,
"Could not instantiate Authenticator [" + className + "], " +
ex.getMessage(), ex);
}
}
else {
throw new OozieClientException(OozieClientException.AUTHENTICATION,
"Authenticator class not found [" + className + "]");
}
}
/**
* Get the map for classes of Authenticator.
* Default values are:
* null -> KerberosAuthenticator
* SIMPLE -> PseudoAuthenticator
* KERBEROS -> KerberosAuthenticator
*
* @return the map for classes of Authenticator
* @throws OozieClientException
*/
protected Map> getAuthenticators() {
Map> authClasses = new HashMap>();
authClasses.put(AuthType.KERBEROS.toString(), KerberosAuthenticator.class);
authClasses.put(AuthType.SIMPLE.toString(), PseudoAuthenticator.class);
authClasses.put(null, KerberosAuthenticator.class);
return authClasses;
}
/**
* Get authOption
*
* @return the authOption
*/
public String getAuthOption() {
return authOption;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy