This project has retired. For details please refer to its
Attic page.
001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.oozie.client;
019
020 import java.io.BufferedReader;
021 import java.io.File;
022 import java.io.FileReader;
023 import java.io.FileWriter;
024 import java.io.IOException;
025 import java.io.Writer;
026 import java.net.HttpURLConnection;
027 import java.net.URL;
028 import java.util.HashMap;
029 import java.util.Map;
030
031 import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
032 import org.apache.hadoop.security.authentication.client.AuthenticationException;
033 import org.apache.hadoop.security.authentication.client.Authenticator;
034 import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
035 import org.apache.hadoop.security.authentication.client.PseudoAuthenticator;
036
037 /**
038 * This subclass of {@link XOozieClient} supports Kerberos HTTP SPNEGO and simple authentication.
039 */
040 public class AuthOozieClient extends XOozieClient {
041
042 /**
043 * Java system property to specify a custom Authenticator implementation.
044 */
045 public static final String AUTHENTICATOR_CLASS_SYS_PROP = "authenticator.class";
046
047 /**
048 * Java system property that, if set the authentication token will be cached in the user home directory in a hidden
049 * file <code>.oozie-auth-token</code> with user read/write permissions only.
050 */
051 public static final String USE_AUTH_TOKEN_CACHE_SYS_PROP = "oozie.auth.token.cache";
052
053 /**
054 * File constant that defines the location of the authentication token cache file.
055 * <p/>
056 * It resolves to <code>${user.home}/.oozie-auth-token</code>.
057 */
058 public static final File AUTH_TOKEN_CACHE_FILE = new File(System.getProperty("user.home"), ".oozie-auth-token");
059
060 public static enum AuthType {
061 KERBEROS, SIMPLE
062 }
063
064 private String authOption = null;
065
066 /**
067 * Create an instance of the AuthOozieClient.
068 *
069 * @param oozieUrl the Oozie URL
070 */
071 public AuthOozieClient(String oozieUrl) {
072 this(oozieUrl, null);
073 }
074
075 /**
076 * Create an instance of the AuthOozieClient.
077 *
078 * @param oozieUrl the Oozie URL
079 * @param authOption the auth option
080 */
081 public AuthOozieClient(String oozieUrl, String authOption) {
082 super(oozieUrl);
083 this.authOption = authOption;
084 }
085
086 /**
087 * Create an authenticated connection to the Oozie server.
088 * <p/>
089 * It uses Hadoop-auth client authentication which by default supports
090 * Kerberos HTTP SPNEGO, Pseudo/Simple and anonymous.
091 * <p/>
092 * if the Java system property {@link #USE_AUTH_TOKEN_CACHE_SYS_PROP} is set to true Hadoop-auth
093 * authentication token will be cached/used in/from the '.oozie-auth-token' file in the user
094 * home directory.
095 *
096 * @param url the URL to open a HTTP connection to.
097 * @param method the HTTP method for the HTTP connection.
098 * @return an authenticated connection to the Oozie server.
099 * @throws IOException if an IO error occurred.
100 * @throws OozieClientException if an oozie client error occurred.
101 */
102 @Override
103 protected HttpURLConnection createConnection(URL url, String method) throws IOException, OozieClientException {
104 boolean useAuthFile = System.getProperty(USE_AUTH_TOKEN_CACHE_SYS_PROP, "false").equalsIgnoreCase("true");
105 AuthenticatedURL.Token readToken = new AuthenticatedURL.Token();
106 AuthenticatedURL.Token currentToken = new AuthenticatedURL.Token();
107
108 if (useAuthFile) {
109 readToken = readAuthToken();
110 if (readToken != null) {
111 currentToken = new AuthenticatedURL.Token(readToken.toString());
112 }
113 }
114
115 if (currentToken.isSet()) {
116 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
117 conn.setRequestMethod("OPTIONS");
118 AuthenticatedURL.injectToken(conn, currentToken);
119 if (conn.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
120 AUTH_TOKEN_CACHE_FILE.delete();
121 currentToken = new AuthenticatedURL.Token();
122 }
123 }
124
125 if (!currentToken.isSet()) {
126 Authenticator authenticator = getAuthenticator();
127 try {
128 new AuthenticatedURL(authenticator).openConnection(url, currentToken);
129 }
130 catch (AuthenticationException ex) {
131 AUTH_TOKEN_CACHE_FILE.delete();
132 throw new OozieClientException(OozieClientException.AUTHENTICATION,
133 "Could not authenticate, " + ex.getMessage(), ex);
134 }
135 }
136 if (useAuthFile && !currentToken.equals(readToken)) {
137 writeAuthToken(currentToken);
138 }
139 HttpURLConnection conn = super.createConnection(url, method);
140 AuthenticatedURL.injectToken(conn, currentToken);
141
142 return conn;
143 }
144
145
146 /**
147 * Read a authentication token cached in the user home directory.
148 * <p/>
149 *
150 * @return the authentication token cached in the user home directory, NULL if none.
151 */
152 protected AuthenticatedURL.Token readAuthToken() {
153 AuthenticatedURL.Token authToken = null;
154 if (AUTH_TOKEN_CACHE_FILE.exists()) {
155 try {
156 BufferedReader reader = new BufferedReader(new FileReader(AUTH_TOKEN_CACHE_FILE));
157 String line = reader.readLine();
158 reader.close();
159 if (line != null) {
160 authToken = new AuthenticatedURL.Token(line);
161 }
162 }
163 catch (IOException ex) {
164 //NOP
165 }
166 }
167 return authToken;
168 }
169
170 /**
171 * Write the current authentication token to the user home directory.authOption
172 * <p/>
173 * The file is written with user only read/write permissions.
174 * <p/>
175 * If the file cannot be updated or the user only ready/write permissions cannot be set the file is deleted.
176 *
177 * @param authToken the authentication token to cache.
178 */
179 protected void writeAuthToken(AuthenticatedURL.Token authToken) {
180 try {
181 Writer writer = new FileWriter(AUTH_TOKEN_CACHE_FILE);
182 writer.write(authToken.toString());
183 writer.close();
184 // sets read-write permissions to owner only
185 AUTH_TOKEN_CACHE_FILE.setReadable(false, false);
186 AUTH_TOKEN_CACHE_FILE.setReadable(true, true);
187 AUTH_TOKEN_CACHE_FILE.setWritable(true, true);
188 }
189 catch (IOException ioe) {
190 // if case of any error we just delete the cache, if user-only
191 // write permissions are not properly set a security exception
192 // is thrown and the file will be deleted.
193 AUTH_TOKEN_CACHE_FILE.delete();
194 }
195 }
196
197 /**
198 * Return the Hadoop-auth Authenticator to use.
199 * <p/>
200 * It first looks for value of command line option 'auth', if not set it continues to check
201 * {@link #AUTHENTICATOR_CLASS_SYS_PROP} Java system property for Authenticator.
202 * <p/>
203 * It the value of the {@link #AUTHENTICATOR_CLASS_SYS_PROP} is not set it uses
204 * Hadoop-auth <code>KerberosAuthenticator</code> which supports both Kerberos HTTP SPNEGO and Pseudo/simple
205 * authentication.
206 *
207 * @return the Authenticator to use, <code>NULL</code> if none.
208 *
209 * @throws OozieClientException thrown if the authenticator could not be instantiated.
210 */
211 protected Authenticator getAuthenticator() throws OozieClientException {
212 if (authOption != null) {
213 try {
214 Class<? extends Authenticator> authClass = getAuthenticators().get(authOption.toUpperCase());
215 if (authClass == null) {
216 throw new OozieClientException(OozieClientException.AUTHENTICATION,
217 "Authenticator class not found [" + authClass + "]");
218 }
219 return authClass.newInstance();
220 }
221 catch (IllegalArgumentException iae) {
222 throw new OozieClientException(OozieClientException.AUTHENTICATION, "Invalid options provided for auth: " + authOption
223 + ", (" + AuthType.KERBEROS + " or " + AuthType.SIMPLE + " expected.)");
224 }
225 catch (InstantiationException ex) {
226 throw new OozieClientException(OozieClientException.AUTHENTICATION,
227 "Could not instantiate Authenticator for option [" + authOption + "], " +
228 ex.getMessage(), ex);
229 }
230 catch (IllegalAccessException ex) {
231 throw new OozieClientException(OozieClientException.AUTHENTICATION,
232 "Could not instantiate Authenticator for option [" + authOption + "], " +
233 ex.getMessage(), ex);
234 }
235
236 }
237
238 String className = System.getProperty(AUTHENTICATOR_CLASS_SYS_PROP, KerberosAuthenticator.class.getName());
239 if (className != null) {
240 try {
241 ClassLoader cl = Thread.currentThread().getContextClassLoader();
242 Class<? extends Object> klass = (cl != null) ? cl.loadClass(className) :
243 getClass().getClassLoader().loadClass(className);
244 if (klass == null) {
245 throw new OozieClientException(OozieClientException.AUTHENTICATION,
246 "Authenticator class not found [" + className + "]");
247 }
248 return (Authenticator) klass.newInstance();
249 }
250 catch (Exception ex) {
251 throw new OozieClientException(OozieClientException.AUTHENTICATION,
252 "Could not instantiate Authenticator [" + className + "], " +
253 ex.getMessage(), ex);
254 }
255 }
256 else {
257 throw new OozieClientException(OozieClientException.AUTHENTICATION,
258 "Authenticator class not found [" + className + "]");
259 }
260 }
261
262 /**
263 * Get the map for classes of Authenticator.
264 * Default values are:
265 * null -> KerberosAuthenticator
266 * SIMPLE -> PseudoAuthenticator
267 * KERBEROS -> KerberosAuthenticator
268 *
269 * @return the map for classes of Authenticator
270 * @throws OozieClientException
271 */
272 protected Map<String, Class<? extends Authenticator>> getAuthenticators() {
273 Map<String, Class<? extends Authenticator>> authClasses = new HashMap<String, Class<? extends Authenticator>>();
274 authClasses.put(AuthType.KERBEROS.toString(), KerberosAuthenticator.class);
275 authClasses.put(AuthType.SIMPLE.toString(), PseudoAuthenticator.class);
276 authClasses.put(null, KerberosAuthenticator.class);
277 return authClasses;
278 }
279
280 /**
281 * Get authOption
282 *
283 * @return the authOption
284 */
285 public String getAuthOption() {
286 return authOption;
287 }
288
289 }