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