1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.security;
22
23 import org.apache.commons.logging.LogFactory;
24 import org.apache.hadoop.conf.Configuration;
25 import org.apache.hadoop.security.UserGroupInformation;
26
27 import java.io.IOException;
28 import java.lang.reflect.Constructor;
29 import java.lang.reflect.InvocationTargetException;
30 import java.lang.reflect.Method;
31 import java.lang.reflect.UndeclaredThrowableException;
32 import java.security.PrivilegedAction;
33 import java.security.PrivilegedExceptionAction;
34 import org.apache.commons.logging.Log;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 public abstract class User {
55 private static boolean IS_SECURE_HADOOP = true;
56 static {
57 try {
58 UserGroupInformation.class.getMethod("isSecurityEnabled");
59 } catch (NoSuchMethodException nsme) {
60 IS_SECURE_HADOOP = false;
61 }
62 }
63 private static Log LOG = LogFactory.getLog(User.class);
64 protected UserGroupInformation ugi;
65
66
67
68
69
70
71 public String getName() {
72 return ugi.getUserName();
73 }
74
75
76
77
78
79
80 public abstract String getShortName();
81
82
83
84
85 public abstract <T> T runAs(PrivilegedAction<T> action);
86
87
88
89
90 public abstract <T> T runAs(PrivilegedExceptionAction<T> action)
91 throws IOException, InterruptedException;
92
93 public String toString() {
94 return ugi.toString();
95 }
96
97
98
99
100 public static User getCurrent() throws IOException {
101 if (IS_SECURE_HADOOP) {
102 return new SecureHadoopUser();
103 } else {
104 return new HadoopUser();
105 }
106 }
107
108
109
110
111
112
113
114 public static User createUserForTesting(Configuration conf,
115 String name, String[] groups) {
116 if (IS_SECURE_HADOOP) {
117 return SecureHadoopUser.createUserForTesting(conf, name, groups);
118 }
119 return HadoopUser.createUserForTesting(conf, name, groups);
120 }
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 public static void login(Configuration conf, String fileConfKey,
139 String principalConfKey, String localhost) throws IOException {
140 if (IS_SECURE_HADOOP) {
141 SecureHadoopUser.login(conf, fileConfKey, principalConfKey, localhost);
142 } else {
143 HadoopUser.login(conf, fileConfKey, principalConfKey, localhost);
144 }
145 }
146
147
148
149
150
151
152
153
154
155 private static class HadoopUser extends User {
156
157 private HadoopUser() {
158 try {
159 ugi = (UserGroupInformation) callStatic("getCurrentUGI");
160 } catch (RuntimeException re) {
161 throw re;
162 } catch (Exception e) {
163 throw new UndeclaredThrowableException(e,
164 "Unexpected exception HadoopUser<init>");
165 }
166 }
167
168 private HadoopUser(UserGroupInformation ugi) {
169 this.ugi = ugi;
170 }
171
172 @Override
173 public String getShortName() {
174 return ugi.getUserName();
175 }
176
177 @Override
178 public <T> T runAs(PrivilegedAction<T> action) {
179 T result = null;
180 UserGroupInformation previous = null;
181 try {
182 previous = (UserGroupInformation) callStatic("getCurrentUGI");
183 try {
184 if (ugi != null) {
185 callStatic("setCurrentUser", new Class[]{UserGroupInformation.class},
186 new Object[]{ugi});
187 }
188 result = action.run();
189 } finally {
190 callStatic("setCurrentUser", new Class[]{UserGroupInformation.class},
191 new Object[]{previous});
192 }
193 } catch (RuntimeException re) {
194 throw re;
195 } catch (Exception e) {
196 throw new UndeclaredThrowableException(e,
197 "Unexpected exception in runAs()");
198 }
199 return result;
200 }
201
202 @Override
203 public <T> T runAs(PrivilegedExceptionAction<T> action)
204 throws IOException, InterruptedException {
205 T result = null;
206 try {
207 UserGroupInformation previous =
208 (UserGroupInformation) callStatic("getCurrentUGI");
209 try {
210 if (ugi != null) {
211 callStatic("setCurrentUGI", new Class[]{UserGroupInformation.class},
212 new Object[]{ugi});
213 }
214 result = action.run();
215 } finally {
216 callStatic("setCurrentUGI", new Class[]{UserGroupInformation.class},
217 new Object[]{previous});
218 }
219 } catch (Exception e) {
220 if (e instanceof IOException) {
221 throw (IOException)e;
222 } else if (e instanceof InterruptedException) {
223 throw (InterruptedException)e;
224 } else if (e instanceof RuntimeException) {
225 throw (RuntimeException)e;
226 } else {
227 throw new UndeclaredThrowableException(e, "Unknown exception in runAs()");
228 }
229 }
230 return result;
231 }
232
233 public static User createUserForTesting(Configuration conf,
234 String name, String[] groups) {
235 try {
236 Class c = Class.forName("org.apache.hadoop.security.UnixUserGroupInformation");
237 Constructor constructor = c.getConstructor(String.class, String[].class);
238 if (constructor == null) {
239 throw new NullPointerException(
240 );
241 }
242 UserGroupInformation newUser =
243 (UserGroupInformation)constructor.newInstance(name, groups);
244
245 conf.set("hadoop.job.ugi", newUser.toString());
246 return new HadoopUser(newUser);
247 } catch (ClassNotFoundException cnfe) {
248 throw new RuntimeException(
249 "UnixUserGroupInformation not found, is this secure Hadoop?", cnfe);
250 } catch (NoSuchMethodException nsme) {
251 throw new RuntimeException(
252 "No valid constructor found for UnixUserGroupInformation!", nsme);
253 } catch (RuntimeException re) {
254 throw re;
255 } catch (Exception e) {
256 throw new UndeclaredThrowableException(e,
257 "Unexpected exception instantiating new UnixUserGroupInformation");
258 }
259 }
260
261 public static void login(Configuration conf, String fileConfKey,
262 String principalConfKey, String localhost) throws IOException {
263 LOG.info("Skipping login, not running on secure Hadoop");
264 }
265 }
266
267
268
269
270
271
272 private static class SecureHadoopUser extends User {
273 private SecureHadoopUser() throws IOException {
274 try {
275 ugi = (UserGroupInformation) callStatic("getCurrentUser");
276 } catch (IOException ioe) {
277 throw ioe;
278 } catch (RuntimeException re) {
279 throw re;
280 } catch (Exception e) {
281 throw new UndeclaredThrowableException(e,
282 "Unexpected exception getting current secure user");
283 }
284 }
285
286 private SecureHadoopUser(UserGroupInformation ugi) {
287 this.ugi = ugi;
288 }
289
290 @Override
291 public String getShortName() {
292 try {
293 return (String)call(ugi, "getShortUserName", null, null);
294 } catch (RuntimeException re) {
295 throw re;
296 } catch (Exception e) {
297 throw new UndeclaredThrowableException(e,
298 "Unexpected error getting user short name");
299 }
300 }
301
302 @Override
303 public <T> T runAs(PrivilegedAction<T> action) {
304 try {
305 return (T) call(ugi, "doAs", new Class[]{PrivilegedAction.class},
306 new Object[]{action});
307 } catch (RuntimeException re) {
308 throw re;
309 } catch (Exception e) {
310 throw new UndeclaredThrowableException(e,
311 "Unexpected exception in runAs()");
312 }
313 }
314
315 @Override
316 public <T> T runAs(PrivilegedExceptionAction<T> action)
317 throws IOException, InterruptedException {
318 try {
319 return (T) call(ugi, "doAs",
320 new Class[]{PrivilegedExceptionAction.class},
321 new Object[]{action});
322 } catch (IOException ioe) {
323 throw ioe;
324 } catch (InterruptedException ie) {
325 throw ie;
326 } catch (RuntimeException re) {
327 throw re;
328 } catch (Exception e) {
329 throw new UndeclaredThrowableException(e,
330 "Unexpected exception in runAs(PrivilegedExceptionAction)");
331 }
332 }
333
334 public static User createUserForTesting(Configuration conf,
335 String name, String[] groups) {
336 try {
337 return new SecureHadoopUser(
338 (UserGroupInformation)callStatic("createUserForTesting",
339 new Class[]{String.class, String[].class},
340 new Object[]{name, groups})
341 );
342 } catch (RuntimeException re) {
343 throw re;
344 } catch (Exception e) {
345 throw new UndeclaredThrowableException(e,
346 "Error creating secure test user");
347 }
348 }
349
350 public static void login(Configuration conf, String fileConfKey,
351 String principalConfKey, String localhost) throws IOException {
352
353 try {
354 Class c = Class.forName("org.apache.hadoop.security.SecurityUtil");
355 Class[] types = new Class[]{
356 Configuration.class, String.class, String.class, String.class };
357 Object[] args = new Object[]{
358 conf, fileConfKey, principalConfKey, localhost };
359 call(c, null, "login", types, args);
360 } catch (ClassNotFoundException cnfe) {
361 throw new RuntimeException("Unable to login using " +
362 "org.apache.hadoop.security.Security.login(). SecurityUtil class " +
363 "was not found! Is this a version of secure Hadoop?", cnfe);
364 } catch (IOException ioe) {
365 throw ioe;
366 } catch (RuntimeException re) {
367 throw re;
368 } catch (Exception e) {
369 throw new UndeclaredThrowableException(e,
370 "Unhandled exception in User.login()");
371 }
372 }
373 }
374
375
376 private static Object callStatic(String methodName) throws Exception {
377 return call(null, methodName, null, null);
378 }
379
380 private static Object callStatic(String methodName, Class[] types,
381 Object[] args) throws Exception {
382 return call(null, methodName, types, args);
383 }
384
385 private static Object call(UserGroupInformation instance, String methodName,
386 Class[] types, Object[] args) throws Exception {
387 return call(UserGroupInformation.class, instance, methodName, types, args);
388 }
389
390 private static <T> Object call(Class<T> clazz, T instance, String methodName,
391 Class[] types, Object[] args) throws Exception {
392 try {
393 Method m = clazz.getMethod(methodName, types);
394 return m.invoke(instance, args);
395 } catch (IllegalArgumentException arge) {
396 LOG.fatal("Constructed invalid call. class="+clazz.getName()+
397 " method=" + methodName + " types=" + stringify(types), arge);
398 throw arge;
399 } catch (NoSuchMethodException nsme) {
400 throw new IllegalArgumentException(
401 "Can't find method "+methodName+" in "+clazz.getName()+"!", nsme);
402 } catch (InvocationTargetException ite) {
403
404 if (ite.getTargetException() != null) {
405 if (ite.getTargetException() instanceof Exception) {
406 throw (Exception)ite.getTargetException();
407 } else if (ite.getTargetException() instanceof Error) {
408 throw (Error)ite.getTargetException();
409 }
410 }
411 throw new UndeclaredThrowableException(ite,
412 "Unknown exception invoking "+clazz.getName()+"."+methodName+"()");
413 } catch (IllegalAccessException iae) {
414 throw new IllegalArgumentException(
415 "Denied access calling "+clazz.getName()+"."+methodName+"()", iae);
416 } catch (SecurityException se) {
417 LOG.fatal("SecurityException calling method. class="+clazz.getName()+
418 " method=" + methodName + " types=" + stringify(types), se);
419 throw se;
420 }
421 }
422
423 private static String stringify(Class[] classes) {
424 StringBuilder buf = new StringBuilder();
425 if (classes != null) {
426 for (Class c : classes) {
427 if (buf.length() > 0) {
428 buf.append(",");
429 }
430 buf.append(c.getName());
431 }
432 } else {
433 buf.append("NULL");
434 }
435 return buf.toString();
436 }
437 }