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