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