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