1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.security.access;
20
21 import java.io.IOException;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.concurrent.ConcurrentSkipListMap;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.hbase.AuthUtil;
30 import org.apache.hadoop.hbase.classification.InterfaceAudience;
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.hbase.Cell;
33 import org.apache.hadoop.hbase.TableName;
34 import org.apache.hadoop.hbase.exceptions.DeserializationException;
35 import org.apache.hadoop.hbase.security.Superusers;
36 import org.apache.hadoop.hbase.security.User;
37 import org.apache.hadoop.hbase.security.UserProvider;
38 import org.apache.hadoop.hbase.util.Bytes;
39 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
40 import org.apache.zookeeper.KeeperException;
41
42 import com.google.common.collect.ArrayListMultimap;
43 import com.google.common.collect.ListMultimap;
44 import com.google.common.collect.Lists;
45
46
47
48
49 @InterfaceAudience.Private
50 public class TableAuthManager {
51 private static class PermissionCache<T extends Permission> {
52
53 private ListMultimap<String,T> userCache = ArrayListMultimap.create();
54
55 private ListMultimap<String,T> groupCache = ArrayListMultimap.create();
56
57 public List<T> getUser(String user) {
58 return userCache.get(user);
59 }
60
61 public void putUser(String user, T perm) {
62 userCache.put(user, perm);
63 }
64
65 public List<T> replaceUser(String user, Iterable<? extends T> perms) {
66 return userCache.replaceValues(user, perms);
67 }
68
69 public List<T> getGroup(String group) {
70 return groupCache.get(group);
71 }
72
73 public void putGroup(String group, T perm) {
74 groupCache.put(group, perm);
75 }
76
77 public List<T> replaceGroup(String group, Iterable<? extends T> perms) {
78 return groupCache.replaceValues(group, perms);
79 }
80
81
82
83
84
85 public ListMultimap<String,T> getAllPermissions() {
86 ListMultimap<String,T> tmp = ArrayListMultimap.create();
87 tmp.putAll(userCache);
88 for (String group : groupCache.keySet()) {
89 tmp.putAll(AuthUtil.toGroupEntry(group), groupCache.get(group));
90 }
91 return tmp;
92 }
93 }
94
95 private static Log LOG = LogFactory.getLog(TableAuthManager.class);
96
97 private static TableAuthManager instance;
98
99
100 private volatile PermissionCache<Permission> globalCache;
101
102 private ConcurrentSkipListMap<TableName, PermissionCache<TablePermission>> tableCache =
103 new ConcurrentSkipListMap<TableName, PermissionCache<TablePermission>>();
104
105 private ConcurrentSkipListMap<String, PermissionCache<TablePermission>> nsCache =
106 new ConcurrentSkipListMap<String, PermissionCache<TablePermission>>();
107
108 private Configuration conf;
109 private ZKPermissionWatcher zkperms;
110 private volatile long mtime;
111
112 private TableAuthManager(ZooKeeperWatcher watcher, Configuration conf)
113 throws IOException {
114 this.conf = conf;
115
116
117 globalCache = initGlobal(conf);
118
119 this.zkperms = new ZKPermissionWatcher(watcher, this, conf);
120 try {
121 this.zkperms.start();
122 } catch (KeeperException ke) {
123 LOG.error("ZooKeeper initialization failed", ke);
124 }
125 }
126
127
128
129
130
131 private PermissionCache<Permission> initGlobal(Configuration conf) throws IOException {
132 UserProvider userProvider = UserProvider.instantiate(conf);
133 User user = userProvider.getCurrent();
134 if (user == null) {
135 throw new IOException("Unable to obtain the current user, " +
136 "authorization checks for internal operations will not work correctly!");
137 }
138 PermissionCache<Permission> newCache = new PermissionCache<Permission>();
139 String currentUser = user.getShortName();
140
141
142 List<String> superusers = Lists.asList(currentUser, conf.getStrings(
143 Superusers.SUPERUSER_CONF_KEY, new String[0]));
144 if (superusers != null) {
145 for (String name : superusers) {
146 if (AuthUtil.isGroupPrincipal(name)) {
147 newCache.putGroup(AuthUtil.getGroupName(name),
148 new Permission(Permission.Action.values()));
149 } else {
150 newCache.putUser(name, new Permission(Permission.Action.values()));
151 }
152 }
153 }
154 return newCache;
155 }
156
157 public ZKPermissionWatcher getZKPermissionWatcher() {
158 return this.zkperms;
159 }
160
161 public void refreshTableCacheFromWritable(TableName table,
162 byte[] data) throws IOException {
163 if (data != null && data.length > 0) {
164 ListMultimap<String,TablePermission> perms;
165 try {
166 perms = AccessControlLists.readPermissions(data, conf);
167 } catch (DeserializationException e) {
168 throw new IOException(e);
169 }
170
171 if (perms != null) {
172 if (Bytes.equals(table.getName(), AccessControlLists.ACL_GLOBAL_NAME)) {
173 updateGlobalCache(perms);
174 } else {
175 updateTableCache(table, perms);
176 }
177 }
178 } else {
179 LOG.debug("Skipping permission cache refresh because writable data is empty");
180 }
181 }
182
183 public void refreshNamespaceCacheFromWritable(String namespace, byte[] data) throws IOException {
184 if (data != null && data.length > 0) {
185 ListMultimap<String,TablePermission> perms;
186 try {
187 perms = AccessControlLists.readPermissions(data, conf);
188 } catch (DeserializationException e) {
189 throw new IOException(e);
190 }
191 if (perms != null) {
192 updateNsCache(namespace, perms);
193 }
194 } else {
195 LOG.debug("Skipping permission cache refresh because writable data is empty");
196 }
197 }
198
199
200
201
202
203
204 private void updateGlobalCache(ListMultimap<String,TablePermission> userPerms) {
205 PermissionCache<Permission> newCache = null;
206 try {
207 newCache = initGlobal(conf);
208 for (Map.Entry<String,TablePermission> entry : userPerms.entries()) {
209 if (AuthUtil.isGroupPrincipal(entry.getKey())) {
210 newCache.putGroup(AuthUtil.getGroupName(entry.getKey()),
211 new Permission(entry.getValue().getActions()));
212 } else {
213 newCache.putUser(entry.getKey(), new Permission(entry.getValue().getActions()));
214 }
215 }
216 globalCache = newCache;
217 mtime++;
218 } catch (IOException e) {
219
220 LOG.error("Error occured while updating the global cache", e);
221 }
222 }
223
224
225
226
227
228
229
230
231
232 private void updateTableCache(TableName table,
233 ListMultimap<String,TablePermission> tablePerms) {
234 PermissionCache<TablePermission> newTablePerms = new PermissionCache<TablePermission>();
235
236 for (Map.Entry<String,TablePermission> entry : tablePerms.entries()) {
237 if (AuthUtil.isGroupPrincipal(entry.getKey())) {
238 newTablePerms.putGroup(AuthUtil.getGroupName(entry.getKey()), entry.getValue());
239 } else {
240 newTablePerms.putUser(entry.getKey(), entry.getValue());
241 }
242 }
243
244 tableCache.put(table, newTablePerms);
245 mtime++;
246 }
247
248
249
250
251
252
253
254
255
256 private void updateNsCache(String namespace,
257 ListMultimap<String, TablePermission> tablePerms) {
258 PermissionCache<TablePermission> newTablePerms = new PermissionCache<TablePermission>();
259
260 for (Map.Entry<String, TablePermission> entry : tablePerms.entries()) {
261 if (AuthUtil.isGroupPrincipal(entry.getKey())) {
262 newTablePerms.putGroup(AuthUtil.getGroupName(entry.getKey()), entry.getValue());
263 } else {
264 newTablePerms.putUser(entry.getKey(), entry.getValue());
265 }
266 }
267
268 nsCache.put(namespace, newTablePerms);
269 mtime++;
270 }
271
272 private PermissionCache<TablePermission> getTablePermissions(TableName table) {
273 if (!tableCache.containsKey(table)) {
274 tableCache.putIfAbsent(table, new PermissionCache<TablePermission>());
275 }
276 return tableCache.get(table);
277 }
278
279 private PermissionCache<TablePermission> getNamespacePermissions(String namespace) {
280 if (!nsCache.containsKey(namespace)) {
281 nsCache.putIfAbsent(namespace, new PermissionCache<TablePermission>());
282 }
283 return nsCache.get(namespace);
284 }
285
286
287
288
289
290
291
292 private boolean authorize(List<Permission> perms, Permission.Action action) {
293 if (perms != null) {
294 for (Permission p : perms) {
295 if (p.implies(action)) {
296 return true;
297 }
298 }
299 } else if (LOG.isDebugEnabled()) {
300 LOG.debug("No permissions found for " + action);
301 }
302
303 return false;
304 }
305
306
307
308
309
310
311
312
313 public boolean authorize(User user, Permission.Action action) {
314 if (user == null) {
315 return false;
316 }
317
318 if (authorize(globalCache.getUser(user.getShortName()), action)) {
319 return true;
320 }
321
322 String[] groups = user.getGroupNames();
323 if (groups != null) {
324 for (String group : groups) {
325 if (authorize(globalCache.getGroup(group), action)) {
326 return true;
327 }
328 }
329 }
330 return false;
331 }
332
333 private boolean authorize(List<TablePermission> perms,
334 TableName table, byte[] family,
335 Permission.Action action) {
336 return authorize(perms, table, family, null, action);
337 }
338
339 private boolean authorize(List<TablePermission> perms,
340 TableName table, byte[] family,
341 byte[] qualifier, Permission.Action action) {
342 if (perms != null) {
343 for (TablePermission p : perms) {
344 if (p.implies(table, family, qualifier, action)) {
345 return true;
346 }
347 }
348 } else if (LOG.isDebugEnabled()) {
349 LOG.debug("No permissions found for table="+table);
350 }
351 return false;
352 }
353
354 private boolean hasAccess(List<TablePermission> perms,
355 TableName table, Permission.Action action) {
356 if (perms != null) {
357 for (TablePermission p : perms) {
358 if (p.implies(action)) {
359 return true;
360 }
361 }
362 } else if (LOG.isDebugEnabled()) {
363 LOG.debug("No permissions found for table="+table);
364 }
365 return false;
366 }
367
368
369
370
371 public boolean authorize(User user, TableName table, Cell cell, Permission.Action action) {
372 try {
373 List<Permission> perms = AccessControlLists.getCellPermissionsForUser(user, cell);
374 if (LOG.isTraceEnabled()) {
375 LOG.trace("Perms for user " + user.getShortName() + " in cell " + cell + ": " +
376 (perms != null ? perms : ""));
377 }
378 if (perms != null) {
379 for (Permission p: perms) {
380 if (p.implies(action)) {
381 return true;
382 }
383 }
384 }
385 } catch (IOException e) {
386
387 LOG.error("Failed parse of ACL tag in cell " + cell);
388
389
390 }
391 return false;
392 }
393
394 public boolean authorize(User user, String namespace, Permission.Action action) {
395
396 if (authorize(user, action)) {
397 return true;
398 }
399
400 PermissionCache<TablePermission> tablePerms = nsCache.get(namespace);
401 if (tablePerms != null) {
402 List<TablePermission> userPerms = tablePerms.getUser(user.getShortName());
403 if (authorize(userPerms, namespace, action)) {
404 return true;
405 }
406 String[] groupNames = user.getGroupNames();
407 if (groupNames != null) {
408 for (String group : groupNames) {
409 List<TablePermission> groupPerms = tablePerms.getGroup(group);
410 if (authorize(groupPerms, namespace, action)) {
411 return true;
412 }
413 }
414 }
415 }
416 return false;
417 }
418
419 private boolean authorize(List<TablePermission> perms, String namespace,
420 Permission.Action action) {
421 if (perms != null) {
422 for (TablePermission p : perms) {
423 if (p.implies(namespace, action)) {
424 return true;
425 }
426 }
427 } else if (LOG.isDebugEnabled()) {
428 LOG.debug("No permissions for authorize() check, table=" + namespace);
429 }
430
431 return false;
432 }
433
434
435
436
437
438
439
440
441
442
443
444 public boolean authorizeUser(User user, TableName table, byte[] family,
445 Permission.Action action) {
446 return authorizeUser(user, table, family, null, action);
447 }
448
449 public boolean authorizeUser(User user, TableName table, byte[] family,
450 byte[] qualifier, Permission.Action action) {
451 if (table == null) table = AccessControlLists.ACL_TABLE_NAME;
452
453 if (authorize(user, table.getNamespaceAsString(), action)) {
454 return true;
455 }
456
457 return authorize(getTablePermissions(table).getUser(user.getShortName()), table, family,
458 qualifier, action);
459 }
460
461
462
463
464
465
466
467
468
469
470 public boolean userHasAccess(User user, TableName table, Permission.Action action) {
471 if (table == null) table = AccessControlLists.ACL_TABLE_NAME;
472
473 if (authorize(user, table.getNamespaceAsString(), action)) {
474 return true;
475 }
476
477 return hasAccess(getTablePermissions(table).getUser(user.getShortName()), table, action);
478 }
479
480
481
482
483
484 public boolean authorizeGroup(String groupName, Permission.Action action) {
485 List<Permission> perms = globalCache.getGroup(groupName);
486 if (LOG.isDebugEnabled()) {
487 LOG.debug("authorizing " + (perms != null && !perms.isEmpty() ? perms.get(0) : "") +
488 " for " + action);
489 }
490 return authorize(perms, action);
491 }
492
493
494
495
496
497
498
499
500
501
502
503 public boolean authorizeGroup(String groupName, TableName table, byte[] family,
504 byte[] qualifier, Permission.Action action) {
505
506 if (authorizeGroup(groupName, action)) {
507 return true;
508 }
509 if (table == null) table = AccessControlLists.ACL_TABLE_NAME;
510
511 String namespace = table.getNamespaceAsString();
512 if (authorize(getNamespacePermissions(namespace).getGroup(groupName), namespace, action)) {
513 return true;
514 }
515
516 List<TablePermission> tblPerms = getTablePermissions(table).getGroup(groupName);
517 if (LOG.isDebugEnabled()) {
518 LOG.debug("authorizing " + (tblPerms != null && !tblPerms.isEmpty() ? tblPerms.get(0) : "") +
519 " for " +groupName + " on " + table + "." + Bytes.toString(family) + "." +
520 Bytes.toString(qualifier) + " with " + action);
521 }
522 return authorize(tblPerms, table, family, qualifier, action);
523 }
524
525
526
527
528
529
530
531
532
533 public boolean groupHasAccess(String groupName, TableName table, Permission.Action action) {
534
535 if (authorizeGroup(groupName, action)) {
536 return true;
537 }
538 if (table == null) table = AccessControlLists.ACL_TABLE_NAME;
539
540 if (hasAccess(getNamespacePermissions(table.getNamespaceAsString()).getGroup(groupName),
541 table, action)) {
542 return true;
543 }
544
545 return hasAccess(getTablePermissions(table).getGroup(groupName), table, action);
546 }
547
548 public boolean authorize(User user, TableName table, byte[] family,
549 byte[] qualifier, Permission.Action action) {
550 if (authorizeUser(user, table, family, qualifier, action)) {
551 return true;
552 }
553
554 String[] groups = user.getGroupNames();
555 if (groups != null) {
556 for (String group : groups) {
557 if (authorizeGroup(group, table, family, qualifier, action)) {
558 return true;
559 }
560 }
561 }
562 return false;
563 }
564
565 public boolean hasAccess(User user, TableName table, Permission.Action action) {
566 if (userHasAccess(user, table, action)) {
567 return true;
568 }
569
570 String[] groups = user.getGroupNames();
571 if (groups != null) {
572 for (String group : groups) {
573 if (groupHasAccess(group, table, action)) {
574 return true;
575 }
576 }
577 }
578 return false;
579 }
580
581 public boolean authorize(User user, TableName table, byte[] family,
582 Permission.Action action) {
583 return authorize(user, table, family, null, action);
584 }
585
586
587
588
589
590
591
592 public boolean matchPermission(User user,
593 TableName table, byte[] family, Permission.Action action) {
594 PermissionCache<TablePermission> tablePerms = tableCache.get(table);
595 if (tablePerms != null) {
596 List<TablePermission> userPerms = tablePerms.getUser(user.getShortName());
597 if (userPerms != null) {
598 for (TablePermission p : userPerms) {
599 if (p.matchesFamily(table, family, action)) {
600 return true;
601 }
602 }
603 }
604
605 String[] groups = user.getGroupNames();
606 if (groups != null) {
607 for (String group : groups) {
608 List<TablePermission> groupPerms = tablePerms.getGroup(group);
609 if (groupPerms != null) {
610 for (TablePermission p : groupPerms) {
611 if (p.matchesFamily(table, family, action)) {
612 return true;
613 }
614 }
615 }
616 }
617 }
618 }
619
620 return false;
621 }
622
623 public boolean matchPermission(User user,
624 TableName table, byte[] family, byte[] qualifier,
625 Permission.Action action) {
626 PermissionCache<TablePermission> tablePerms = tableCache.get(table);
627 if (tablePerms != null) {
628 List<TablePermission> userPerms = tablePerms.getUser(user.getShortName());
629 if (userPerms != null) {
630 for (TablePermission p : userPerms) {
631 if (p.matchesFamilyQualifier(table, family, qualifier, action)) {
632 return true;
633 }
634 }
635 }
636
637 String[] groups = user.getGroupNames();
638 if (groups != null) {
639 for (String group : groups) {
640 List<TablePermission> groupPerms = tablePerms.getGroup(group);
641 if (groupPerms != null) {
642 for (TablePermission p : groupPerms) {
643 if (p.matchesFamilyQualifier(table, family, qualifier, action)) {
644 return true;
645 }
646 }
647 }
648 }
649 }
650 }
651 return false;
652 }
653
654 public void removeNamespace(byte[] ns) {
655 nsCache.remove(Bytes.toString(ns));
656 }
657
658 public void removeTable(TableName table) {
659 tableCache.remove(table);
660 }
661
662
663
664
665
666
667
668
669 public void setTableUserPermissions(String username, TableName table,
670 List<TablePermission> perms) {
671 PermissionCache<TablePermission> tablePerms = getTablePermissions(table);
672 tablePerms.replaceUser(username, perms);
673 writeTableToZooKeeper(table, tablePerms);
674 }
675
676
677
678
679
680
681
682
683 public void setTableGroupPermissions(String group, TableName table,
684 List<TablePermission> perms) {
685 PermissionCache<TablePermission> tablePerms = getTablePermissions(table);
686 tablePerms.replaceGroup(group, perms);
687 writeTableToZooKeeper(table, tablePerms);
688 }
689
690
691
692
693
694
695
696
697 public void setNamespaceUserPermissions(String username, String namespace,
698 List<TablePermission> perms) {
699 PermissionCache<TablePermission> tablePerms = getNamespacePermissions(namespace);
700 tablePerms.replaceUser(username, perms);
701 writeNamespaceToZooKeeper(namespace, tablePerms);
702 }
703
704
705
706
707
708
709
710
711 public void setNamespaceGroupPermissions(String group, String namespace,
712 List<TablePermission> perms) {
713 PermissionCache<TablePermission> tablePerms = getNamespacePermissions(namespace);
714 tablePerms.replaceGroup(group, perms);
715 writeNamespaceToZooKeeper(namespace, tablePerms);
716 }
717
718 public void writeTableToZooKeeper(TableName table,
719 PermissionCache<TablePermission> tablePerms) {
720 byte[] serialized = new byte[0];
721 if (tablePerms != null) {
722 serialized = AccessControlLists.writePermissionsAsBytes(tablePerms.getAllPermissions(), conf);
723 }
724 zkperms.writeToZookeeper(table.getName(), serialized);
725 }
726
727 public void writeNamespaceToZooKeeper(String namespace,
728 PermissionCache<TablePermission> tablePerms) {
729 byte[] serialized = new byte[0];
730 if (tablePerms != null) {
731 serialized = AccessControlLists.writePermissionsAsBytes(tablePerms.getAllPermissions(), conf);
732 }
733 zkperms.writeToZookeeper(Bytes.toBytes(AccessControlLists.toNamespaceEntry(namespace)),
734 serialized);
735 }
736
737 public long getMTime() {
738 return mtime;
739 }
740
741 static Map<ZooKeeperWatcher,TableAuthManager> managerMap =
742 new HashMap<ZooKeeperWatcher,TableAuthManager>();
743
744 public synchronized static TableAuthManager get(
745 ZooKeeperWatcher watcher, Configuration conf) throws IOException {
746 instance = managerMap.get(watcher);
747 if (instance == null) {
748 instance = new TableAuthManager(watcher, conf);
749 managerMap.put(watcher, instance);
750 }
751 return instance;
752 }
753 }