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.fs.CommonConfigurationKeys;
26 import org.apache.hadoop.hbase.HBaseConfiguration;
27 import org.apache.hadoop.hbase.util.Methods;
28 import org.apache.hadoop.mapred.JobConf;
29 import org.apache.hadoop.mapreduce.Job;
30 import org.apache.hadoop.security.UserGroupInformation;
31
32 import java.io.IOException;
33 import java.lang.reflect.Constructor;
34 import java.lang.reflect.UndeclaredThrowableException;
35 import java.security.PrivilegedAction;
36 import java.security.PrivilegedExceptionAction;
37
38 import org.apache.commons.logging.Log;
39
40
41
42
43
44
45
46
47
48
49
50
51 public abstract class User {
52 public static final String HBASE_SECURITY_CONF_KEY =
53 "hbase.security.authentication";
54
55
56
57
58
59
60 private static boolean IS_SECURE_HADOOP = true;
61 static {
62 try {
63 UserGroupInformation.class.getMethod("isSecurityEnabled");
64 } catch (NoSuchMethodException nsme) {
65 IS_SECURE_HADOOP = false;
66 }
67 }
68 private static Log LOG = LogFactory.getLog(User.class);
69
70 protected UserGroupInformation ugi;
71
72 public UserGroupInformation getUGI() {
73 return ugi;
74 }
75
76
77
78
79
80
81 public String getName() {
82 return ugi.getUserName();
83 }
84
85
86
87
88
89
90 public String[] getGroupNames() {
91 return ugi.getGroupNames();
92 }
93
94
95
96
97
98
99 public abstract String getShortName();
100
101
102
103
104 public abstract <T> T runAs(PrivilegedAction<T> action);
105
106
107
108
109 public abstract <T> T runAs(PrivilegedExceptionAction<T> action)
110 throws IOException, InterruptedException;
111
112
113
114
115
116
117
118 public abstract void obtainAuthTokenForJob(Configuration conf, Job job)
119 throws IOException, InterruptedException;
120
121
122
123
124
125
126
127 public abstract void obtainAuthTokenForJob(JobConf job)
128 throws IOException, InterruptedException;
129
130 public String toString() {
131 return ugi.toString();
132 }
133
134
135
136
137 public static User getCurrent() throws IOException {
138 User user;
139 if (IS_SECURE_HADOOP) {
140 user = new SecureHadoopUser();
141 } else {
142 user = new HadoopUser();
143 }
144 if (user.getUGI() == null) {
145 return null;
146 }
147 return user;
148 }
149
150
151
152
153
154
155 public static User create(UserGroupInformation ugi) {
156 if (ugi == null) {
157 return null;
158 }
159
160 if (IS_SECURE_HADOOP) {
161 return new SecureHadoopUser(ugi);
162 }
163 return new HadoopUser(ugi);
164 }
165
166
167
168
169
170
171
172 public static User createUserForTesting(Configuration conf,
173 String name, String[] groups) {
174 if (IS_SECURE_HADOOP) {
175 return SecureHadoopUser.createUserForTesting(conf, name, groups);
176 }
177 return HadoopUser.createUserForTesting(conf, name, groups);
178 }
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196 public static void login(Configuration conf, String fileConfKey,
197 String principalConfKey, String localhost) throws IOException {
198 if (IS_SECURE_HADOOP) {
199 SecureHadoopUser.login(conf, fileConfKey, principalConfKey, localhost);
200 } else {
201 HadoopUser.login(conf, fileConfKey, principalConfKey, localhost);
202 }
203 }
204
205
206
207
208
209
210
211 public static boolean isSecurityEnabled() {
212 if (IS_SECURE_HADOOP) {
213 return SecureHadoopUser.isSecurityEnabled();
214 } else {
215 return HadoopUser.isSecurityEnabled();
216 }
217 }
218
219
220
221
222
223
224
225 public static boolean isHBaseSecurityEnabled(Configuration conf) {
226 return "kerberos".equalsIgnoreCase(conf.get(HBASE_SECURITY_CONF_KEY)) &&
227 "kerberos".equalsIgnoreCase(
228 conf.get(CommonConfigurationKeys.HADOOP_SECURITY_AUTHENTICATION));
229 }
230
231
232
233
234
235
236
237
238
239 private static class HadoopUser extends User {
240
241 private HadoopUser() {
242 try {
243 ugi = (UserGroupInformation) callStatic("getCurrentUGI");
244 if (ugi == null) {
245
246
247 Configuration conf = HBaseConfiguration.create();
248 ugi = (UserGroupInformation) callStatic("login",
249 new Class[]{ Configuration.class }, new Object[]{ conf });
250 if (ugi != null) {
251 callStatic("setCurrentUser",
252 new Class[]{ UserGroupInformation.class }, new Object[]{ ugi });
253 }
254 }
255 } catch (RuntimeException re) {
256 throw re;
257 } catch (Exception e) {
258 throw new UndeclaredThrowableException(e,
259 "Unexpected exception HadoopUser<init>");
260 }
261 }
262
263 private HadoopUser(UserGroupInformation ugi) {
264 this.ugi = ugi;
265 }
266
267 @Override
268 public String getShortName() {
269 return ugi != null ? ugi.getUserName() : null;
270 }
271
272 @Override
273 public <T> T runAs(PrivilegedAction<T> action) {
274 T result = null;
275 UserGroupInformation previous = null;
276 try {
277 previous = (UserGroupInformation) callStatic("getCurrentUGI");
278 try {
279 if (ugi != null) {
280 callStatic("setCurrentUser", new Class[]{UserGroupInformation.class},
281 new Object[]{ugi});
282 }
283 result = action.run();
284 } finally {
285 callStatic("setCurrentUser", new Class[]{UserGroupInformation.class},
286 new Object[]{previous});
287 }
288 } catch (RuntimeException re) {
289 throw re;
290 } catch (Exception e) {
291 throw new UndeclaredThrowableException(e,
292 "Unexpected exception in runAs()");
293 }
294 return result;
295 }
296
297 @Override
298 public <T> T runAs(PrivilegedExceptionAction<T> action)
299 throws IOException, InterruptedException {
300 T result = null;
301 try {
302 UserGroupInformation previous =
303 (UserGroupInformation) callStatic("getCurrentUGI");
304 try {
305 if (ugi != null) {
306 callStatic("setCurrentUGI", new Class[]{UserGroupInformation.class},
307 new Object[]{ugi});
308 }
309 result = action.run();
310 } finally {
311 callStatic("setCurrentUGI", new Class[]{UserGroupInformation.class},
312 new Object[]{previous});
313 }
314 } catch (Exception e) {
315 if (e instanceof IOException) {
316 throw (IOException)e;
317 } else if (e instanceof InterruptedException) {
318 throw (InterruptedException)e;
319 } else if (e instanceof RuntimeException) {
320 throw (RuntimeException)e;
321 } else {
322 throw new UndeclaredThrowableException(e, "Unknown exception in runAs()");
323 }
324 }
325 return result;
326 }
327
328 @Override
329 public void obtainAuthTokenForJob(Configuration conf, Job job)
330 throws IOException, InterruptedException {
331
332
333 }
334
335 @Override
336 public void obtainAuthTokenForJob(JobConf job)
337 throws IOException, InterruptedException {
338
339
340 }
341
342
343 public static User createUserForTesting(Configuration conf,
344 String name, String[] groups) {
345 try {
346 Class c = Class.forName("org.apache.hadoop.security.UnixUserGroupInformation");
347 Constructor constructor = c.getConstructor(String.class, String[].class);
348 if (constructor == null) {
349 throw new NullPointerException(
350 );
351 }
352 UserGroupInformation newUser =
353 (UserGroupInformation)constructor.newInstance(name, groups);
354
355 conf.set("hadoop.job.ugi", newUser.toString());
356 return new HadoopUser(newUser);
357 } catch (ClassNotFoundException cnfe) {
358 throw new RuntimeException(
359 "UnixUserGroupInformation not found, is this secure Hadoop?", cnfe);
360 } catch (NoSuchMethodException nsme) {
361 throw new RuntimeException(
362 "No valid constructor found for UnixUserGroupInformation!", nsme);
363 } catch (RuntimeException re) {
364 throw re;
365 } catch (Exception e) {
366 throw new UndeclaredThrowableException(e,
367 "Unexpected exception instantiating new UnixUserGroupInformation");
368 }
369 }
370
371
372
373
374
375
376 public static void login(Configuration conf, String fileConfKey,
377 String principalConfKey, String localhost) throws IOException {
378 LOG.info("Skipping login, not running on secure Hadoop");
379 }
380
381
382 public static boolean isSecurityEnabled() {
383 return false;
384 }
385 }
386
387
388
389
390
391
392 private static class SecureHadoopUser extends User {
393 private String shortName;
394
395 private SecureHadoopUser() throws IOException {
396 try {
397 ugi = (UserGroupInformation) callStatic("getCurrentUser");
398 } catch (IOException ioe) {
399 throw ioe;
400 } catch (RuntimeException re) {
401 throw re;
402 } catch (Exception e) {
403 throw new UndeclaredThrowableException(e,
404 "Unexpected exception getting current secure user");
405 }
406 }
407
408 private SecureHadoopUser(UserGroupInformation ugi) {
409 this.ugi = ugi;
410 }
411
412 @Override
413 public String getShortName() {
414 if (shortName != null) return shortName;
415
416 try {
417 shortName = (String)call(ugi, "getShortUserName", null, null);
418 return shortName;
419 } catch (RuntimeException re) {
420 throw re;
421 } catch (Exception e) {
422 throw new UndeclaredThrowableException(e,
423 "Unexpected error getting user short name");
424 }
425 }
426
427 @Override
428 public <T> T runAs(PrivilegedAction<T> action) {
429 try {
430 return (T) call(ugi, "doAs", new Class[]{PrivilegedAction.class},
431 new Object[]{action});
432 } catch (RuntimeException re) {
433 throw re;
434 } catch (Exception e) {
435 throw new UndeclaredThrowableException(e,
436 "Unexpected exception in runAs()");
437 }
438 }
439
440 @Override
441 public <T> T runAs(PrivilegedExceptionAction<T> action)
442 throws IOException, InterruptedException {
443 try {
444 return (T) call(ugi, "doAs",
445 new Class[]{PrivilegedExceptionAction.class},
446 new Object[]{action});
447 } catch (IOException ioe) {
448 throw ioe;
449 } catch (InterruptedException ie) {
450 throw ie;
451 } catch (RuntimeException re) {
452 throw re;
453 } catch (Exception e) {
454 throw new UndeclaredThrowableException(e,
455 "Unexpected exception in runAs(PrivilegedExceptionAction)");
456 }
457 }
458
459 @Override
460 public void obtainAuthTokenForJob(Configuration conf, Job job)
461 throws IOException, InterruptedException {
462 try {
463 Class c = Class.forName(
464 "org.apache.hadoop.hbase.security.token.TokenUtil");
465 Methods.call(c, null, "obtainTokenForJob",
466 new Class[]{Configuration.class, UserGroupInformation.class,
467 Job.class},
468 new Object[]{conf, ugi, job});
469 } catch (ClassNotFoundException cnfe) {
470 throw new RuntimeException("Failure loading TokenUtil class, "
471 +"is secure RPC available?", cnfe);
472 } catch (IOException ioe) {
473 throw ioe;
474 } catch (InterruptedException ie) {
475 throw ie;
476 } catch (RuntimeException re) {
477 throw re;
478 } catch (Exception e) {
479 throw new UndeclaredThrowableException(e,
480 "Unexpected error calling TokenUtil.obtainAndCacheToken()");
481 }
482 }
483
484 @Override
485 public void obtainAuthTokenForJob(JobConf job)
486 throws IOException, InterruptedException {
487 try {
488 Class c = Class.forName(
489 "org.apache.hadoop.hbase.security.token.TokenUtil");
490 Methods.call(c, null, "obtainTokenForJob",
491 new Class[]{JobConf.class, UserGroupInformation.class},
492 new Object[]{job, ugi});
493 } catch (ClassNotFoundException cnfe) {
494 throw new RuntimeException("Failure loading TokenUtil class, "
495 +"is secure RPC available?", cnfe);
496 } catch (IOException ioe) {
497 throw ioe;
498 } catch (InterruptedException ie) {
499 throw ie;
500 } catch (RuntimeException re) {
501 throw re;
502 } catch (Exception e) {
503 throw new UndeclaredThrowableException(e,
504 "Unexpected error calling TokenUtil.obtainAndCacheToken()");
505 }
506 }
507
508
509 public static User createUserForTesting(Configuration conf,
510 String name, String[] groups) {
511 try {
512 return new SecureHadoopUser(
513 (UserGroupInformation)callStatic("createUserForTesting",
514 new Class[]{String.class, String[].class},
515 new Object[]{name, groups})
516 );
517 } catch (RuntimeException re) {
518 throw re;
519 } catch (Exception e) {
520 throw new UndeclaredThrowableException(e,
521 "Error creating secure test user");
522 }
523 }
524
525
526
527
528
529
530
531
532
533
534
535
536
537 public static void login(Configuration conf, String fileConfKey,
538 String principalConfKey, String localhost) throws IOException {
539 if (isSecurityEnabled()) {
540
541 try {
542 Class c = Class.forName("org.apache.hadoop.security.SecurityUtil");
543 Class[] types = new Class[]{
544 Configuration.class, String.class, String.class, String.class };
545 Object[] args = new Object[]{
546 conf, fileConfKey, principalConfKey, localhost };
547 Methods.call(c, null, "login", types, args);
548 } catch (ClassNotFoundException cnfe) {
549 throw new RuntimeException("Unable to login using " +
550 "org.apache.hadoop.security.SecurityUtil.login(). SecurityUtil class " +
551 "was not found! Is this a version of secure Hadoop?", cnfe);
552 } catch (IOException ioe) {
553 throw ioe;
554 } catch (RuntimeException re) {
555 throw re;
556 } catch (Exception e) {
557 throw new UndeclaredThrowableException(e,
558 "Unhandled exception in User.login()");
559 }
560 }
561 }
562
563
564
565
566 public static boolean isSecurityEnabled() {
567 try {
568 return (Boolean)callStatic("isSecurityEnabled");
569 } catch (RuntimeException re) {
570 throw re;
571 } catch (Exception e) {
572 throw new UndeclaredThrowableException(e,
573 "Unexpected exception calling UserGroupInformation.isSecurityEnabled()");
574 }
575 }
576 }
577
578
579 private static Object callStatic(String methodName) throws Exception {
580 return call(null, methodName, null, null);
581 }
582
583 private static Object callStatic(String methodName, Class[] types,
584 Object[] args) throws Exception {
585 return call(null, methodName, types, args);
586 }
587
588 private static Object call(UserGroupInformation instance, String methodName,
589 Class[] types, Object[] args) throws Exception {
590 return Methods.call(UserGroupInformation.class, instance, methodName, types,
591 args);
592 }
593 }