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