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