1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.apache.hadoop.hbase.security.access;
16
17 import java.io.IOException;
18 import java.net.InetAddress;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.TreeMap;
26 import java.util.TreeSet;
27
28 import com.google.protobuf.Message;
29 import com.google.protobuf.RpcCallback;
30 import com.google.protobuf.RpcController;
31 import com.google.protobuf.Service;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.hadoop.conf.Configuration;
36 import org.apache.hadoop.hbase.Cell;
37 import org.apache.hadoop.hbase.CellUtil;
38 import org.apache.hadoop.hbase.CoprocessorEnvironment;
39 import org.apache.hadoop.hbase.DoNotRetryIOException;
40 import org.apache.hadoop.hbase.HConstants;
41 import org.apache.hadoop.hbase.TableName;
42 import org.apache.hadoop.hbase.HColumnDescriptor;
43 import org.apache.hadoop.hbase.HRegionInfo;
44 import org.apache.hadoop.hbase.HTableDescriptor;
45 import org.apache.hadoop.hbase.KeyValue;
46 import org.apache.hadoop.hbase.KeyValueUtil;
47 import org.apache.hadoop.hbase.NamespaceDescriptor;
48 import org.apache.hadoop.hbase.ServerName;
49 import org.apache.hadoop.hbase.TableNotDisabledException;
50 import org.apache.hadoop.hbase.TableNotFoundException;
51 import org.apache.hadoop.hbase.Tag;
52 import org.apache.hadoop.hbase.client.Append;
53 import org.apache.hadoop.hbase.client.Delete;
54 import org.apache.hadoop.hbase.client.Get;
55 import org.apache.hadoop.hbase.client.Increment;
56 import org.apache.hadoop.hbase.client.Mutation;
57 import org.apache.hadoop.hbase.client.Put;
58 import org.apache.hadoop.hbase.client.Query;
59 import org.apache.hadoop.hbase.client.Result;
60 import org.apache.hadoop.hbase.client.Scan;
61 import org.apache.hadoop.hbase.client.Durability;
62 import org.apache.hadoop.hbase.coprocessor.*;
63 import org.apache.hadoop.hbase.filter.CompareFilter;
64 import org.apache.hadoop.hbase.filter.Filter;
65 import org.apache.hadoop.hbase.filter.FilterList;
66 import org.apache.hadoop.hbase.filter.ByteArrayComparable;
67 import org.apache.hadoop.hbase.io.hfile.HFile;
68 import org.apache.hadoop.hbase.ipc.RequestContext;
69 import org.apache.hadoop.hbase.master.MasterServices;
70 import org.apache.hadoop.hbase.master.RegionPlan;
71 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
72 import org.apache.hadoop.hbase.protobuf.ResponseConverter;
73 import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos;
74 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
75 import org.apache.hadoop.hbase.regionserver.HRegion;
76 import org.apache.hadoop.hbase.regionserver.InternalScanner;
77 import org.apache.hadoop.hbase.regionserver.RegionScanner;
78 import org.apache.hadoop.hbase.regionserver.Store;
79 import org.apache.hadoop.hbase.regionserver.ScanType;
80 import org.apache.hadoop.hbase.regionserver.StoreFile;
81 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
82 import org.apache.hadoop.hbase.security.AccessDeniedException;
83 import org.apache.hadoop.hbase.security.User;
84 import org.apache.hadoop.hbase.security.UserProvider;
85 import org.apache.hadoop.hbase.security.access.Permission.Action;
86 import org.apache.hadoop.hbase.util.Bytes;
87 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
88 import org.apache.hadoop.hbase.util.Pair;
89
90 import com.google.common.collect.ArrayListMultimap;
91 import com.google.common.collect.ImmutableSet;
92 import com.google.common.collect.ListMultimap;
93 import com.google.common.collect.Lists;
94 import com.google.common.collect.MapMaker;
95 import com.google.common.collect.Sets;
96
97 import static org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130 public class AccessController extends BaseRegionObserver
131 implements MasterObserver, RegionServerObserver,
132 AccessControlService.Interface, CoprocessorService, EndpointObserver {
133
134 public static final Log LOG = LogFactory.getLog(AccessController.class);
135
136 private static final Log AUDITLOG =
137 LogFactory.getLog("SecurityLogger."+AccessController.class.getName());
138
139 static final String EXEC_PERMISSION_CHECKS_KEY = "hbase.security.exec.permission.checks";
140 static final boolean DEFAULT_EXEC_PERMISSION_CHECKS = false;
141
142 TableAuthManager authManager = null;
143
144
145 boolean aclRegion = false;
146
147
148
149 private RegionCoprocessorEnvironment regionEnv;
150
151
152 private Map<InternalScanner,String> scannerOwners =
153 new MapMaker().weakKeys().makeMap();
154
155 private UserProvider userProvider;
156
157
158 boolean canPersistCellACLs;
159
160
161 boolean shouldCheckExecPermissions;
162
163 private volatile boolean initialized = false;
164
165 public HRegion getRegion() {
166 return regionEnv != null ? regionEnv.getRegion() : null;
167 }
168
169 public TableAuthManager getAuthManager() {
170 return authManager;
171 }
172
173 void initialize(RegionCoprocessorEnvironment e) throws IOException {
174 final HRegion region = e.getRegion();
175 Map<byte[], ListMultimap<String,TablePermission>> tables =
176 AccessControlLists.loadAll(region);
177
178
179 for (Map.Entry<byte[], ListMultimap<String,TablePermission>> t:
180 tables.entrySet()) {
181 byte[] entry = t.getKey();
182 ListMultimap<String,TablePermission> perms = t.getValue();
183 byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, e.getConfiguration());
184 this.authManager.getZKPermissionWatcher().writeToZookeeper(entry, serialized);
185 }
186 shouldCheckExecPermissions = e.getConfiguration().getBoolean(EXEC_PERMISSION_CHECKS_KEY,
187 DEFAULT_EXEC_PERMISSION_CHECKS);
188 initialized = true;
189 }
190
191
192
193
194
195
196 void updateACL(RegionCoprocessorEnvironment e,
197 final Map<byte[], List<Cell>> familyMap) {
198 Set<byte[]> entries =
199 new TreeSet<byte[]>(Bytes.BYTES_RAWCOMPARATOR);
200 for (Map.Entry<byte[], List<Cell>> f : familyMap.entrySet()) {
201 List<Cell> cells = f.getValue();
202 for (Cell cell: cells) {
203 KeyValue kv = KeyValueUtil.ensureKeyValue(cell);
204 if (Bytes.equals(kv.getBuffer(), kv.getFamilyOffset(),
205 kv.getFamilyLength(), AccessControlLists.ACL_LIST_FAMILY, 0,
206 AccessControlLists.ACL_LIST_FAMILY.length)) {
207 entries.add(kv.getRow());
208 }
209 }
210 }
211 ZKPermissionWatcher zkw = this.authManager.getZKPermissionWatcher();
212 Configuration conf = regionEnv.getConfiguration();
213 for (byte[] entry: entries) {
214 try {
215 ListMultimap<String,TablePermission> perms =
216 AccessControlLists.getPermissions(conf, entry);
217 byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, conf);
218 zkw.writeToZookeeper(entry, serialized);
219 } catch (IOException ex) {
220 LOG.error("Failed updating permissions mirror for '" + Bytes.toString(entry) + "'",
221 ex);
222 }
223 }
224 }
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240 AuthResult permissionGranted(String request, User user, Permission.Action permRequest,
241 RegionCoprocessorEnvironment e,
242 Map<byte [], ? extends Collection<?>> families) {
243 HRegionInfo hri = e.getRegion().getRegionInfo();
244 TableName tableName = hri.getTable();
245
246
247
248 if (hri.isMetaRegion()) {
249 if (permRequest == Permission.Action.READ) {
250 return AuthResult.allow(request, "All users allowed", user,
251 permRequest, tableName, families);
252 }
253 }
254
255 if (user == null) {
256 return AuthResult.deny(request, "No user associated with request!", null,
257 permRequest, tableName, families);
258 }
259
260
261
262
263
264
265 if (permRequest == Permission.Action.WRITE &&
266 (hri.isMetaRegion() ||
267 Bytes.equals(tableName.getName(), AccessControlLists.ACL_GLOBAL_NAME)) &&
268 (authManager.authorize(user, Permission.Action.CREATE) ||
269 authManager.authorize(user, Permission.Action.ADMIN)))
270 {
271 return AuthResult.allow(request, "Table permission granted", user,
272 permRequest, tableName, families);
273 }
274
275
276 if (authManager.authorize(user, tableName, (byte[])null, permRequest)) {
277 return AuthResult.allow(request, "Table permission granted", user,
278 permRequest, tableName, families);
279 }
280
281
282 if (families != null && families.size() > 0) {
283
284 for (Map.Entry<byte [], ? extends Collection<?>> family : families.entrySet()) {
285
286 if (authManager.authorize(user, tableName, family.getKey(),
287 permRequest)) {
288 continue;
289 }
290
291
292 if ((family.getValue() != null) && (family.getValue().size() > 0)) {
293 if (family.getValue() instanceof Set) {
294
295 Set<byte[]> familySet = (Set<byte[]>)family.getValue();
296 for (byte[] qualifier : familySet) {
297 if (!authManager.authorize(user, tableName, family.getKey(),
298 qualifier, permRequest)) {
299 return AuthResult.deny(request, "Failed qualifier check", user,
300 permRequest, tableName, makeFamilyMap(family.getKey(), qualifier));
301 }
302 }
303 } else if (family.getValue() instanceof List) {
304 List<KeyValue> kvList = (List<KeyValue>)family.getValue();
305 for (KeyValue kv : kvList) {
306 if (!authManager.authorize(user, tableName, family.getKey(),
307 kv.getQualifier(), permRequest)) {
308 return AuthResult.deny(request, "Failed qualifier check", user,
309 permRequest, tableName, makeFamilyMap(family.getKey(), kv.getQualifier()));
310 }
311 }
312 }
313 } else {
314
315 return AuthResult.deny(request, "Failed family check", user, permRequest,
316 tableName, makeFamilyMap(family.getKey(), null));
317 }
318 }
319
320
321 return AuthResult.allow(request, "All family checks passed", user, permRequest,
322 tableName, families);
323 }
324
325
326 return AuthResult.deny(request, "No families to check and table permission failed",
327 user, permRequest, tableName, families);
328 }
329
330 private void logResult(AuthResult result) {
331 if (AUDITLOG.isTraceEnabled()) {
332 RequestContext ctx = RequestContext.get();
333 InetAddress remoteAddr = null;
334 if (ctx != null) {
335 remoteAddr = ctx.getRemoteAddress();
336 }
337 AUDITLOG.trace("Access " + (result.isAllowed() ? "allowed" : "denied") +
338 " for user " + (result.getUser() != null ? result.getUser().getShortName() : "UNKNOWN") +
339 "; reason: " + result.getReason() +
340 "; remote address: " + (remoteAddr != null ? remoteAddr : "") +
341 "; request: " + result.getRequest() +
342 "; context: " + result.toContextString());
343 }
344 }
345
346
347
348
349
350
351 private User getActiveUser() throws IOException {
352 User user = RequestContext.getRequestUser();
353 if (!RequestContext.isInRequestContext()) {
354
355 user = userProvider.getCurrent();
356 }
357 return user;
358 }
359
360
361
362
363
364
365
366
367
368
369 private void requirePermission(String request, TableName tableName, byte[] family, byte[] qualifier,
370 Action... permissions) throws IOException {
371 User user = getActiveUser();
372 AuthResult result = null;
373
374 for (Action permission : permissions) {
375 if (authManager.authorize(user, tableName, family, qualifier, permission)) {
376 result = AuthResult.allow(request, "Table permission granted", user,
377 permission, tableName, family, qualifier);
378 break;
379 } else {
380
381 result = AuthResult.deny(request, "Insufficient permissions", user,
382 permission, tableName, family, qualifier);
383 }
384 }
385 logResult(result);
386 if (!result.isAllowed()) {
387 throw new AccessDeniedException("Insufficient permissions " + result.toContextString());
388 }
389 }
390
391
392
393
394
395
396
397 private void requirePermission(String request, Permission.Action perm) throws IOException {
398 requireGlobalPermission(request, perm, null, null);
399 }
400
401
402
403
404
405
406
407
408
409 private void requirePermission(String request, Permission.Action perm,
410 RegionCoprocessorEnvironment env,
411 Map<byte[], ? extends Collection<?>> families)
412 throws IOException {
413 User user = getActiveUser();
414 AuthResult result = permissionGranted(request, user, perm, env, families);
415 logResult(result);
416
417 if (!result.isAllowed()) {
418 throw new AccessDeniedException("Insufficient permissions (table=" +
419 env.getRegion().getTableDesc().getTableName()+
420 ((families != null && families.size() > 0) ? ", family: " +
421 result.toFamilyString() : "") + ", action=" +
422 perm.toString() + ")");
423 }
424 }
425
426
427
428
429
430
431
432
433
434 private void requireGlobalPermission(String request, Permission.Action perm, TableName tableName,
435 Map<byte[], ? extends Collection<byte[]>> familyMap) throws IOException {
436 User user = getActiveUser();
437 if (authManager.authorize(user, perm)) {
438 logResult(AuthResult.allow(request, "Global check allowed", user, perm, tableName, familyMap));
439 } else {
440 logResult(AuthResult.deny(request, "Global check failed", user, perm, tableName, familyMap));
441 throw new AccessDeniedException("Insufficient permissions for user '" +
442 (user != null ? user.getShortName() : "null") +"' (global, action=" +
443 perm.toString() + ")");
444 }
445 }
446
447
448
449
450
451
452
453
454 private void requireGlobalPermission(String request, Permission.Action perm,
455 String namespace) throws IOException {
456 User user = getActiveUser();
457 if (authManager.authorize(user, perm)) {
458 logResult(AuthResult.allow(request, "Global check allowed", user, perm, namespace));
459 } else {
460 logResult(AuthResult.deny(request, "Global check failed", user, perm, namespace));
461 throw new AccessDeniedException("Insufficient permissions for user '" +
462 (user != null ? user.getShortName() : "null") +"' (global, action=" +
463 perm.toString() + ")");
464 }
465 }
466
467 private void requireCoveringPermission(String request, RegionCoprocessorEnvironment e,
468 byte[] row, Map<byte[], ? extends Collection<?>> familyMap, long timestamp,
469 boolean allVersions, Action...actions) throws IOException {
470 User user = getActiveUser();
471
472
473
474
475 List<Action> cellCheckActions = Lists.newArrayList();
476
477
478
479 AuthResult results[] = new AuthResult[actions.length];
480 for (int i = 0; i < actions.length; i++) {
481 results[i] = permissionGranted(request, user, actions[i], e, familyMap);
482 if (!results[i].isAllowed()) {
483 if (LOG.isTraceEnabled()) {
484 LOG.trace("Got " + results[i] + ", added to cellCheckActions");
485 }
486 cellCheckActions.add(actions[i]);
487 }
488 }
489
490 if (cellCheckActions.isEmpty()) {
491 if (LOG.isTraceEnabled()) {
492 LOG.trace("All permissions checks passed, we can early out");
493 }
494 for (int i = 0; i < results.length; i++) {
495 logResult(results[i]);
496 }
497 return;
498 }
499
500
501
502 int cellsChecked = 0;
503 if (canPersistCellACLs) {
504 Get get = new Get(row);
505 if (timestamp != HConstants.LATEST_TIMESTAMP) get.setTimeStamp(timestamp);
506 get.setMaxResultsPerColumnFamily(1);
507 if (allVersions) {
508 get.setMaxVersions();
509 } else {
510 get.setMaxVersions(1);
511 }
512 for (Map.Entry<byte[], ? extends Collection<?>> entry: familyMap.entrySet()) {
513 byte[] col = entry.getKey();
514
515
516 if (entry.getValue() instanceof Set) {
517 Set<byte[]> set = (Set<byte[]>)entry.getValue();
518 if (set == null || set.isEmpty()) {
519 get.addFamily(col);
520 } else {
521 for (byte[] qual: set) {
522 get.addColumn(col, qual);
523 }
524 }
525 } else if (entry.getValue() instanceof List) {
526 List<Cell> list = (List<Cell>)entry.getValue();
527 if (list == null || list.isEmpty()) {
528 get.addFamily(col);
529 } else {
530 for (Cell cell: list) {
531 get.addColumn(col, CellUtil.cloneQualifier(cell));
532 }
533 }
534 } else {
535 throw new RuntimeException("Unhandled collection type " +
536 entry.getValue().getClass().getName());
537 }
538 }
539 if (LOG.isTraceEnabled()) {
540 LOG.trace("Scanning for cells with " + get);
541 }
542 RegionScanner scanner = getRegion(e).getScanner(new Scan(get));
543 List<Cell> cells = Lists.newArrayList();
544 try {
545 boolean more = false;
546 do {
547 cells.clear();
548 more = scanner.next(cells);
549 for (Cell cell: cells) {
550 if (LOG.isTraceEnabled()) {
551 LOG.trace("Found cell " + cell);
552 }
553 for (Action action: cellCheckActions) {
554
555 if (!authManager.authorize(user, getTableName(e), cell, false, action)) {
556 AuthResult authResult = AuthResult.deny(request, "Insufficient permissions",
557 user, action, getTableName(e), CellUtil.cloneFamily(cell),
558 CellUtil.cloneQualifier(cell));
559 logResult(authResult);
560 throw new AccessDeniedException("Insufficient permissions " +
561 authResult.toContextString());
562 }
563 }
564 cellsChecked++;
565 }
566 } while (more);
567 } catch (AccessDeniedException ex) {
568 throw ex;
569 } catch (IOException ex) {
570 LOG.error("Exception while getting cells to calculate covering permission", ex);
571 } finally {
572 scanner.close();
573 }
574 }
575
576
577 if (cellsChecked < 1) {
578 if (LOG.isTraceEnabled()) {
579 LOG.trace("No cells found with scan");
580 }
581 AuthResult authResult = AuthResult.deny(request, "Insufficient permissions",
582 user, cellCheckActions.get(0), getTableName(e), familyMap);
583 logResult(authResult);
584 throw new AccessDeniedException("Insufficient permissions " +
585 authResult.toContextString());
586 }
587
588
589
590 for (byte[] family: familyMap.keySet()) {
591 for (Action action: actions) {
592 logResult(AuthResult.allow(request, "Permission granted", user, action,
593 getTableName(e), family, null));
594 }
595 }
596 }
597
598 private void addCellPermissions(final byte[] perms, Map<byte[], List<Cell>> familyMap) {
599
600
601 for (Map.Entry<byte[], List<Cell>> e: familyMap.entrySet()) {
602 List<Cell> newCells = Lists.newArrayList();
603 for (Cell cell: e.getValue()) {
604 List<Tag> tags = Lists.newArrayList(new Tag(AccessControlLists.ACL_TAG_TYPE, perms));
605 byte[] tagBytes = CellUtil.getTagArray(cell);
606 Iterator<Tag> tagIterator = CellUtil.tagsIterator(tagBytes, 0, tagBytes.length);
607 while (tagIterator.hasNext()) {
608 tags.add(tagIterator.next());
609 }
610
611
612 KeyValue kv = KeyValueUtil.ensureKeyValue(cell);
613 byte[] bytes = kv.getBuffer();
614 newCells.add(
615 new KeyValue(bytes, kv.getRowOffset(), kv.getRowLength(),
616 bytes, kv.getFamilyOffset(), kv.getFamilyLength(),
617 bytes, kv.getQualifierOffset(), kv.getQualifierLength(),
618 kv.getTimestamp(), KeyValue.Type.codeToType(kv.getTypeByte()),
619 bytes, kv.getValueOffset(), kv.getValueLength(),
620 tags));
621 }
622
623 e.setValue(newCells);
624 }
625 }
626
627 private void internalPreRead(final ObserverContext<RegionCoprocessorEnvironment> c,
628 final Query query) throws IOException {
629 TableName tableName = getTableName(c.getEnvironment());
630 User activeUser = getActiveUser();
631 Filter filter = query.getFilter();
632 boolean cellFirstStrategy = query.getACLStrategy();
633
634 if (filter != null && filter instanceof AccessControlFilter) {
635 return;
636 }
637 Filter newFilter = (filter != null)
638 ? new FilterList(FilterList.Operator.MUST_PASS_ALL,
639 Lists.newArrayList(
640 new AccessControlFilter(authManager, activeUser, tableName, cellFirstStrategy),
641 filter))
642 : new AccessControlFilter(authManager, activeUser, tableName, cellFirstStrategy);
643 query.setFilter(newFilter);
644 }
645
646
647
648 public void start(CoprocessorEnvironment env) throws IOException {
649 canPersistCellACLs = HFile.getFormatVersion(env.getConfiguration()) >=
650 HFile.MIN_FORMAT_VERSION_WITH_TAGS;
651 if (!canPersistCellACLs) {
652 LOG.info("A minimum HFile version of " + HFile.MIN_FORMAT_VERSION_WITH_TAGS
653 + " is required to persist cell ACLs. Consider setting " + HFile.FORMAT_VERSION_KEY
654 + " accordingly.");
655 }
656 ZooKeeperWatcher zk = null;
657 if (env instanceof MasterCoprocessorEnvironment) {
658
659 MasterCoprocessorEnvironment mEnv = (MasterCoprocessorEnvironment) env;
660 zk = mEnv.getMasterServices().getZooKeeper();
661 } else if (env instanceof RegionServerCoprocessorEnvironment) {
662 RegionServerCoprocessorEnvironment rsEnv = (RegionServerCoprocessorEnvironment) env;
663 zk = rsEnv.getRegionServerServices().getZooKeeper();
664 } else if (env instanceof RegionCoprocessorEnvironment) {
665
666 regionEnv = (RegionCoprocessorEnvironment) env;
667 zk = regionEnv.getRegionServerServices().getZooKeeper();
668 }
669
670
671 this.userProvider = UserProvider.instantiate(env.getConfiguration());
672
673
674
675 if (zk != null) {
676 try {
677 this.authManager = TableAuthManager.get(zk, env.getConfiguration());
678 } catch (IOException ioe) {
679 throw new RuntimeException("Error obtaining TableAuthManager", ioe);
680 }
681 } else {
682 throw new RuntimeException("Error obtaining TableAuthManager, zk found null.");
683 }
684 }
685
686 public void stop(CoprocessorEnvironment env) {
687
688 }
689
690 @Override
691 public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
692 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
693 Set<byte[]> families = desc.getFamiliesKeys();
694 Map<byte[], Set<byte[]>> familyMap = new TreeMap<byte[], Set<byte[]>>(Bytes.BYTES_COMPARATOR);
695 for (byte[] family: families) {
696 familyMap.put(family, null);
697 }
698 requireGlobalPermission("createTable", Permission.Action.CREATE, desc.getTableName(), familyMap);
699 }
700
701 @Override
702 public void preCreateTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
703 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {}
704
705 @Override
706 public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
707 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
708 if (!AccessControlLists.isAclTable(desc)) {
709 String owner = desc.getOwnerString();
710
711 if (owner == null) owner = getActiveUser().getShortName();
712 UserPermission userperm = new UserPermission(Bytes.toBytes(owner), desc.getTableName(), null,
713 Action.values());
714 AccessControlLists.addUserPermission(c.getEnvironment().getConfiguration(), userperm);
715 }
716 }
717
718 @Override
719 public void postCreateTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
720 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {}
721
722 @Override
723 public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
724 throws IOException {
725 requirePermission("deleteTable", tableName, null, null, Action.ADMIN, Action.CREATE);
726 }
727
728 @Override
729 public void preDeleteTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
730 TableName tableName) throws IOException {}
731
732 @Override
733 public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c,
734 TableName tableName) throws IOException {
735 AccessControlLists.removeTablePermissions(c.getEnvironment().getConfiguration(), tableName);
736 }
737
738 @Override
739 public void postDeleteTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
740 TableName tableName) throws IOException {}
741
742 @Override
743 public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
744 HTableDescriptor htd) throws IOException {
745 requirePermission("modifyTable", tableName, null, null, Action.ADMIN, Action.CREATE);
746 }
747
748 @Override
749 public void preModifyTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
750 TableName tableName, HTableDescriptor htd) throws IOException {}
751
752 @Override
753 public void postModifyTable(ObserverContext<MasterCoprocessorEnvironment> c,
754 TableName tableName, HTableDescriptor htd) throws IOException {
755 String owner = htd.getOwnerString();
756
757 if (owner == null) owner = getActiveUser().getShortName();
758 UserPermission userperm = new UserPermission(Bytes.toBytes(owner), htd.getTableName(), null,
759 Action.values());
760 AccessControlLists.addUserPermission(c.getEnvironment().getConfiguration(), userperm);
761 }
762
763 @Override
764 public void postModifyTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
765 TableName tableName, HTableDescriptor htd) throws IOException {}
766
767
768 @Override
769 public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
770 HColumnDescriptor column) throws IOException {
771 requirePermission("addColumn", tableName, null, null, Action.ADMIN, Action.CREATE);
772 }
773
774 @Override
775 public void preAddColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
776 TableName tableName, HColumnDescriptor column) throws IOException {}
777
778 @Override
779 public void postAddColumn(ObserverContext<MasterCoprocessorEnvironment> c,
780 TableName tableName, HColumnDescriptor column) throws IOException {}
781
782 @Override
783 public void postAddColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
784 TableName tableName, HColumnDescriptor column) throws IOException {}
785
786 @Override
787 public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
788 HColumnDescriptor descriptor) throws IOException {
789 requirePermission("modifyColumn", tableName, null, null, Action.ADMIN, Action.CREATE);
790 }
791
792 @Override
793 public void preModifyColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
794 TableName tableName, HColumnDescriptor descriptor) throws IOException {}
795
796 @Override
797 public void postModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c,
798 TableName tableName, HColumnDescriptor descriptor) throws IOException {}
799
800 @Override
801 public void postModifyColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
802 TableName tableName, HColumnDescriptor descriptor) throws IOException {}
803
804 @Override
805 public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
806 byte[] col) throws IOException {
807 requirePermission("deleteColumn", tableName, null, null, Action.ADMIN, Action.CREATE);
808 }
809
810 @Override
811 public void preDeleteColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
812 TableName tableName, byte[] col) throws IOException {}
813
814 @Override
815 public void postDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c,
816 TableName tableName, byte[] col) throws IOException {
817 AccessControlLists.removeTablePermissions(c.getEnvironment().getConfiguration(),
818 tableName, col);
819 }
820
821 @Override
822 public void postDeleteColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
823 TableName tableName, byte[] col) throws IOException {}
824
825 @Override
826 public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
827 throws IOException {
828 requirePermission("enableTable", tableName, null, null, Action.ADMIN, Action.CREATE);
829 }
830
831 @Override
832 public void preEnableTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
833 TableName tableName) throws IOException {}
834
835 @Override
836 public void postEnableTable(ObserverContext<MasterCoprocessorEnvironment> c,
837 TableName tableName) throws IOException {}
838
839 @Override
840 public void postEnableTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
841 TableName tableName) throws IOException {}
842
843 @Override
844 public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
845 throws IOException {
846 if (Bytes.equals(tableName.getName(), AccessControlLists.ACL_GLOBAL_NAME)) {
847 throw new AccessDeniedException("Not allowed to disable "
848 + AccessControlLists.ACL_TABLE_NAME + " table.");
849 }
850 requirePermission("disableTable", tableName, null, null, Action.ADMIN, Action.CREATE);
851 }
852
853 @Override
854 public void preDisableTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
855 TableName tableName) throws IOException {}
856
857 @Override
858 public void postDisableTable(ObserverContext<MasterCoprocessorEnvironment> c,
859 TableName tableName) throws IOException {}
860
861 @Override
862 public void postDisableTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
863 TableName tableName) throws IOException {}
864
865 @Override
866 public void preMove(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo region,
867 ServerName srcServer, ServerName destServer) throws IOException {
868 requirePermission("move", region.getTable(), null, null, Action.ADMIN);
869 }
870
871 @Override
872 public void postMove(ObserverContext<MasterCoprocessorEnvironment> c,
873 HRegionInfo region, ServerName srcServer, ServerName destServer)
874 throws IOException {}
875
876 @Override
877 public void preAssign(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo regionInfo)
878 throws IOException {
879 requirePermission("assign", regionInfo.getTable(), null, null, Action.ADMIN);
880 }
881
882 @Override
883 public void postAssign(ObserverContext<MasterCoprocessorEnvironment> c,
884 HRegionInfo regionInfo) throws IOException {}
885
886 @Override
887 public void preUnassign(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo regionInfo,
888 boolean force) throws IOException {
889 requirePermission("unassign", regionInfo.getTable(), null, null, Action.ADMIN);
890 }
891
892 @Override
893 public void postUnassign(ObserverContext<MasterCoprocessorEnvironment> c,
894 HRegionInfo regionInfo, boolean force) throws IOException {}
895
896 @Override
897 public void preRegionOffline(ObserverContext<MasterCoprocessorEnvironment> c,
898 HRegionInfo regionInfo) throws IOException {
899 requirePermission("regionOffline", regionInfo.getTable(), null, null, Action.ADMIN);
900 }
901
902 @Override
903 public void postRegionOffline(ObserverContext<MasterCoprocessorEnvironment> c,
904 HRegionInfo regionInfo) throws IOException {
905 }
906
907 @Override
908 public void preBalance(ObserverContext<MasterCoprocessorEnvironment> c)
909 throws IOException {
910 requirePermission("balance", Permission.Action.ADMIN);
911 }
912 @Override
913 public void postBalance(ObserverContext<MasterCoprocessorEnvironment> c, List<RegionPlan> plans)
914 throws IOException {}
915
916 @Override
917 public boolean preBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> c,
918 boolean newValue) throws IOException {
919 requirePermission("balanceSwitch", Permission.Action.ADMIN);
920 return newValue;
921 }
922
923 @Override
924 public void postBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> c,
925 boolean oldValue, boolean newValue) throws IOException {}
926
927 @Override
928 public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> c)
929 throws IOException {
930 requirePermission("shutdown", Permission.Action.ADMIN);
931 }
932
933 @Override
934 public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> c)
935 throws IOException {
936 requirePermission("stopMaster", Permission.Action.ADMIN);
937 }
938
939 @Override
940 public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx)
941 throws IOException {
942
943 AccessControlLists.init(ctx.getEnvironment().getMasterServices());
944 }
945
946 @Override
947 public void preMasterInitialization(
948 ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
949 }
950
951 @Override
952 public void preSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
953 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
954 throws IOException {
955 requirePermission("snapshot", Permission.Action.ADMIN);
956 }
957
958 @Override
959 public void postSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
960 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
961 throws IOException {
962 }
963
964 @Override
965 public void preCloneSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
966 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
967 throws IOException {
968 requirePermission("clone", Permission.Action.ADMIN);
969 }
970
971 @Override
972 public void postCloneSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
973 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
974 throws IOException {
975 }
976
977 @Override
978 public void preRestoreSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
979 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
980 throws IOException {
981 requirePermission("restore", Permission.Action.ADMIN);
982 }
983
984 @Override
985 public void postRestoreSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
986 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
987 throws IOException {
988 }
989
990 @Override
991 public void preDeleteSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
992 final SnapshotDescription snapshot) throws IOException {
993 requirePermission("deleteSnapshot", Permission.Action.ADMIN);
994 }
995
996 @Override
997 public void postDeleteSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
998 final SnapshotDescription snapshot) throws IOException {
999 }
1000
1001 @Override
1002 public void preCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
1003 NamespaceDescriptor ns) throws IOException {
1004 requireGlobalPermission("createNamespace", Action.ADMIN, ns.getName());
1005 }
1006
1007 @Override
1008 public void postCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
1009 NamespaceDescriptor ns) throws IOException {
1010 }
1011
1012 @Override
1013 public void preDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, String namespace)
1014 throws IOException {
1015 requireGlobalPermission("deleteNamespace", Action.ADMIN, namespace);
1016 }
1017
1018 @Override
1019 public void postDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
1020 String namespace) throws IOException {
1021 AccessControlLists.removeNamespacePermissions(ctx.getEnvironment().getConfiguration(),
1022 namespace);
1023 LOG.info(namespace + "entry deleted in "+AccessControlLists.ACL_TABLE_NAME+" table.");
1024 }
1025
1026 @Override
1027 public void preModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
1028 NamespaceDescriptor ns) throws IOException {
1029 requireGlobalPermission("modifyNamespace", Action.ADMIN, ns.getName());
1030 }
1031
1032 @Override
1033 public void postModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
1034 NamespaceDescriptor ns) throws IOException {
1035 }
1036
1037
1038
1039 @Override
1040 public void preOpen(ObserverContext<RegionCoprocessorEnvironment> e)
1041 throws IOException {
1042 RegionCoprocessorEnvironment env = e.getEnvironment();
1043 final HRegion region = env.getRegion();
1044 if (region == null) {
1045 LOG.error("NULL region from RegionCoprocessorEnvironment in preOpen()");
1046 } else {
1047 HRegionInfo regionInfo = region.getRegionInfo();
1048 if (regionInfo.getTable().isSystemTable()) {
1049 isSystemOrSuperUser(regionEnv.getConfiguration());
1050 } else {
1051 requirePermission("preOpen", Action.ADMIN);
1052 }
1053 }
1054 }
1055
1056 @Override
1057 public void postOpen(ObserverContext<RegionCoprocessorEnvironment> c) {
1058 RegionCoprocessorEnvironment env = c.getEnvironment();
1059 final HRegion region = env.getRegion();
1060 if (region == null) {
1061 LOG.error("NULL region from RegionCoprocessorEnvironment in postOpen()");
1062 return;
1063 }
1064 if (AccessControlLists.isAclRegion(region)) {
1065 aclRegion = true;
1066
1067 if (!region.isRecovering()) {
1068 try {
1069 initialize(env);
1070 } catch (IOException ex) {
1071
1072
1073 throw new RuntimeException("Failed to initialize permissions cache", ex);
1074 }
1075 }
1076 } else {
1077 initialized = true;
1078 }
1079 }
1080
1081 @Override
1082 public void postLogReplay(ObserverContext<RegionCoprocessorEnvironment> c) {
1083 if (aclRegion) {
1084 try {
1085 initialize(c.getEnvironment());
1086 } catch (IOException ex) {
1087
1088
1089 throw new RuntimeException("Failed to initialize permissions cache", ex);
1090 }
1091 }
1092 }
1093
1094 @Override
1095 public void preFlush(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
1096 requirePermission("flush", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
1097 }
1098
1099 @Override
1100 public void preSplit(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
1101 requirePermission("split", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
1102 }
1103
1104 @Override
1105 public void preSplit(ObserverContext<RegionCoprocessorEnvironment> e,
1106 byte[] splitRow) throws IOException {
1107 requirePermission("split", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
1108 }
1109
1110 @Override
1111 public InternalScanner preCompact(ObserverContext<RegionCoprocessorEnvironment> e,
1112 final Store store, final InternalScanner scanner, final ScanType scanType)
1113 throws IOException {
1114 requirePermission("compact", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
1115 return scanner;
1116 }
1117
1118 @Override
1119 public void preCompactSelection(final ObserverContext<RegionCoprocessorEnvironment> e,
1120 final Store store, final List<StoreFile> candidates) throws IOException {
1121 requirePermission("compact", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
1122 }
1123
1124 @Override
1125 public void preGetClosestRowBefore(final ObserverContext<RegionCoprocessorEnvironment> c,
1126 final byte [] row, final byte [] family, final Result result)
1127 throws IOException {
1128 assert family != null;
1129 requireCoveringPermission("getClosestRowBefore", c.getEnvironment(), row,
1130 makeFamilyMap(family, null), HConstants.LATEST_TIMESTAMP, false, Permission.Action.READ);
1131 }
1132
1133 @Override
1134 public void preGetOp(final ObserverContext<RegionCoprocessorEnvironment> c,
1135 final Get get, final List<Cell> result) throws IOException {
1136 internalPreRead(c, get);
1137 }
1138
1139 @Override
1140 public boolean preExists(final ObserverContext<RegionCoprocessorEnvironment> c,
1141 final Get get, final boolean exists) throws IOException {
1142 internalPreRead(c, get);
1143 return exists;
1144 }
1145
1146 @Override
1147 public void prePut(final ObserverContext<RegionCoprocessorEnvironment> c,
1148 final Put put, final WALEdit edit, final Durability durability)
1149 throws IOException {
1150
1151
1152
1153
1154
1155
1156 requireCoveringPermission("put", c.getEnvironment(), put.getRow(),
1157 put.getFamilyCellMap(), put.getTimeStamp(), false, Permission.Action.WRITE);
1158 byte[] bytes = put.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
1159 if (bytes != null) {
1160 if (canPersistCellACLs) {
1161 addCellPermissions(bytes, put.getFamilyCellMap());
1162 } else {
1163 throw new DoNotRetryIOException("Cell ACLs cannot be persisted");
1164 }
1165 }
1166 }
1167
1168 @Override
1169 public void postPut(final ObserverContext<RegionCoprocessorEnvironment> c,
1170 final Put put, final WALEdit edit, final Durability durability) {
1171 if (aclRegion) {
1172 updateACL(c.getEnvironment(), put.getFamilyCellMap());
1173 }
1174 }
1175
1176 @Override
1177 public void preDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
1178 final Delete delete, final WALEdit edit, final Durability durability)
1179 throws IOException {
1180
1181 if (delete.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL) != null) {
1182 throw new DoNotRetryIOException("ACL on delete has no effect: " + delete.toString());
1183 }
1184
1185
1186
1187
1188
1189 requireCoveringPermission("delete", c.getEnvironment(), delete.getRow(),
1190 delete.getFamilyCellMap(), delete.getTimeStamp(), true, Action.WRITE);
1191 }
1192
1193 @Override
1194 public void postDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
1195 final Delete delete, final WALEdit edit, final Durability durability)
1196 throws IOException {
1197 if (aclRegion) {
1198 updateACL(c.getEnvironment(), delete.getFamilyCellMap());
1199 }
1200 }
1201
1202 @Override
1203 public boolean preCheckAndPut(final ObserverContext<RegionCoprocessorEnvironment> c,
1204 final byte [] row, final byte [] family, final byte [] qualifier,
1205 final CompareFilter.CompareOp compareOp,
1206 final ByteArrayComparable comparator, final Put put,
1207 final boolean result) throws IOException {
1208
1209 requireCoveringPermission("checkAndPut", c.getEnvironment(), row,
1210 makeFamilyMap(family, qualifier), HConstants.LATEST_TIMESTAMP, false,
1211 Action.READ, Action.WRITE);
1212 byte[] bytes = put.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
1213 if (bytes != null) {
1214 if (canPersistCellACLs) {
1215 addCellPermissions(bytes, put.getFamilyCellMap());
1216 } else {
1217 throw new DoNotRetryIOException("Cell ACLs cannot be persisted");
1218 }
1219 }
1220 return result;
1221 }
1222
1223 @Override
1224 public boolean preCheckAndDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
1225 final byte [] row, final byte [] family, final byte [] qualifier,
1226 final CompareFilter.CompareOp compareOp,
1227 final ByteArrayComparable comparator, final Delete delete,
1228 final boolean result) throws IOException {
1229
1230 if (delete.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL) != null) {
1231 throw new DoNotRetryIOException("ACL on checkAndDelete has no effect: " +
1232 delete.toString());
1233 }
1234
1235
1236 requireCoveringPermission("checkAndDelete", c.getEnvironment(), row,
1237 makeFamilyMap(family, qualifier), HConstants.LATEST_TIMESTAMP, false,
1238 Action.READ, Action.WRITE);
1239 return result;
1240 }
1241
1242 @Override
1243 public long preIncrementColumnValue(final ObserverContext<RegionCoprocessorEnvironment> c,
1244 final byte [] row, final byte [] family, final byte [] qualifier,
1245 final long amount, final boolean writeToWAL)
1246 throws IOException {
1247
1248
1249 requireCoveringPermission("incrementColumnValue", c.getEnvironment(), row,
1250 makeFamilyMap(family, qualifier), HConstants.LATEST_TIMESTAMP, false,
1251 Action.WRITE);
1252 return -1;
1253 }
1254
1255 @Override
1256 public Result preAppend(ObserverContext<RegionCoprocessorEnvironment> c, Append append)
1257 throws IOException {
1258
1259 requireCoveringPermission("append", c.getEnvironment(), append.getRow(),
1260 append.getFamilyCellMap(), append.getTimeStamp(), false,
1261 Action.WRITE);
1262 byte[] bytes = append.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
1263 if (bytes != null) {
1264 if (canPersistCellACLs) {
1265 addCellPermissions(bytes, append.getFamilyCellMap());
1266 } else {
1267 throw new DoNotRetryIOException("Cell ACLs cannot be persisted");
1268 }
1269 }
1270 return null;
1271 }
1272
1273 @Override
1274 public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> c,
1275 final Increment increment)
1276 throws IOException {
1277
1278
1279 requireCoveringPermission("increment", c.getEnvironment(), increment.getRow(),
1280 increment.getFamilyCellMap(), increment.getTimeRange().getMax(), false,
1281 Action.WRITE);
1282 byte[] bytes = increment.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
1283 if (bytes != null) {
1284 if (canPersistCellACLs) {
1285 addCellPermissions(bytes, increment.getFamilyCellMap());
1286 } else {
1287 throw new DoNotRetryIOException("Cell ACLs cannot be persisted");
1288 }
1289 }
1290 return null;
1291 }
1292
1293 @Override
1294 public Cell postMutationBeforeWAL(ObserverContext<RegionCoprocessorEnvironment> ctx,
1295 MutationType opType, Mutation mutation, Cell oldCell, Cell newCell) throws IOException {
1296
1297
1298 if (!canPersistCellACLs) {
1299 return newCell;
1300 }
1301
1302
1303 List<Tag> tags = Lists.newArrayList();
1304 ListMultimap<String,Permission> perms = ArrayListMultimap.create();
1305 if (oldCell != null) {
1306 byte[] tagBytes = CellUtil.getTagArray(oldCell);
1307 Iterator<Tag> tagIterator = CellUtil.tagsIterator(tagBytes, 0, tagBytes.length);
1308 while (tagIterator.hasNext()) {
1309 Tag tag = tagIterator.next();
1310 if (tag.getType() != AccessControlLists.ACL_TAG_TYPE) {
1311 if (LOG.isTraceEnabled()) {
1312 LOG.trace("Carrying forward tag from " + oldCell + ": type " + tag.getType() +
1313 " length " + tag.getValue().length);
1314 }
1315 tags.add(tag);
1316 } else {
1317 ListMultimap<String,Permission> kvPerms = ProtobufUtil.toUsersAndPermissions(
1318 AccessControlProtos.UsersAndPermissions.newBuilder().mergeFrom(
1319 tag.getBuffer(), tag.getTagOffset(), tag.getTagLength()).build());
1320 perms.putAll(kvPerms);
1321 }
1322 }
1323 }
1324
1325
1326 byte[] aclBytes = mutation.getACL();
1327 if (aclBytes != null) {
1328
1329 tags.add(new Tag(AccessControlLists.ACL_TAG_TYPE, aclBytes));
1330 } else {
1331
1332 if (perms != null) {
1333
1334
1335
1336 if (LOG.isTraceEnabled()) {
1337 LOG.trace("Carrying forward ACLs from " + oldCell + ": " + perms);
1338 }
1339 tags.add(new Tag(AccessControlLists.ACL_TAG_TYPE,
1340 ProtobufUtil.toUsersAndPermissions(perms).toByteArray()));
1341 }
1342 }
1343
1344
1345 if (tags.isEmpty()) {
1346 return newCell;
1347 }
1348
1349
1350
1351 KeyValue newKv = KeyValueUtil.ensureKeyValue(newCell);
1352 byte[] bytes = newKv.getBuffer();
1353 KeyValue rewriteKv = new KeyValue(bytes, newKv.getRowOffset(), newKv.getRowLength(),
1354 bytes, newKv.getFamilyOffset(), newKv.getFamilyLength(),
1355 bytes, newKv.getQualifierOffset(), newKv.getQualifierLength(),
1356 newKv.getTimestamp(), KeyValue.Type.codeToType(newKv.getTypeByte()),
1357 bytes, newKv.getValueOffset(), newKv.getValueLength(),
1358 tags);
1359
1360 rewriteKv.setMvccVersion(newKv.getMvccVersion());
1361 return rewriteKv;
1362 }
1363
1364 @Override
1365 public RegionScanner preScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
1366 final Scan scan, final RegionScanner s) throws IOException {
1367 internalPreRead(c, scan);
1368 return s;
1369 }
1370
1371 @Override
1372 public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
1373 final Scan scan, final RegionScanner s) throws IOException {
1374 User user = getActiveUser();
1375 if (user != null && user.getShortName() != null) {
1376 scannerOwners.put(s, user.getShortName());
1377 }
1378 return s;
1379 }
1380
1381 @Override
1382 public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c,
1383 final InternalScanner s, final List<Result> result,
1384 final int limit, final boolean hasNext) throws IOException {
1385 requireScannerOwner(s);
1386 return hasNext;
1387 }
1388
1389 @Override
1390 public void preScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
1391 final InternalScanner s) throws IOException {
1392 requireScannerOwner(s);
1393 }
1394
1395 @Override
1396 public void postScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
1397 final InternalScanner s) throws IOException {
1398
1399 scannerOwners.remove(s);
1400 }
1401
1402
1403
1404
1405
1406
1407 private void requireScannerOwner(InternalScanner s)
1408 throws AccessDeniedException {
1409 if (RequestContext.isInRequestContext()) {
1410 String requestUserName = RequestContext.getRequestUserName();
1411 String owner = scannerOwners.get(s);
1412 if (owner != null && !owner.equals(requestUserName)) {
1413 throw new AccessDeniedException("User '"+ requestUserName +"' is not the scanner owner!");
1414 }
1415 }
1416 }
1417
1418
1419
1420
1421
1422
1423
1424 @Override
1425 public void preBulkLoadHFile(ObserverContext<RegionCoprocessorEnvironment> ctx,
1426 List<Pair<byte[], String>> familyPaths) throws IOException {
1427 for(Pair<byte[],String> el : familyPaths) {
1428 requirePermission("preBulkLoadHFile",
1429 ctx.getEnvironment().getRegion().getTableDesc().getTableName(),
1430 el.getFirst(),
1431 null,
1432 Permission.Action.WRITE);
1433 }
1434 }
1435
1436 private AuthResult hasSomeAccess(RegionCoprocessorEnvironment e, String method, Action action) throws IOException {
1437 User requestUser = getActiveUser();
1438 TableName tableName = e.getRegion().getTableDesc().getTableName();
1439 AuthResult authResult = permissionGranted(method, requestUser,
1440 action, e, Collections.EMPTY_MAP);
1441 if (!authResult.isAllowed()) {
1442 for(UserPermission userPerm:
1443 AccessControlLists.getUserTablePermissions(regionEnv.getConfiguration(), tableName)) {
1444 for(Permission.Action userAction: userPerm.getActions()) {
1445 if(userAction.equals(action)) {
1446 return AuthResult.allow(method, "Access allowed", requestUser,
1447 action, tableName, null, null);
1448 }
1449 }
1450 }
1451 }
1452 return authResult;
1453 }
1454
1455
1456
1457
1458
1459
1460
1461
1462 public void prePrepareBulkLoad(RegionCoprocessorEnvironment e) throws IOException {
1463 AuthResult authResult = hasSomeAccess(e, "prePrepareBulkLoad", Action.WRITE);
1464 logResult(authResult);
1465 if (!authResult.isAllowed()) {
1466 throw new AccessDeniedException("Insufficient permissions (table=" +
1467 e.getRegion().getTableDesc().getTableName() + ", action=WRITE)");
1468 }
1469 }
1470
1471
1472
1473
1474
1475
1476
1477
1478 public void preCleanupBulkLoad(RegionCoprocessorEnvironment e) throws IOException {
1479 AuthResult authResult = hasSomeAccess(e, "preCleanupBulkLoad", Action.WRITE);
1480 logResult(authResult);
1481 if (!authResult.isAllowed()) {
1482 throw new AccessDeniedException("Insufficient permissions (table=" +
1483 e.getRegion().getTableDesc().getTableName() + ", action=WRITE)");
1484 }
1485 }
1486
1487
1488
1489 @Override
1490 public Message preEndpointInvocation(ObserverContext<RegionCoprocessorEnvironment> ctx,
1491 Service service, String methodName, Message request) throws IOException {
1492
1493
1494 if (shouldCheckExecPermissions && !(service instanceof AccessControlService)) {
1495 requirePermission("invoke(" + service.getDescriptorForType().getName() + "." +
1496 methodName + ")",
1497 getTableName(ctx.getEnvironment()), null, null,
1498 Action.EXEC);
1499 }
1500 return request;
1501 }
1502
1503 @Override
1504 public void postEndpointInvocation(ObserverContext<RegionCoprocessorEnvironment> ctx,
1505 Service service, String methodName, Message request, Message.Builder responseBuilder)
1506 throws IOException { }
1507
1508
1509
1510 @Override
1511 public void grant(RpcController controller,
1512 AccessControlProtos.GrantRequest request,
1513 RpcCallback<AccessControlProtos.GrantResponse> done) {
1514 UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission());
1515 AccessControlProtos.GrantResponse response = null;
1516 try {
1517
1518 if (aclRegion) {
1519 if (!initialized) {
1520 throw new CoprocessorException("AccessController not yet initialized");
1521 }
1522 if (LOG.isDebugEnabled()) {
1523 LOG.debug("Received request to grant access permission " + perm.toString());
1524 }
1525
1526 switch(request.getUserPermission().getPermission().getType()) {
1527 case Global :
1528 case Table :
1529 requirePermission("grant", perm.getTableName(), perm.getFamily(),
1530 perm.getQualifier(), Action.ADMIN);
1531 break;
1532 case Namespace :
1533 requireGlobalPermission("grant", Action.ADMIN, perm.getNamespace());
1534 }
1535
1536 AccessControlLists.addUserPermission(regionEnv.getConfiguration(), perm);
1537 if (AUDITLOG.isTraceEnabled()) {
1538
1539 AUDITLOG.trace("Granted permission " + perm.toString());
1540 }
1541 } else {
1542 throw new CoprocessorException(AccessController.class, "This method "
1543 + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
1544 }
1545 response = AccessControlProtos.GrantResponse.getDefaultInstance();
1546 } catch (IOException ioe) {
1547
1548 ResponseConverter.setControllerException(controller, ioe);
1549 }
1550 done.run(response);
1551 }
1552
1553 @Override
1554 public void revoke(RpcController controller,
1555 AccessControlProtos.RevokeRequest request,
1556 RpcCallback<AccessControlProtos.RevokeResponse> done) {
1557 UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission());
1558 AccessControlProtos.RevokeResponse response = null;
1559 try {
1560
1561 if (aclRegion) {
1562 if (!initialized) {
1563 throw new CoprocessorException("AccessController not yet initialized");
1564 }
1565 if (LOG.isDebugEnabled()) {
1566 LOG.debug("Received request to revoke access permission " + perm.toString());
1567 }
1568
1569 switch(request.getUserPermission().getPermission().getType()) {
1570 case Global :
1571 case Table :
1572 requirePermission("revoke", perm.getTableName(), perm.getFamily(),
1573 perm.getQualifier(), Action.ADMIN);
1574 break;
1575 case Namespace :
1576 requireGlobalPermission("revoke", Action.ADMIN, perm.getNamespace());
1577 }
1578
1579 AccessControlLists.removeUserPermission(regionEnv.getConfiguration(), perm);
1580 if (AUDITLOG.isTraceEnabled()) {
1581
1582 AUDITLOG.trace("Revoked permission " + perm.toString());
1583 }
1584 } else {
1585 throw new CoprocessorException(AccessController.class, "This method "
1586 + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
1587 }
1588 response = AccessControlProtos.RevokeResponse.getDefaultInstance();
1589 } catch (IOException ioe) {
1590
1591 ResponseConverter.setControllerException(controller, ioe);
1592 }
1593 done.run(response);
1594 }
1595
1596 @Override
1597 public void getUserPermissions(RpcController controller,
1598 AccessControlProtos.GetUserPermissionsRequest request,
1599 RpcCallback<AccessControlProtos.GetUserPermissionsResponse> done) {
1600 AccessControlProtos.GetUserPermissionsResponse response = null;
1601 try {
1602
1603 if (aclRegion) {
1604 if (!initialized) {
1605 throw new CoprocessorException("AccessController not yet initialized");
1606 }
1607 List<UserPermission> perms = null;
1608 if(request.getType() == AccessControlProtos.Permission.Type.Table) {
1609 TableName table = null;
1610 if (request.hasTableName()) {
1611 table = ProtobufUtil.toTableName(request.getTableName());
1612 }
1613 requirePermission("userPermissions", table, null, null, Action.ADMIN);
1614
1615 perms = AccessControlLists.getUserTablePermissions(
1616 regionEnv.getConfiguration(), table);
1617 } else if (request.getType() == AccessControlProtos.Permission.Type.Namespace) {
1618 perms = AccessControlLists.getUserNamespacePermissions(
1619 regionEnv.getConfiguration(), request.getNamespaceName().toStringUtf8());
1620 } else {
1621 perms = AccessControlLists.getUserPermissions(
1622 regionEnv.getConfiguration(), null);
1623 }
1624 response = ResponseConverter.buildGetUserPermissionsResponse(perms);
1625 } else {
1626 throw new CoprocessorException(AccessController.class, "This method "
1627 + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
1628 }
1629 } catch (IOException ioe) {
1630
1631 ResponseConverter.setControllerException(controller, ioe);
1632 }
1633 done.run(response);
1634 }
1635
1636 @Override
1637 public void checkPermissions(RpcController controller,
1638 AccessControlProtos.CheckPermissionsRequest request,
1639 RpcCallback<AccessControlProtos.CheckPermissionsResponse> done) {
1640 Permission[] permissions = new Permission[request.getPermissionCount()];
1641 for (int i=0; i < request.getPermissionCount(); i++) {
1642 permissions[i] = ProtobufUtil.toPermission(request.getPermission(i));
1643 }
1644 AccessControlProtos.CheckPermissionsResponse response = null;
1645 try {
1646 TableName tableName = regionEnv.getRegion().getTableDesc().getTableName();
1647 for (Permission permission : permissions) {
1648 if (permission instanceof TablePermission) {
1649 TablePermission tperm = (TablePermission) permission;
1650 for (Permission.Action action : permission.getActions()) {
1651 if (!tperm.getTableName().equals(tableName)) {
1652 throw new CoprocessorException(AccessController.class, String.format("This method "
1653 + "can only execute at the table specified in TablePermission. " +
1654 "Table of the region:%s , requested table:%s", tableName,
1655 tperm.getTableName()));
1656 }
1657
1658 Map<byte[], Set<byte[]>> familyMap = new TreeMap<byte[], Set<byte[]>>(Bytes.BYTES_COMPARATOR);
1659 if (tperm.getFamily() != null) {
1660 if (tperm.getQualifier() != null) {
1661 Set<byte[]> qualifiers = Sets.newTreeSet(Bytes.BYTES_COMPARATOR);
1662 qualifiers.add(tperm.getQualifier());
1663 familyMap.put(tperm.getFamily(), qualifiers);
1664 } else {
1665 familyMap.put(tperm.getFamily(), null);
1666 }
1667 }
1668
1669 requirePermission("checkPermissions", action, regionEnv, familyMap);
1670 }
1671
1672 } else {
1673 for (Permission.Action action : permission.getActions()) {
1674 requirePermission("checkPermissions", action);
1675 }
1676 }
1677 }
1678 response = AccessControlProtos.CheckPermissionsResponse.getDefaultInstance();
1679 } catch (IOException ioe) {
1680 ResponseConverter.setControllerException(controller, ioe);
1681 }
1682 done.run(response);
1683 }
1684
1685 @Override
1686 public Service getService() {
1687 return AccessControlProtos.AccessControlService.newReflectiveService(this);
1688 }
1689
1690 private HRegion getRegion(RegionCoprocessorEnvironment e) {
1691 return e.getRegion();
1692 }
1693
1694 private TableName getTableName(RegionCoprocessorEnvironment e) {
1695 HRegion region = e.getRegion();
1696 TableName tableName = null;
1697
1698 if (region != null) {
1699 HRegionInfo regionInfo = region.getRegionInfo();
1700 if (regionInfo != null) {
1701 tableName = regionInfo.getTable();
1702 }
1703 }
1704 return tableName;
1705 }
1706
1707
1708 @Override
1709 public void preClose(ObserverContext<RegionCoprocessorEnvironment> e, boolean abortRequested)
1710 throws IOException {
1711 requirePermission("preClose", Action.ADMIN);
1712 }
1713
1714 private void isSystemOrSuperUser(Configuration conf) throws IOException {
1715 User user = userProvider.getCurrent();
1716 if (user == null) {
1717 throw new IOException("Unable to obtain the current user, " +
1718 "authorization checks for internal operations will not work correctly!");
1719 }
1720
1721 String currentUser = user.getShortName();
1722 List<String> superusers = Lists.asList(currentUser, conf.getStrings(
1723 AccessControlLists.SUPERUSER_CONF_KEY, new String[0]));
1724
1725 User activeUser = getActiveUser();
1726 if (!(superusers.contains(activeUser.getShortName()))) {
1727 throw new AccessDeniedException("User '" + (user != null ? user.getShortName() : "null") +
1728 "is not system or super user.");
1729 }
1730 }
1731
1732 @Override
1733 public void preStopRegionServer(
1734 ObserverContext<RegionServerCoprocessorEnvironment> env)
1735 throws IOException {
1736 requirePermission("preStopRegionServer", Permission.Action.ADMIN);
1737 }
1738
1739 private Map<byte[], ? extends Collection<byte[]>> makeFamilyMap(byte[] family,
1740 byte[] qualifier) {
1741 if (family == null) {
1742 return null;
1743 }
1744
1745 Map<byte[], Collection<byte[]>> familyMap = new TreeMap<byte[], Collection<byte[]>>(Bytes.BYTES_COMPARATOR);
1746 familyMap.put(family, qualifier != null ? ImmutableSet.of(qualifier) : null);
1747 return familyMap;
1748 }
1749
1750 @Override
1751 public void preGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
1752 List<TableName> tableNamesList,
1753 List<HTableDescriptor> descriptors) throws IOException {
1754
1755
1756 if (tableNamesList == null || tableNamesList.isEmpty()) {
1757 requireGlobalPermission("getTableDescriptors", Permission.Action.ADMIN, null, null);
1758 }
1759
1760
1761 else {
1762 MasterServices masterServices = ctx.getEnvironment().getMasterServices();
1763 for (TableName tableName: tableNamesList) {
1764
1765 try {
1766 masterServices.checkTableModifiable(tableName);
1767 } catch (TableNotFoundException ex) {
1768
1769 continue;
1770 } catch (TableNotDisabledException ex) {
1771
1772 }
1773 requirePermission("getTableDescriptors", tableName, null, null,
1774 Permission.Action.ADMIN, Permission.Action.CREATE);
1775 }
1776 }
1777 }
1778
1779 @Override
1780 public void postGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
1781 List<HTableDescriptor> descriptors) throws IOException {
1782 }
1783
1784 @Override
1785 public void preMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx, HRegion regionA,
1786 HRegion regionB) throws IOException {
1787 requirePermission("mergeRegions", regionA.getTableDesc().getTableName(), null, null,
1788 Action.ADMIN);
1789 }
1790
1791 @Override
1792 public void postMerge(ObserverContext<RegionServerCoprocessorEnvironment> c, HRegion regionA,
1793 HRegion regionB, HRegion mergedRegion) throws IOException { }
1794
1795 @Override
1796 public void preMergeCommit(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
1797 HRegion regionA, HRegion regionB, List<Mutation> metaEntries) throws IOException { }
1798
1799 @Override
1800 public void postMergeCommit(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
1801 HRegion regionA, HRegion regionB, HRegion mergedRegion) throws IOException { }
1802
1803 @Override
1804 public void preRollBackMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
1805 HRegion regionA, HRegion regionB) throws IOException { }
1806
1807 @Override
1808 public void postRollBackMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
1809 HRegion regionA, HRegion regionB) throws IOException { }
1810 }