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