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