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