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