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