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.security.PrivilegedExceptionAction;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.Set;
30 import java.util.TreeMap;
31 import java.util.TreeSet;
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.CellScanner;
38 import org.apache.hadoop.hbase.CellUtil;
39 import org.apache.hadoop.hbase.CompoundConfiguration;
40 import org.apache.hadoop.hbase.CoprocessorEnvironment;
41 import org.apache.hadoop.hbase.DoNotRetryIOException;
42 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
43 import org.apache.hadoop.hbase.HColumnDescriptor;
44 import org.apache.hadoop.hbase.HConstants;
45 import org.apache.hadoop.hbase.HRegionInfo;
46 import org.apache.hadoop.hbase.HTableDescriptor;
47 import org.apache.hadoop.hbase.KeyValue;
48 import org.apache.hadoop.hbase.KeyValue.Type;
49 import org.apache.hadoop.hbase.MetaTableAccessor;
50 import org.apache.hadoop.hbase.NamespaceDescriptor;
51 import org.apache.hadoop.hbase.ProcedureInfo;
52 import org.apache.hadoop.hbase.ServerName;
53 import org.apache.hadoop.hbase.TableName;
54 import org.apache.hadoop.hbase.Tag;
55 import org.apache.hadoop.hbase.TagRewriteCell;
56 import org.apache.hadoop.hbase.classification.InterfaceAudience;
57 import org.apache.hadoop.hbase.client.Admin;
58 import org.apache.hadoop.hbase.client.Append;
59 import org.apache.hadoop.hbase.client.Delete;
60 import org.apache.hadoop.hbase.client.Durability;
61 import org.apache.hadoop.hbase.client.Get;
62 import org.apache.hadoop.hbase.client.Increment;
63 import org.apache.hadoop.hbase.client.Mutation;
64 import org.apache.hadoop.hbase.client.Put;
65 import org.apache.hadoop.hbase.client.Query;
66 import org.apache.hadoop.hbase.client.Result;
67 import org.apache.hadoop.hbase.client.Scan;
68 import org.apache.hadoop.hbase.client.Table;
69 import org.apache.hadoop.hbase.coprocessor.BaseMasterAndRegionObserver;
70 import org.apache.hadoop.hbase.coprocessor.BulkLoadObserver;
71 import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
72 import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
73 import org.apache.hadoop.hbase.coprocessor.EndpointObserver;
74 import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
75 import org.apache.hadoop.hbase.coprocessor.ObserverContext;
76 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
77 import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
78 import org.apache.hadoop.hbase.coprocessor.RegionServerObserver;
79 import org.apache.hadoop.hbase.filter.ByteArrayComparable;
80 import org.apache.hadoop.hbase.filter.CompareFilter;
81 import org.apache.hadoop.hbase.filter.Filter;
82 import org.apache.hadoop.hbase.filter.FilterList;
83 import org.apache.hadoop.hbase.io.hfile.HFile;
84 import org.apache.hadoop.hbase.ipc.RpcServer;
85 import org.apache.hadoop.hbase.master.MasterServices;
86 import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
87 import org.apache.hadoop.hbase.net.Address;
88 import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
89 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
90 import org.apache.hadoop.hbase.protobuf.ResponseConverter;
91 import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos;
92 import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
93 import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.WALEntry;
94 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
95 import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas;
96 import org.apache.hadoop.hbase.protobuf.generated.SecureBulkLoadProtos.CleanupBulkLoadRequest;
97 import org.apache.hadoop.hbase.protobuf.generated.SecureBulkLoadProtos.PrepareBulkLoadRequest;
98 import org.apache.hadoop.hbase.regionserver.InternalScanner;
99 import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress;
100 import org.apache.hadoop.hbase.regionserver.Region;
101 import org.apache.hadoop.hbase.regionserver.RegionScanner;
102 import org.apache.hadoop.hbase.regionserver.ScanType;
103 import org.apache.hadoop.hbase.regionserver.ScannerContext;
104 import org.apache.hadoop.hbase.regionserver.Store;
105 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
106 import org.apache.hadoop.hbase.replication.ReplicationEndpoint;
107 import org.apache.hadoop.hbase.security.AccessDeniedException;
108 import org.apache.hadoop.hbase.security.Superusers;
109 import org.apache.hadoop.hbase.security.User;
110 import org.apache.hadoop.hbase.security.UserProvider;
111 import org.apache.hadoop.hbase.security.access.Permission.Action;
112 import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
113 import org.apache.hadoop.hbase.util.ByteRange;
114 import org.apache.hadoop.hbase.util.Bytes;
115 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
116 import org.apache.hadoop.hbase.util.Pair;
117 import org.apache.hadoop.hbase.util.SimpleMutableByteRange;
118 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
119
120 import com.google.common.collect.ImmutableSet;
121 import com.google.common.collect.ListMultimap;
122 import com.google.common.collect.Lists;
123 import com.google.common.collect.MapMaker;
124 import com.google.common.collect.Maps;
125 import com.google.common.collect.Sets;
126 import com.google.protobuf.Message;
127 import com.google.protobuf.RpcCallback;
128 import com.google.protobuf.RpcController;
129 import com.google.protobuf.Service;
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
165 public class AccessController extends BaseMasterAndRegionObserver
166 implements RegionServerObserver,
167 AccessControlService.Interface, CoprocessorService, EndpointObserver, BulkLoadObserver {
168
169 private static final Log LOG = LogFactory.getLog(AccessController.class);
170
171 private static final Log AUDITLOG =
172 LogFactory.getLog("SecurityLogger."+AccessController.class.getName());
173 private static final String CHECK_COVERING_PERM = "check_covering_perm";
174 private static final String TAG_CHECK_PASSED = "tag_check_passed";
175 private static final byte[] TRUE = Bytes.toBytes(true);
176
177 private AccessChecker accessChecker;
178
179
180 private boolean aclRegion = false;
181
182
183
184 private RegionCoprocessorEnvironment regionEnv;
185
186
187 private Map<InternalScanner,String> scannerOwners =
188 new MapMaker().weakKeys().makeMap();
189
190 private Map<TableName, List<UserPermission>> tableAcls;
191
192
193 private UserProvider userProvider;
194
195
196
197 private boolean authorizationEnabled;
198
199
200 private boolean cellFeaturesEnabled;
201
202
203 private boolean shouldCheckExecPermission;
204
205
206
207 private boolean compatibleEarlyTermination;
208
209
210 private volatile boolean initialized = false;
211
212
213 private volatile boolean aclTabAvailable = false;
214
215 public static boolean isAuthorizationSupported(Configuration conf) {
216 return AccessChecker.isAuthorizationSupported(conf);
217 }
218
219 public static boolean isCellAuthorizationSupported(Configuration conf) {
220 return AccessChecker.isAuthorizationSupported(conf) &&
221 (HFile.getFormatVersion(conf) >= HFile.MIN_FORMAT_VERSION_WITH_TAGS);
222 }
223
224 public Region getRegion() {
225 return regionEnv != null ? regionEnv.getRegion() : null;
226 }
227
228 public TableAuthManager getAuthManager() {
229 return accessChecker.getAuthManager();
230 }
231
232 private void initialize(RegionCoprocessorEnvironment e) throws IOException {
233 final Region region = e.getRegion();
234 Configuration conf = e.getConfiguration();
235 Map<byte[], ListMultimap<String,TablePermission>> tables =
236 AccessControlLists.loadAll(region);
237
238
239 for (Map.Entry<byte[], ListMultimap<String,TablePermission>> t:
240 tables.entrySet()) {
241 byte[] entry = t.getKey();
242 ListMultimap<String,TablePermission> perms = t.getValue();
243 byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, conf);
244 getAuthManager().getZKPermissionWatcher().writeToZookeeper(entry, serialized);
245 }
246 initialized = true;
247 }
248
249
250
251
252
253
254 private void updateACL(RegionCoprocessorEnvironment e,
255 final Map<byte[], List<Cell>> familyMap) {
256 Set<byte[]> entries =
257 new TreeSet<byte[]>(Bytes.BYTES_RAWCOMPARATOR);
258 for (Map.Entry<byte[], List<Cell>> f : familyMap.entrySet()) {
259 List<Cell> cells = f.getValue();
260 for (Cell cell: cells) {
261 if (Bytes.equals(cell.getFamilyArray(), cell.getFamilyOffset(),
262 cell.getFamilyLength(), AccessControlLists.ACL_LIST_FAMILY, 0,
263 AccessControlLists.ACL_LIST_FAMILY.length)) {
264 entries.add(CellUtil.cloneRow(cell));
265 }
266 }
267 }
268 ZKPermissionWatcher zkw = getAuthManager().getZKPermissionWatcher();
269 Configuration conf = regionEnv.getConfiguration();
270 for (byte[] entry: entries) {
271 try {
272 try (Table t = regionEnv.getTable(AccessControlLists.ACL_TABLE_NAME)) {
273 ListMultimap<String,TablePermission> perms =
274 AccessControlLists.getPermissions(conf, entry, t);
275 byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, conf);
276 zkw.writeToZookeeper(entry, serialized);
277 }
278 } catch (IOException ex) {
279 LOG.error("Failed updating permissions mirror for '" + Bytes.toString(entry) + "'",
280 ex);
281 }
282 }
283 }
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299 AuthResult permissionGranted(String request, User user, Action permRequest,
300 RegionCoprocessorEnvironment e,
301 Map<byte [], ? extends Collection<?>> families) {
302 HRegionInfo hri = e.getRegion().getRegionInfo();
303 TableName tableName = hri.getTable();
304
305
306
307 if (hri.isMetaRegion()) {
308 if (permRequest == Action.READ) {
309 return AuthResult.allow(request, "All users allowed", user,
310 permRequest, tableName, families);
311 }
312 }
313
314 if (user == null) {
315 return AuthResult.deny(request, "No user associated with request!", null,
316 permRequest, tableName, families);
317 }
318
319
320 if (getAuthManager().authorize(user, tableName, (byte[])null, permRequest)) {
321 return AuthResult.allow(request, "Table permission granted", user,
322 permRequest, tableName, families);
323 }
324
325
326 if (families != null && families.size() > 0) {
327
328 for (Map.Entry<byte [], ? extends Collection<?>> family : families.entrySet()) {
329
330 if (getAuthManager().authorize(user, tableName, family.getKey(),
331 permRequest)) {
332 continue;
333 }
334
335
336 if ((family.getValue() != null) && (family.getValue().size() > 0)) {
337 if (family.getValue() instanceof Set) {
338
339 Set<byte[]> familySet = (Set<byte[]>)family.getValue();
340 for (byte[] qualifier : familySet) {
341 if (!getAuthManager().authorize(user, tableName, family.getKey(),
342 qualifier, permRequest)) {
343 return AuthResult.deny(request, "Failed qualifier check", user,
344 permRequest, tableName, makeFamilyMap(family.getKey(), qualifier));
345 }
346 }
347 } else if (family.getValue() instanceof List) {
348 List<KeyValue> kvList = (List<KeyValue>)family.getValue();
349 for (KeyValue kv : kvList) {
350 if (!getAuthManager().authorize(user, tableName, family.getKey(),
351 kv.getQualifier(), permRequest)) {
352 return AuthResult.deny(request, "Failed qualifier check", user,
353 permRequest, tableName, makeFamilyMap(family.getKey(), kv.getQualifier()));
354 }
355 }
356 }
357 } else {
358
359 return AuthResult.deny(request, "Failed family check", user, permRequest,
360 tableName, makeFamilyMap(family.getKey(), null));
361 }
362 }
363
364
365 return AuthResult.allow(request, "All family checks passed", user, permRequest,
366 tableName, families);
367 }
368
369
370 return AuthResult.deny(request, "No families to check and table permission failed",
371 user, permRequest, tableName, families);
372 }
373
374
375
376
377
378
379
380
381
382
383
384
385 AuthResult permissionGranted(OpType opType, User user, RegionCoprocessorEnvironment e,
386 Map<byte [], ? extends Collection<?>> families, Action... actions) {
387 AuthResult result = null;
388 for (Action action: actions) {
389 result = permissionGranted(opType.toString(), user, action, e, families);
390 if (!result.isAllowed()) {
391 return result;
392 }
393 }
394 return result;
395 }
396
397
398
399
400
401
402 private User getActiveUser(ObserverContext ctx) throws IOException {
403 User user = ctx.getCaller();
404 if (user == null) {
405
406 user = userProvider.getCurrent();
407 }
408 return user;
409 }
410
411
412
413
414
415
416
417
418
419
420
421
422 public void requirePermission(User user, String request, TableName tableName, byte[] family,
423 byte[] qualifier, Action... permissions) throws IOException {
424 accessChecker.requirePermission(user, request,
425 tableName, family, qualifier, permissions);
426 }
427
428
429
430
431
432
433
434
435
436
437
438
439 public void requireTablePermission(User user, String request, TableName tableName, byte[] family,
440 byte[] qualifier, Action... permissions) throws IOException {
441 accessChecker.requireTablePermission(user, request,
442 tableName, family, qualifier, permissions);
443 }
444
445
446
447
448
449
450
451
452
453
454 public void requireAccess(User user, String request, TableName tableName,
455 Action... permissions) throws IOException {
456 accessChecker.requireAccess(user, request, tableName, permissions);
457 }
458
459
460
461
462
463
464
465
466
467 public void requirePermission(User user, String request, Action perm) throws IOException {
468 accessChecker.requirePermission(user, request, perm);
469 }
470
471
472
473
474
475
476
477
478
479
480
481 public void requireGlobalPermission(User user, String request, Action perm, TableName tableName,
482 Map<byte[], ? extends Collection<byte[]>> familyMap) throws IOException {
483 accessChecker.requireGlobalPermission(user, request, perm, tableName, familyMap);
484 }
485
486
487
488
489
490
491
492
493
494
495 public void requireGlobalPermission(User user, String request, Action perm,
496 String namespace) throws IOException {
497 accessChecker.requireGlobalPermission(user, request, perm, namespace);
498 }
499
500
501
502
503
504
505
506
507 public void requireNamespacePermission(User user, String request, String namespace,
508 Action... permissions) throws IOException {
509 accessChecker.requireNamespacePermission(user, request, namespace, permissions);
510 }
511
512
513
514
515
516
517
518
519
520
521 public void requireNamespacePermission(User user, String request, String namespace,
522 TableName tableName, Map<byte[], ? extends Collection<byte[]>> familyMap,
523 Action... permissions) throws IOException {
524 accessChecker.requireNamespacePermission(user, request, namespace, tableName, familyMap,
525 permissions);
526 }
527
528
529
530
531
532 private boolean hasFamilyQualifierPermission(User user,
533 Action perm,
534 RegionCoprocessorEnvironment env,
535 Map<byte[], ? extends Collection<byte[]>> familyMap)
536 throws IOException {
537 HRegionInfo hri = env.getRegion().getRegionInfo();
538 TableName tableName = hri.getTable();
539
540 if (user == null) {
541 return false;
542 }
543
544 if (familyMap != null && familyMap.size() > 0) {
545
546 for (Map.Entry<byte[], ? extends Collection<byte[]>> family :
547 familyMap.entrySet()) {
548 if (family.getValue() != null && !family.getValue().isEmpty()) {
549 for (byte[] qualifier : family.getValue()) {
550 if (getAuthManager().matchPermission(user, tableName,
551 family.getKey(), qualifier, perm)) {
552 return true;
553 }
554 }
555 } else {
556 if (getAuthManager().matchPermission(user, tableName, family.getKey(),
557 perm)) {
558 return true;
559 }
560 }
561 }
562 } else if (LOG.isDebugEnabled()) {
563 LOG.debug("Empty family map passed for permission check");
564 }
565
566 return false;
567 }
568
569 private enum OpType {
570 GET_CLOSEST_ROW_BEFORE("getClosestRowBefore"),
571 GET("get"),
572 EXISTS("exists"),
573 SCAN("scan"),
574 PUT("put"),
575 DELETE("delete"),
576 CHECK_AND_PUT("checkAndPut"),
577 CHECK_AND_DELETE("checkAndDelete"),
578 INCREMENT_COLUMN_VALUE("incrementColumnValue"),
579 APPEND("append"),
580 INCREMENT("increment");
581
582 private String type;
583
584 private OpType(String type) {
585 this.type = type;
586 }
587
588 @Override
589 public String toString() {
590 return type;
591 }
592 }
593
594
595
596
597
598
599 private boolean checkCoveringPermission(User user, OpType request, RegionCoprocessorEnvironment e,
600 byte[] row, Map<byte[], ? extends Collection<?>> familyMap, long opTs, Action... actions)
601 throws IOException {
602 if (!cellFeaturesEnabled) {
603 return false;
604 }
605 long cellGrants = 0;
606 long latestCellTs = 0;
607 Get get = new Get(row);
608
609
610
611
612
613
614
615 boolean considerCellTs = (request == OpType.PUT || request == OpType.DELETE);
616 if (considerCellTs) {
617 get.setMaxVersions();
618 } else {
619 get.setMaxVersions(1);
620 }
621 boolean diffCellTsFromOpTs = false;
622 for (Map.Entry<byte[], ? extends Collection<?>> entry: familyMap.entrySet()) {
623 byte[] col = entry.getKey();
624
625
626 if (entry.getValue() instanceof Set) {
627 Set<byte[]> set = (Set<byte[]>)entry.getValue();
628 if (set == null || set.isEmpty()) {
629 get.addFamily(col);
630 } else {
631 for (byte[] qual: set) {
632 get.addColumn(col, qual);
633 }
634 }
635 } else if (entry.getValue() instanceof List) {
636 List<Cell> list = (List<Cell>)entry.getValue();
637 if (list == null || list.isEmpty()) {
638 get.addFamily(col);
639 } else {
640
641 for (Cell cell : list) {
642 if (cell.getQualifierLength() == 0
643 && (cell.getTypeByte() == Type.DeleteFamily.getCode()
644 || cell.getTypeByte() == Type.DeleteFamilyVersion.getCode())) {
645 get.addFamily(col);
646 } else {
647 get.addColumn(col, CellUtil.cloneQualifier(cell));
648 }
649 if (considerCellTs) {
650 long cellTs = cell.getTimestamp();
651 latestCellTs = Math.max(latestCellTs, cellTs);
652 diffCellTsFromOpTs = diffCellTsFromOpTs || (opTs != cellTs);
653 }
654 }
655 }
656 } else if (entry.getValue() == null) {
657 get.addFamily(col);
658 } else {
659 throw new RuntimeException("Unhandled collection type " +
660 entry.getValue().getClass().getName());
661 }
662 }
663
664
665
666
667
668
669 long latestTs = Math.max(opTs, latestCellTs);
670 if (latestTs == 0 || latestTs == HConstants.LATEST_TIMESTAMP) {
671 latestTs = EnvironmentEdgeManager.currentTime();
672 }
673 get.setTimeRange(0, latestTs + 1);
674
675
676
677 if (!diffCellTsFromOpTs && request == OpType.PUT) {
678 get.setMaxVersions(1);
679 }
680 if (LOG.isTraceEnabled()) {
681 LOG.trace("Scanning for cells with " + get);
682 }
683
684
685
686 Map<ByteRange, List<Cell>> familyMap1 = new HashMap<ByteRange, List<Cell>>();
687 for (Entry<byte[], ? extends Collection<?>> entry : familyMap.entrySet()) {
688 if (entry.getValue() instanceof List) {
689 familyMap1.put(new SimpleMutableByteRange(entry.getKey()), (List<Cell>) entry.getValue());
690 }
691 }
692 RegionScanner scanner = getRegion(e).getScanner(new Scan(get));
693 List<Cell> cells = Lists.newArrayList();
694 Cell prevCell = null;
695 ByteRange curFam = new SimpleMutableByteRange();
696 boolean curColAllVersions = (request == OpType.DELETE);
697 long curColCheckTs = opTs;
698 boolean foundColumn = false;
699 try {
700 boolean more = false;
701 ScannerContext scannerContext = ScannerContext.newBuilder().setBatchLimit(1).build();
702
703 do {
704 cells.clear();
705
706 more = scanner.next(cells, scannerContext);
707 for (Cell cell: cells) {
708 if (LOG.isTraceEnabled()) {
709 LOG.trace("Found cell " + cell);
710 }
711 boolean colChange = prevCell == null || !CellUtil.matchingColumn(prevCell, cell);
712 if (colChange) foundColumn = false;
713 prevCell = cell;
714 if (!curColAllVersions && foundColumn) {
715 continue;
716 }
717 if (colChange && considerCellTs) {
718 curFam.set(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
719 List<Cell> cols = familyMap1.get(curFam);
720 for (Cell col : cols) {
721
722
723
724 if ((col.getQualifierLength() == 0 && request == OpType.DELETE)
725 || CellUtil.matchingQualifier(cell, col)) {
726 byte type = col.getTypeByte();
727 if (considerCellTs) {
728 curColCheckTs = col.getTimestamp();
729 }
730
731
732
733
734 curColAllVersions = (KeyValue.Type.DeleteColumn.getCode() == type)
735 || (KeyValue.Type.DeleteFamily.getCode() == type);
736 break;
737 }
738 }
739 }
740 if (cell.getTimestamp() > curColCheckTs) {
741
742 continue;
743 }
744 foundColumn = true;
745 for (Action action: actions) {
746
747 if (!getAuthManager().authorize(user, getTableName(e), cell, action)) {
748
749 return false;
750 }
751 }
752 cellGrants++;
753 }
754 } while (more);
755 } catch (AccessDeniedException ex) {
756 throw ex;
757 } catch (IOException ex) {
758 LOG.error("Exception while getting cells to calculate covering permission", ex);
759 } finally {
760 scanner.close();
761 }
762
763
764
765 return cellGrants > 0;
766 }
767
768 private static void addCellPermissions(final byte[] perms, Map<byte[], List<Cell>> familyMap) {
769
770
771 for (Map.Entry<byte[], List<Cell>> e: familyMap.entrySet()) {
772 List<Cell> newCells = Lists.newArrayList();
773 for (Cell cell: e.getValue()) {
774
775 List<Tag> tags = Lists.newArrayList(new Tag(AccessControlLists.ACL_TAG_TYPE, perms));
776 if (cell.getTagsLength() > 0) {
777 Iterator<Tag> tagIterator = CellUtil.tagsIterator(cell.getTagsArray(),
778 cell.getTagsOffset(), cell.getTagsLength());
779 while (tagIterator.hasNext()) {
780 tags.add(tagIterator.next());
781 }
782 }
783 newCells.add(new TagRewriteCell(cell, Tag.fromList(tags)));
784 }
785
786 e.setValue(newCells);
787 }
788 }
789
790
791
792 private void checkForReservedTagPresence(User user, Mutation m) throws IOException {
793
794 if (!authorizationEnabled) {
795 m.setAttribute(TAG_CHECK_PASSED, TRUE);
796 return;
797 }
798
799 if (Superusers.isSuperUser(user)) {
800 m.setAttribute(TAG_CHECK_PASSED, TRUE);
801 return;
802 }
803
804 if (m.getAttribute(TAG_CHECK_PASSED) != null) {
805 return;
806 }
807 for (CellScanner cellScanner = m.cellScanner(); cellScanner.advance();) {
808 Cell cell = cellScanner.current();
809 if (cell.getTagsLength() > 0) {
810 Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
811 cell.getTagsLength());
812 while (tagsItr.hasNext()) {
813 if (tagsItr.next().getType() == AccessControlLists.ACL_TAG_TYPE) {
814 throw new AccessDeniedException("Mutation contains cell with reserved type tag");
815 }
816 }
817 }
818 }
819 m.setAttribute(TAG_CHECK_PASSED, TRUE);
820 }
821
822
823 @Override
824 public void start(CoprocessorEnvironment env) throws IOException {
825 CompoundConfiguration conf = new CompoundConfiguration();
826 conf.add(env.getConfiguration());
827
828 authorizationEnabled = isAuthorizationSupported(conf);
829 if (!authorizationEnabled) {
830 LOG.warn("The AccessController has been loaded with authorization checks disabled.");
831 }
832
833 shouldCheckExecPermission = conf.getBoolean(AccessControlConstants.EXEC_PERMISSION_CHECKS_KEY,
834 AccessControlConstants.DEFAULT_EXEC_PERMISSION_CHECKS);
835
836 cellFeaturesEnabled = (HFile.getFormatVersion(conf) >= HFile.MIN_FORMAT_VERSION_WITH_TAGS);
837 if (!cellFeaturesEnabled) {
838 LOG.info("A minimum HFile version of " + HFile.MIN_FORMAT_VERSION_WITH_TAGS
839 + " is required to persist cell ACLs. Consider setting " + HFile.FORMAT_VERSION_KEY
840 + " accordingly.");
841 }
842
843 ZooKeeperWatcher zk = null;
844 if (env instanceof MasterCoprocessorEnvironment) {
845
846 MasterCoprocessorEnvironment mEnv = (MasterCoprocessorEnvironment) env;
847 zk = mEnv.getMasterServices().getZooKeeper();
848 } else if (env instanceof RegionServerCoprocessorEnvironment) {
849 RegionServerCoprocessorEnvironment rsEnv = (RegionServerCoprocessorEnvironment) env;
850 zk = rsEnv.getRegionServerServices().getZooKeeper();
851 } else if (env instanceof RegionCoprocessorEnvironment) {
852
853 regionEnv = (RegionCoprocessorEnvironment) env;
854 conf.addStringMap(regionEnv.getRegion().getTableDesc().getConfiguration());
855 zk = regionEnv.getRegionServerServices().getZooKeeper();
856 compatibleEarlyTermination = conf.getBoolean(AccessControlConstants.CF_ATTRIBUTE_EARLY_OUT,
857 AccessControlConstants.DEFAULT_ATTRIBUTE_EARLY_OUT);
858 }
859
860
861 this.userProvider = UserProvider.instantiate(env.getConfiguration());
862
863 accessChecker = new AccessChecker(env.getConfiguration(), zk);
864 tableAcls = new MapMaker().weakValues().makeMap();
865 }
866
867 @Override
868 public void stop(CoprocessorEnvironment env) {
869 accessChecker.stop();
870 }
871
872 @Override
873 public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
874 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
875 Set<byte[]> families = desc.getFamiliesKeys();
876 Map<byte[], Set<byte[]>> familyMap = new TreeMap<byte[], Set<byte[]>>(Bytes.BYTES_COMPARATOR);
877 for (byte[] family: families) {
878 familyMap.put(family, null);
879 }
880 requireNamespacePermission(getActiveUser(c), "createTable",
881 desc.getTableName().getNamespaceAsString(), desc.getTableName(), familyMap, Action.CREATE);
882 }
883
884 @Override
885 public void postCreateTableHandler(final ObserverContext<MasterCoprocessorEnvironment> c,
886 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902 if (AccessControlLists.isAclTable(desc)) {
903 this.aclTabAvailable = true;
904 } else if (!(TableName.NAMESPACE_TABLE_NAME.equals(desc.getTableName()))) {
905 if (!aclTabAvailable) {
906 LOG.warn("Not adding owner permission for table " + desc.getTableName() + ". "
907 + AccessControlLists.ACL_TABLE_NAME + " is not yet created. "
908 + getClass().getSimpleName() + " should be configured as the first Coprocessor");
909 } else {
910 String owner = desc.getOwnerString();
911
912 if (owner == null)
913 owner = getActiveUser(c).getShortName();
914 final UserPermission userperm = new UserPermission(Bytes.toBytes(owner),
915 desc.getTableName(), null, Action.values());
916
917 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
918 @Override
919 public Void run() throws Exception {
920 AccessControlLists.addUserPermission(c.getEnvironment().getConfiguration(),
921 userperm, c.getEnvironment().getTable(AccessControlLists.ACL_TABLE_NAME));
922 return null;
923 }
924 });
925 }
926 }
927 }
928
929 @Override
930 public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
931 throws IOException {
932 requirePermission(getActiveUser(c), "deleteTable", tableName, null, null,
933 Action.ADMIN, Action.CREATE);
934 }
935
936 @Override
937 public void postDeleteTable(final ObserverContext<MasterCoprocessorEnvironment> c,
938 final TableName tableName) throws IOException {
939 final Configuration conf = c.getEnvironment().getConfiguration();
940 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
941 @Override
942 public Void run() throws Exception {
943 AccessControlLists.removeTablePermissions(conf, tableName,
944 c.getEnvironment().getTable(AccessControlLists.ACL_TABLE_NAME));
945 return null;
946 }
947 });
948 getAuthManager().getZKPermissionWatcher().deleteTableACLNode(tableName);
949 }
950
951 @Override
952 public void preTruncateTable(ObserverContext<MasterCoprocessorEnvironment> c,
953 final TableName tableName) throws IOException {
954 requirePermission(getActiveUser(c), "truncateTable", tableName, null, null,
955 Action.ADMIN, Action.CREATE);
956
957 final Configuration conf = c.getEnvironment().getConfiguration();
958 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
959 @Override
960 public Void run() throws Exception {
961 List<UserPermission> acls = AccessControlLists.getUserTablePermissions(conf, tableName);
962 if (acls != null) {
963 tableAcls.put(tableName, acls);
964 }
965 return null;
966 }
967 });
968 }
969
970 @Override
971 public void postTruncateTable(final ObserverContext<MasterCoprocessorEnvironment> ctx,
972 final TableName tableName) throws IOException {
973 final Configuration conf = ctx.getEnvironment().getConfiguration();
974 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
975 @Override
976 public Void run() throws Exception {
977 List<UserPermission> perms = tableAcls.get(tableName);
978 if (perms != null) {
979 for (UserPermission perm : perms) {
980 AccessControlLists.addUserPermission(conf, perm,
981 ctx.getEnvironment().getTable(AccessControlLists.ACL_TABLE_NAME));
982 }
983 }
984 tableAcls.remove(tableName);
985 return null;
986 }
987 });
988 }
989
990 @Override
991 public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
992 HTableDescriptor htd) throws IOException {
993 requirePermission(getActiveUser(c), "modifyTable", tableName, null, null,
994 Action.ADMIN, Action.CREATE);
995 }
996
997 @Override
998 public void postModifyTable(final ObserverContext<MasterCoprocessorEnvironment> c,
999 TableName tableName, final HTableDescriptor htd) throws IOException {
1000 final Configuration conf = c.getEnvironment().getConfiguration();
1001
1002 final String owner = (htd.getOwnerString() != null) ? htd.getOwnerString() :
1003 getActiveUser(c).getShortName();
1004 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
1005 @Override
1006 public Void run() throws Exception {
1007 UserPermission userperm = new UserPermission(Bytes.toBytes(owner),
1008 htd.getTableName(), null, Action.values());
1009 AccessControlLists.addUserPermission(conf, userperm,
1010 c.getEnvironment().getTable(AccessControlLists.ACL_TABLE_NAME));
1011 return null;
1012 }
1013 });
1014 }
1015
1016 @Override
1017 public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
1018 HColumnDescriptor column) throws IOException {
1019 requireTablePermission(getActiveUser(c), "addColumn", tableName, column.getName(), null,
1020 Action.ADMIN, Action.CREATE);
1021 }
1022
1023 @Override
1024 public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
1025 HColumnDescriptor descriptor) throws IOException {
1026 requirePermission(getActiveUser(c), "modifyColumn", tableName, descriptor.getName(), null,
1027 Action.ADMIN, Action.CREATE);
1028 }
1029
1030 @Override
1031 public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
1032 byte[] col) throws IOException {
1033 requirePermission(getActiveUser(c), "deleteColumn", tableName, col, null, Action.ADMIN,
1034 Action.CREATE);
1035 }
1036
1037 @Override
1038 public void postDeleteColumn(final ObserverContext<MasterCoprocessorEnvironment> c,
1039 final TableName tableName, final byte[] col) throws IOException {
1040 final Configuration conf = c.getEnvironment().getConfiguration();
1041 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
1042 @Override
1043 public Void run() throws Exception {
1044 AccessControlLists.removeTablePermissions(conf, tableName, col,
1045 c.getEnvironment().getTable(AccessControlLists.ACL_TABLE_NAME));
1046 return null;
1047 }
1048 });
1049 }
1050
1051 @Override
1052 public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
1053 throws IOException {
1054 requirePermission(getActiveUser(c), "enableTable", tableName, null, null,
1055 Action.ADMIN, Action.CREATE);
1056 }
1057
1058 @Override
1059 public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
1060 throws IOException {
1061 if (Bytes.equals(tableName.getName(), AccessControlLists.ACL_GLOBAL_NAME)) {
1062
1063
1064
1065
1066 throw new AccessDeniedException("Not allowed to disable "
1067 + AccessControlLists.ACL_TABLE_NAME + " table with AccessController installed");
1068 }
1069 requirePermission(getActiveUser(c), "disableTable", tableName, null, null,
1070 Action.ADMIN, Action.CREATE);
1071 }
1072
1073 @Override
1074 public void preAbortProcedure(
1075 ObserverContext<MasterCoprocessorEnvironment> ctx,
1076 final ProcedureExecutor<MasterProcedureEnv> procEnv,
1077 final long procId) throws IOException {
1078 if (!procEnv.isProcedureOwner(procId, getActiveUser(ctx))) {
1079
1080
1081 requirePermission(getActiveUser(ctx), "abortProcedure", Action.ADMIN);
1082 }
1083 }
1084
1085 @Override
1086 public void postAbortProcedure(ObserverContext<MasterCoprocessorEnvironment> ctx)
1087 throws IOException {
1088
1089 }
1090
1091 @Override
1092 public void preListProcedures(ObserverContext<MasterCoprocessorEnvironment> ctx)
1093 throws IOException {
1094
1095
1096 }
1097
1098 @Override
1099 public void postListProcedures(
1100 ObserverContext<MasterCoprocessorEnvironment> ctx,
1101 List<ProcedureInfo> procInfoList) throws IOException {
1102 if (procInfoList.isEmpty()) {
1103 return;
1104 }
1105
1106
1107
1108 Iterator<ProcedureInfo> itr = procInfoList.iterator();
1109 User user = getActiveUser(ctx);
1110 while (itr.hasNext()) {
1111 ProcedureInfo procInfo = itr.next();
1112 try {
1113 if (!ProcedureInfo.isProcedureOwner(procInfo, user)) {
1114
1115
1116 requirePermission(user, "listProcedures", Action.ADMIN);
1117 }
1118 } catch (AccessDeniedException e) {
1119 itr.remove();
1120 }
1121 }
1122 }
1123
1124 @Override
1125 public void preMove(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo region,
1126 ServerName srcServer, ServerName destServer) throws IOException {
1127 requirePermission(getActiveUser(c), "move", region.getTable(), null, null, Action.ADMIN);
1128 }
1129
1130 @Override
1131 public void preAssign(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo regionInfo)
1132 throws IOException {
1133 requirePermission(getActiveUser(c), "assign", regionInfo.getTable(), null, null,
1134 Action.ADMIN);
1135 }
1136
1137 @Override
1138 public void preUnassign(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo regionInfo,
1139 boolean force) throws IOException {
1140 requirePermission(getActiveUser(c), "unassign", regionInfo.getTable(), null, null,
1141 Action.ADMIN);
1142 }
1143
1144 @Override
1145 public void preRegionOffline(ObserverContext<MasterCoprocessorEnvironment> c,
1146 HRegionInfo regionInfo) throws IOException {
1147 requirePermission(getActiveUser(c), "regionOffline", regionInfo.getTable(), null, null,
1148 Action.ADMIN);
1149 }
1150
1151 @Override
1152 public boolean preSetSplitOrMergeEnabled(final ObserverContext<MasterCoprocessorEnvironment> ctx,
1153 final boolean newValue, final Admin.MasterSwitchType switchType) throws IOException {
1154 requirePermission(getActiveUser(ctx), "setSplitOrMergeEnabled", Action.ADMIN);
1155 return false;
1156 }
1157
1158 @Override
1159 public void postSetSplitOrMergeEnabled(final ObserverContext<MasterCoprocessorEnvironment> ctx,
1160 final boolean newValue, final Admin.MasterSwitchType switchType) throws IOException {
1161 }
1162
1163 @Override
1164 public void preBalance(ObserverContext<MasterCoprocessorEnvironment> c)
1165 throws IOException {
1166 requirePermission(getActiveUser(c), "balance", Action.ADMIN);
1167 }
1168
1169 @Override
1170 public boolean preBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> c,
1171 boolean newValue) throws IOException {
1172 requirePermission(getActiveUser(c), "balanceSwitch", Action.ADMIN);
1173 return newValue;
1174 }
1175
1176 @Override
1177 public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> c)
1178 throws IOException {
1179 requirePermission(getActiveUser(c), "shutdown", Action.ADMIN);
1180 }
1181
1182 @Override
1183 public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> c)
1184 throws IOException {
1185 requirePermission(getActiveUser(c), "stopMaster", Action.ADMIN);
1186 }
1187
1188 @Override
1189 public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx)
1190 throws IOException {
1191 if (!MetaTableAccessor.tableExists(ctx.getEnvironment().getMasterServices()
1192 .getConnection(), AccessControlLists.ACL_TABLE_NAME)) {
1193
1194 AccessControlLists.createACLTable(ctx.getEnvironment().getMasterServices());
1195 } else {
1196 aclTabAvailable = true;
1197 }
1198 }
1199
1200 @Override
1201 public void preSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
1202 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
1203 throws IOException {
1204 requirePermission(getActiveUser(ctx), "snapshot " + snapshot.getName(),
1205 hTableDescriptor.getTableName(), null, null, Permission.Action.ADMIN);
1206 }
1207
1208 @Override
1209 public void preListSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
1210 final SnapshotDescription snapshot) throws IOException {
1211 User user = getActiveUser(ctx);
1212 if (SnapshotDescriptionUtils.isSnapshotOwner(snapshot, user)) {
1213
1214 AuthResult result = AuthResult.allow("listSnapshot " + snapshot.getName(),
1215 "Snapshot owner check allowed", user, null, null, null);
1216 accessChecker.logResult(result);
1217 } else {
1218 requirePermission(user, "listSnapshot " + snapshot.getName(), Action.ADMIN);
1219 }
1220 }
1221
1222 @Override
1223 public void preCloneSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
1224 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
1225 throws IOException {
1226 User user = getActiveUser(ctx);
1227 if (SnapshotDescriptionUtils.isSnapshotOwner(snapshot, user)
1228 && hTableDescriptor.getNameAsString().equals(snapshot.getTable())) {
1229
1230 AuthResult result = AuthResult.allow("cloneSnapshot " + snapshot.getName(),
1231 "Snapshot owner check allowed", user, null, hTableDescriptor.getTableName(), null);
1232 accessChecker.logResult(result);
1233 } else {
1234 requirePermission(user, "cloneSnapshot " + snapshot.getName(), Action.ADMIN);
1235 }
1236 }
1237
1238 @Override
1239 public void preRestoreSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
1240 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
1241 throws IOException {
1242 User user = getActiveUser(ctx);
1243 if (SnapshotDescriptionUtils.isSnapshotOwner(snapshot, user)) {
1244 requirePermission(user, "restoreSnapshot " + snapshot.getName(),
1245 hTableDescriptor.getTableName(), null, null, Permission.Action.ADMIN);
1246 } else {
1247 requirePermission(user, "restoreSnapshot " + snapshot.getName(), Action.ADMIN);
1248 }
1249 }
1250
1251 @Override
1252 public void preDeleteSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
1253 final SnapshotDescription snapshot) throws IOException {
1254 User user = getActiveUser(ctx);
1255 if (SnapshotDescriptionUtils.isSnapshotOwner(snapshot, user)) {
1256
1257 AuthResult result = AuthResult.allow("deleteSnapshot " + snapshot.getName(),
1258 "Snapshot owner check allowed", user, null, null, null);
1259 accessChecker.logResult(result);
1260 } else {
1261 requirePermission(user, "deleteSnapshot", Action.ADMIN);
1262 }
1263 }
1264
1265 @Override
1266 public void preCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
1267 NamespaceDescriptor ns) throws IOException {
1268 requireGlobalPermission(getActiveUser(ctx), "createNamespace", Action.ADMIN, ns.getName());
1269 }
1270
1271 @Override
1272 public void preDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, String namespace)
1273 throws IOException {
1274 requireGlobalPermission(getActiveUser(ctx), "deleteNamespace", Action.ADMIN, namespace);
1275 }
1276
1277 @Override
1278 public void postDeleteNamespace(final ObserverContext<MasterCoprocessorEnvironment> ctx,
1279 final String namespace) throws IOException {
1280 final Configuration conf = ctx.getEnvironment().getConfiguration();
1281 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
1282 @Override
1283 public Void run() throws Exception {
1284 AccessControlLists.removeNamespacePermissions(conf, namespace,
1285 ctx.getEnvironment().getTable(AccessControlLists.ACL_TABLE_NAME));
1286 return null;
1287 }
1288 });
1289 getAuthManager().getZKPermissionWatcher().deleteNamespaceACLNode(namespace);
1290 LOG.info(namespace + " entry deleted in " + AccessControlLists.ACL_TABLE_NAME + " table.");
1291 }
1292
1293 @Override
1294 public void preModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
1295 NamespaceDescriptor ns) throws IOException {
1296
1297
1298 requireGlobalPermission(getActiveUser(ctx), "modifyNamespace", Action.ADMIN, ns.getName());
1299 }
1300
1301 @Override
1302 public void preGetNamespaceDescriptor(ObserverContext<MasterCoprocessorEnvironment> ctx,
1303 String namespace) throws IOException {
1304 requireNamespacePermission(getActiveUser(ctx), "getNamespaceDescriptor", namespace,
1305 Action.ADMIN);
1306 }
1307
1308 @Override
1309 public void preListNamespaces(ObserverContext<MasterCoprocessorEnvironment> ctx,
1310 List<String> namespaces) throws IOException {
1311
1312 }
1313
1314 @Override
1315 public void postListNamespaceDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
1316 List<NamespaceDescriptor> descriptors) throws IOException {
1317
1318
1319 Iterator<NamespaceDescriptor> itr = descriptors.iterator();
1320 User user = getActiveUser(ctx);
1321 while (itr.hasNext()) {
1322 NamespaceDescriptor desc = itr.next();
1323 try {
1324 requireNamespacePermission(user, "listNamespaces", desc.getName(), Action.ADMIN);
1325 } catch (AccessDeniedException e) {
1326 itr.remove();
1327 }
1328 }
1329 }
1330
1331 @Override
1332 public void preTableFlush(final ObserverContext<MasterCoprocessorEnvironment> ctx,
1333 final TableName tableName) throws IOException {
1334 requirePermission(getActiveUser(ctx), "flushTable", tableName, null, null,
1335 Action.ADMIN, Action.CREATE);
1336 }
1337
1338
1339
1340 @Override
1341 public void preOpen(ObserverContext<RegionCoprocessorEnvironment> c)
1342 throws IOException {
1343 RegionCoprocessorEnvironment env = c.getEnvironment();
1344 final Region region = env.getRegion();
1345 if (region == null) {
1346 LOG.error("NULL region from RegionCoprocessorEnvironment in preOpen()");
1347 } else {
1348 HRegionInfo regionInfo = region.getRegionInfo();
1349 if (regionInfo.getTable().isSystemTable()) {
1350 checkSystemOrSuperUser(getActiveUser(c));
1351 } else {
1352 requirePermission(getActiveUser(c), "preOpen", Action.ADMIN);
1353 }
1354 }
1355 }
1356
1357 @Override
1358 public void postOpen(ObserverContext<RegionCoprocessorEnvironment> c) {
1359 RegionCoprocessorEnvironment env = c.getEnvironment();
1360 final Region region = env.getRegion();
1361 if (region == null) {
1362 LOG.error("NULL region from RegionCoprocessorEnvironment in postOpen()");
1363 return;
1364 }
1365 if (AccessControlLists.isAclRegion(region)) {
1366 aclRegion = true;
1367
1368 if (!region.isRecovering()) {
1369 try {
1370 initialize(env);
1371 } catch (IOException ex) {
1372
1373
1374 throw new RuntimeException("Failed to initialize permissions cache", ex);
1375 }
1376 }
1377 } else {
1378 initialized = true;
1379 }
1380 }
1381
1382 @Override
1383 public void postLogReplay(ObserverContext<RegionCoprocessorEnvironment> c) {
1384 if (aclRegion) {
1385 try {
1386 initialize(c.getEnvironment());
1387 } catch (IOException ex) {
1388
1389
1390 throw new RuntimeException("Failed to initialize permissions cache", ex);
1391 }
1392 }
1393 }
1394
1395 @Override
1396 public void preFlush(ObserverContext<RegionCoprocessorEnvironment> c) throws IOException {
1397 requirePermission(getActiveUser(c), "flush", getTableName(c.getEnvironment()), null, null,
1398 Action.ADMIN, Action.CREATE);
1399 }
1400
1401 @Override
1402 public void preSplit(ObserverContext<RegionCoprocessorEnvironment> c) throws IOException {
1403 requirePermission(getActiveUser(c), "split", getTableName(c.getEnvironment()), null, null,
1404 Action.ADMIN);
1405 }
1406
1407 @Override
1408 public void preSplit(ObserverContext<RegionCoprocessorEnvironment> c,
1409 byte[] splitRow) throws IOException {
1410 requirePermission(getActiveUser(c), "split", getTableName(c.getEnvironment()), null, null,
1411 Action.ADMIN);
1412 }
1413
1414 @Override
1415 public InternalScanner preCompact(ObserverContext<RegionCoprocessorEnvironment> c,
1416 final Store store, final InternalScanner scanner, final ScanType scanType)
1417 throws IOException {
1418 requirePermission(getActiveUser(c), "compact", getTableName(c.getEnvironment()), null, null,
1419 Action.ADMIN, Action.CREATE);
1420 return scanner;
1421 }
1422
1423 @Override
1424 public void preGetClosestRowBefore(final ObserverContext<RegionCoprocessorEnvironment> c,
1425 final byte [] row, final byte [] family, final Result result)
1426 throws IOException {
1427 assert family != null;
1428 RegionCoprocessorEnvironment env = c.getEnvironment();
1429 Map<byte[],? extends Collection<byte[]>> families = makeFamilyMap(family, null);
1430 User user = getActiveUser(c);
1431 AuthResult authResult = permissionGranted(OpType.GET_CLOSEST_ROW_BEFORE, user, env, families,
1432 Action.READ);
1433 if (!authResult.isAllowed() && cellFeaturesEnabled && !compatibleEarlyTermination) {
1434 authResult.setAllowed(checkCoveringPermission(user, OpType.GET_CLOSEST_ROW_BEFORE, env, row,
1435 families, HConstants.LATEST_TIMESTAMP, Action.READ));
1436 authResult.setReason("Covering cell set");
1437 }
1438 accessChecker.logResult(authResult);
1439 if (authorizationEnabled && !authResult.isAllowed()) {
1440 throw new AccessDeniedException("Insufficient permissions " +
1441 authResult.toContextString());
1442 }
1443 }
1444
1445 private void internalPreRead(final ObserverContext<RegionCoprocessorEnvironment> c,
1446 final Query query, OpType opType) throws IOException {
1447 Filter filter = query.getFilter();
1448
1449 if (filter != null && filter instanceof AccessControlFilter) {
1450 return;
1451 }
1452 User user = getActiveUser(c);
1453 RegionCoprocessorEnvironment env = c.getEnvironment();
1454 Map<byte[],? extends Collection<byte[]>> families = null;
1455 switch (opType) {
1456 case GET:
1457 case EXISTS:
1458 families = ((Get)query).getFamilyMap();
1459 break;
1460 case SCAN:
1461 families = ((Scan)query).getFamilyMap();
1462 break;
1463 default:
1464 throw new RuntimeException("Unhandled operation " + opType);
1465 }
1466 AuthResult authResult = permissionGranted(opType, user, env, families, Action.READ);
1467 Region region = getRegion(env);
1468 TableName table = getTableName(region);
1469 Map<ByteRange, Integer> cfVsMaxVersions = Maps.newHashMap();
1470 for (HColumnDescriptor hcd : region.getTableDesc().getFamilies()) {
1471 cfVsMaxVersions.put(new SimpleMutableByteRange(hcd.getName()), hcd.getMaxVersions());
1472 }
1473 if (!authResult.isAllowed()) {
1474 if (!cellFeaturesEnabled || compatibleEarlyTermination) {
1475
1476
1477
1478
1479
1480
1481
1482
1483 if (hasFamilyQualifierPermission(user, Action.READ, env, families)) {
1484 authResult.setAllowed(true);
1485 authResult.setReason("Access allowed with filter");
1486
1487 if (authorizationEnabled) {
1488 Filter ourFilter = new AccessControlFilter(getAuthManager(), user, table,
1489 AccessControlFilter.Strategy.CHECK_TABLE_AND_CF_ONLY,
1490 cfVsMaxVersions);
1491
1492 if (filter != null) {
1493 ourFilter = new FilterList(FilterList.Operator.MUST_PASS_ALL,
1494 Lists.newArrayList(ourFilter, filter));
1495 }
1496 switch (opType) {
1497 case GET:
1498 case EXISTS:
1499 ((Get)query).setFilter(ourFilter);
1500 break;
1501 case SCAN:
1502 ((Scan)query).setFilter(ourFilter);
1503 break;
1504 default:
1505 throw new RuntimeException("Unhandled operation " + opType);
1506 }
1507 }
1508 }
1509 } else {
1510
1511
1512
1513
1514 authResult.setAllowed(true);
1515 authResult.setReason("Access allowed with filter");
1516
1517 if (authorizationEnabled) {
1518 Filter ourFilter = new AccessControlFilter(getAuthManager(), user, table,
1519 AccessControlFilter.Strategy.CHECK_CELL_DEFAULT, cfVsMaxVersions);
1520
1521 if (filter != null) {
1522 ourFilter = new FilterList(FilterList.Operator.MUST_PASS_ALL,
1523 Lists.newArrayList(ourFilter, filter));
1524 }
1525 switch (opType) {
1526 case GET:
1527 case EXISTS:
1528 ((Get)query).setFilter(ourFilter);
1529 break;
1530 case SCAN:
1531 ((Scan)query).setFilter(ourFilter);
1532 break;
1533 default:
1534 throw new RuntimeException("Unhandled operation " + opType);
1535 }
1536 }
1537 }
1538 }
1539
1540 accessChecker.logResult(authResult);
1541 if (authorizationEnabled && !authResult.isAllowed()) {
1542 throw new AccessDeniedException("Insufficient permissions for user '"
1543 + (user != null ? user.getShortName() : "null")
1544 + "' (table=" + table + ", action=READ)");
1545 }
1546 }
1547
1548 @Override
1549 public void preGetOp(final ObserverContext<RegionCoprocessorEnvironment> c,
1550 final Get get, final List<Cell> result) throws IOException {
1551 internalPreRead(c, get, OpType.GET);
1552 }
1553
1554 @Override
1555 public boolean preExists(final ObserverContext<RegionCoprocessorEnvironment> c,
1556 final Get get, final boolean exists) throws IOException {
1557 internalPreRead(c, get, OpType.EXISTS);
1558 return exists;
1559 }
1560
1561 @Override
1562 public void prePut(final ObserverContext<RegionCoprocessorEnvironment> c,
1563 final Put put, final WALEdit edit, final Durability durability)
1564 throws IOException {
1565 User user = getActiveUser(c);
1566 checkForReservedTagPresence(user, put);
1567
1568
1569
1570
1571
1572
1573
1574 RegionCoprocessorEnvironment env = c.getEnvironment();
1575 Map<byte[],? extends Collection<Cell>> families = put.getFamilyCellMap();
1576 AuthResult authResult = permissionGranted(OpType.PUT, user, env, families, Action.WRITE);
1577 accessChecker.logResult(authResult);
1578 if (!authResult.isAllowed()) {
1579 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1580 put.setAttribute(CHECK_COVERING_PERM, TRUE);
1581 } else if (authorizationEnabled) {
1582 throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
1583 }
1584 }
1585
1586
1587 byte[] bytes = put.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
1588 if (bytes != null) {
1589 if (cellFeaturesEnabled) {
1590 addCellPermissions(bytes, put.getFamilyCellMap());
1591 } else {
1592 throw new DoNotRetryIOException("Cell ACLs cannot be persisted");
1593 }
1594 }
1595 }
1596
1597 @Override
1598 public void postPut(final ObserverContext<RegionCoprocessorEnvironment> c,
1599 final Put put, final WALEdit edit, final Durability durability) {
1600 if (aclRegion) {
1601 updateACL(c.getEnvironment(), put.getFamilyCellMap());
1602 }
1603 }
1604
1605 @Override
1606 public void preDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
1607 final Delete delete, final WALEdit edit, final Durability durability)
1608 throws IOException {
1609
1610 if (delete.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL) != null) {
1611 throw new DoNotRetryIOException("ACL on delete has no effect: " + delete.toString());
1612 }
1613
1614
1615
1616
1617
1618 RegionCoprocessorEnvironment env = c.getEnvironment();
1619 Map<byte[],? extends Collection<Cell>> families = delete.getFamilyCellMap();
1620 User user = getActiveUser(c);
1621 AuthResult authResult = permissionGranted(OpType.DELETE, user, env, families, Action.WRITE);
1622 accessChecker.logResult(authResult);
1623 if (!authResult.isAllowed()) {
1624 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1625 delete.setAttribute(CHECK_COVERING_PERM, TRUE);
1626 } else if (authorizationEnabled) {
1627 throw new AccessDeniedException("Insufficient permissions " +
1628 authResult.toContextString());
1629 }
1630 }
1631 }
1632
1633 @Override
1634 public void preBatchMutate(ObserverContext<RegionCoprocessorEnvironment> c,
1635 MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException {
1636 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1637 TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
1638 User user = getActiveUser(c);
1639 for (int i = 0; i < miniBatchOp.size(); i++) {
1640 Mutation m = miniBatchOp.getOperation(i);
1641 if (m.getAttribute(CHECK_COVERING_PERM) != null) {
1642
1643
1644 OpType opType;
1645 if (m instanceof Put) {
1646 checkForReservedTagPresence(user, m);
1647 opType = OpType.PUT;
1648 } else {
1649 opType = OpType.DELETE;
1650 }
1651 AuthResult authResult = null;
1652 if (checkCoveringPermission(user, opType, c.getEnvironment(), m.getRow(),
1653 m.getFamilyCellMap(), m.getTimeStamp(), Action.WRITE)) {
1654 authResult = AuthResult.allow(opType.toString(), "Covering cell set",
1655 user, Action.WRITE, table, m.getFamilyCellMap());
1656 } else {
1657 authResult = AuthResult.deny(opType.toString(), "Covering cell set",
1658 user, Action.WRITE, table, m.getFamilyCellMap());
1659 }
1660 accessChecker.logResult(authResult);
1661 if (authorizationEnabled && !authResult.isAllowed()) {
1662 throw new AccessDeniedException("Insufficient permissions "
1663 + authResult.toContextString());
1664 }
1665 }
1666 }
1667 }
1668 }
1669
1670 @Override
1671 public void postDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
1672 final Delete delete, final WALEdit edit, final Durability durability)
1673 throws IOException {
1674 if (aclRegion) {
1675 updateACL(c.getEnvironment(), delete.getFamilyCellMap());
1676 }
1677 }
1678
1679 @Override
1680 public boolean preCheckAndPut(final ObserverContext<RegionCoprocessorEnvironment> c,
1681 final byte [] row, final byte [] family, final byte [] qualifier,
1682 final CompareFilter.CompareOp compareOp,
1683 final ByteArrayComparable comparator, final Put put,
1684 final boolean result) throws IOException {
1685 User user = getActiveUser(c);
1686 checkForReservedTagPresence(user, put);
1687
1688
1689 RegionCoprocessorEnvironment env = c.getEnvironment();
1690 Map<byte[],? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier);
1691 AuthResult authResult = permissionGranted(OpType.CHECK_AND_PUT, user, env, families,
1692 Action.READ, Action.WRITE);
1693 accessChecker.logResult(authResult);
1694 if (!authResult.isAllowed()) {
1695 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1696 put.setAttribute(CHECK_COVERING_PERM, TRUE);
1697 } else if (authorizationEnabled) {
1698 throw new AccessDeniedException("Insufficient permissions " +
1699 authResult.toContextString());
1700 }
1701 }
1702
1703 byte[] bytes = put.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
1704 if (bytes != null) {
1705 if (cellFeaturesEnabled) {
1706 addCellPermissions(bytes, put.getFamilyCellMap());
1707 } else {
1708 throw new DoNotRetryIOException("Cell ACLs cannot be persisted");
1709 }
1710 }
1711 return result;
1712 }
1713
1714 @Override
1715 public boolean preCheckAndPutAfterRowLock(final ObserverContext<RegionCoprocessorEnvironment> c,
1716 final byte[] row, final byte[] family, final byte[] qualifier,
1717 final CompareFilter.CompareOp compareOp, final ByteArrayComparable comparator, final Put put,
1718 final boolean result) throws IOException {
1719 if (put.getAttribute(CHECK_COVERING_PERM) != null) {
1720
1721
1722 TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
1723 Map<byte[], ? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier);
1724 AuthResult authResult = null;
1725 User user = getActiveUser(c);
1726 if (checkCoveringPermission(user, OpType.CHECK_AND_PUT, c.getEnvironment(), row, families,
1727 HConstants.LATEST_TIMESTAMP, Action.READ)) {
1728 authResult = AuthResult.allow(OpType.CHECK_AND_PUT.toString(), "Covering cell set",
1729 user, Action.READ, table, families);
1730 } else {
1731 authResult = AuthResult.deny(OpType.CHECK_AND_PUT.toString(), "Covering cell set",
1732 user, Action.READ, table, families);
1733 }
1734 accessChecker.logResult(authResult);
1735 if (authorizationEnabled && !authResult.isAllowed()) {
1736 throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
1737 }
1738 }
1739 return result;
1740 }
1741
1742 @Override
1743 public boolean preCheckAndDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
1744 final byte [] row, final byte [] family, final byte [] qualifier,
1745 final CompareFilter.CompareOp compareOp,
1746 final ByteArrayComparable comparator, final Delete delete,
1747 final boolean result) throws IOException {
1748
1749 if (delete.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL) != null) {
1750 throw new DoNotRetryIOException("ACL on checkAndDelete has no effect: " +
1751 delete.toString());
1752 }
1753
1754
1755 RegionCoprocessorEnvironment env = c.getEnvironment();
1756 Map<byte[],? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier);
1757 User user = getActiveUser(c);
1758 AuthResult authResult = permissionGranted(OpType.CHECK_AND_DELETE, user, env, families,
1759 Action.READ, Action.WRITE);
1760 accessChecker.logResult(authResult);
1761 if (!authResult.isAllowed()) {
1762 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1763 delete.setAttribute(CHECK_COVERING_PERM, TRUE);
1764 } else if (authorizationEnabled) {
1765 throw new AccessDeniedException("Insufficient permissions " +
1766 authResult.toContextString());
1767 }
1768 }
1769 return result;
1770 }
1771
1772 @Override
1773 public boolean preCheckAndDeleteAfterRowLock(
1774 final ObserverContext<RegionCoprocessorEnvironment> c, final byte[] row, final byte[] family,
1775 final byte[] qualifier, final CompareFilter.CompareOp compareOp,
1776 final ByteArrayComparable comparator, final Delete delete, final boolean result)
1777 throws IOException {
1778 if (delete.getAttribute(CHECK_COVERING_PERM) != null) {
1779
1780
1781 TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
1782 Map<byte[], ? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier);
1783 AuthResult authResult = null;
1784 User user = getActiveUser(c);
1785 if (checkCoveringPermission(user, OpType.CHECK_AND_DELETE, c.getEnvironment(), row, families,
1786 HConstants.LATEST_TIMESTAMP, Action.READ)) {
1787 authResult = AuthResult.allow(OpType.CHECK_AND_DELETE.toString(), "Covering cell set",
1788 user, Action.READ, table, families);
1789 } else {
1790 authResult = AuthResult.deny(OpType.CHECK_AND_DELETE.toString(), "Covering cell set",
1791 user, Action.READ, table, families);
1792 }
1793 accessChecker.logResult(authResult);
1794 if (authorizationEnabled && !authResult.isAllowed()) {
1795 throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
1796 }
1797 }
1798 return result;
1799 }
1800
1801 @Override
1802 public long preIncrementColumnValue(final ObserverContext<RegionCoprocessorEnvironment> c,
1803 final byte [] row, final byte [] family, final byte [] qualifier,
1804 final long amount, final boolean writeToWAL)
1805 throws IOException {
1806
1807
1808 RegionCoprocessorEnvironment env = c.getEnvironment();
1809 Map<byte[],? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier);
1810 User user = getActiveUser(c);
1811 AuthResult authResult = permissionGranted(OpType.INCREMENT_COLUMN_VALUE, user, env, families,
1812 Action.WRITE);
1813 if (!authResult.isAllowed() && cellFeaturesEnabled && !compatibleEarlyTermination) {
1814 authResult.setAllowed(checkCoveringPermission(user, OpType.INCREMENT_COLUMN_VALUE, env, row,
1815 families, HConstants.LATEST_TIMESTAMP, Action.WRITE));
1816 authResult.setReason("Covering cell set");
1817 }
1818 accessChecker.logResult(authResult);
1819 if (authorizationEnabled && !authResult.isAllowed()) {
1820 throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
1821 }
1822 return -1;
1823 }
1824
1825 @Override
1826 public Result preAppend(ObserverContext<RegionCoprocessorEnvironment> c, Append append)
1827 throws IOException {
1828 User user = getActiveUser(c);
1829 checkForReservedTagPresence(user, append);
1830
1831
1832 RegionCoprocessorEnvironment env = c.getEnvironment();
1833 Map<byte[],? extends Collection<Cell>> families = append.getFamilyCellMap();
1834 AuthResult authResult = permissionGranted(OpType.APPEND, user, env, families, Action.WRITE);
1835 accessChecker.logResult(authResult);
1836 if (!authResult.isAllowed()) {
1837 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1838 append.setAttribute(CHECK_COVERING_PERM, TRUE);
1839 } else if (authorizationEnabled) {
1840 throw new AccessDeniedException("Insufficient permissions " +
1841 authResult.toContextString());
1842 }
1843 }
1844
1845 byte[] bytes = append.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
1846 if (bytes != null) {
1847 if (cellFeaturesEnabled) {
1848 addCellPermissions(bytes, append.getFamilyCellMap());
1849 } else {
1850 throw new DoNotRetryIOException("Cell ACLs cannot be persisted");
1851 }
1852 }
1853
1854 return null;
1855 }
1856
1857 @Override
1858 public Result preAppendAfterRowLock(final ObserverContext<RegionCoprocessorEnvironment> c,
1859 final Append append) throws IOException {
1860 if (append.getAttribute(CHECK_COVERING_PERM) != null) {
1861
1862
1863 TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
1864 AuthResult authResult = null;
1865 User user = getActiveUser(c);
1866 if (checkCoveringPermission(user, OpType.APPEND, c.getEnvironment(), append.getRow(),
1867 append.getFamilyCellMap(), HConstants.LATEST_TIMESTAMP, Action.WRITE)) {
1868 authResult = AuthResult.allow(OpType.APPEND.toString(), "Covering cell set",
1869 user, Action.WRITE, table, append.getFamilyCellMap());
1870 } else {
1871 authResult = AuthResult.deny(OpType.APPEND.toString(), "Covering cell set",
1872 user, Action.WRITE, table, append.getFamilyCellMap());
1873 }
1874 accessChecker.logResult(authResult);
1875 if (authorizationEnabled && !authResult.isAllowed()) {
1876 throw new AccessDeniedException("Insufficient permissions " +
1877 authResult.toContextString());
1878 }
1879 }
1880 return null;
1881 }
1882
1883 @Override
1884 public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> c,
1885 final Increment increment)
1886 throws IOException {
1887 User user = getActiveUser(c);
1888 checkForReservedTagPresence(user, increment);
1889
1890
1891
1892 RegionCoprocessorEnvironment env = c.getEnvironment();
1893 Map<byte[],? extends Collection<Cell>> families = increment.getFamilyCellMap();
1894 AuthResult authResult = permissionGranted(OpType.INCREMENT, user, env, families,
1895 Action.WRITE);
1896 accessChecker.logResult(authResult);
1897 if (!authResult.isAllowed()) {
1898 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1899 increment.setAttribute(CHECK_COVERING_PERM, TRUE);
1900 } else if (authorizationEnabled) {
1901 throw new AccessDeniedException("Insufficient permissions " +
1902 authResult.toContextString());
1903 }
1904 }
1905
1906 byte[] bytes = increment.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
1907 if (bytes != null) {
1908 if (cellFeaturesEnabled) {
1909 addCellPermissions(bytes, increment.getFamilyCellMap());
1910 } else {
1911 throw new DoNotRetryIOException("Cell ACLs cannot be persisted");
1912 }
1913 }
1914
1915 return null;
1916 }
1917
1918 @Override
1919 public Result preIncrementAfterRowLock(final ObserverContext<RegionCoprocessorEnvironment> c,
1920 final Increment increment) throws IOException {
1921 if (increment.getAttribute(CHECK_COVERING_PERM) != null) {
1922
1923
1924 TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
1925 AuthResult authResult = null;
1926 User user = getActiveUser(c);
1927 if (checkCoveringPermission(user, OpType.INCREMENT, c.getEnvironment(), increment.getRow(),
1928 increment.getFamilyCellMap(), increment.getTimeRange().getMax(), Action.WRITE)) {
1929 authResult = AuthResult.allow(OpType.INCREMENT.toString(), "Covering cell set",
1930 user, Action.WRITE, table, increment.getFamilyCellMap());
1931 } else {
1932 authResult = AuthResult.deny(OpType.INCREMENT.toString(), "Covering cell set",
1933 user, Action.WRITE, table, increment.getFamilyCellMap());
1934 }
1935 accessChecker.logResult(authResult);
1936 if (authorizationEnabled && !authResult.isAllowed()) {
1937 throw new AccessDeniedException("Insufficient permissions " +
1938 authResult.toContextString());
1939 }
1940 }
1941 return null;
1942 }
1943
1944 @Override
1945 public Cell postMutationBeforeWAL(ObserverContext<RegionCoprocessorEnvironment> ctx,
1946 MutationType opType, Mutation mutation, Cell oldCell, Cell newCell) throws IOException {
1947
1948
1949 if (!cellFeaturesEnabled || mutation.getACL() == null) {
1950 return newCell;
1951 }
1952
1953
1954
1955
1956 List<Tag> tags = Lists.newArrayList();
1957 if (newCell != null) {
1958
1959 if (newCell.getTagsLength() > 0) {
1960 Iterator<Tag> tagIterator = CellUtil
1961 .tagsIterator(newCell.getTagsArray(), newCell.getTagsOffset(), newCell.getTagsLength());
1962 while (tagIterator.hasNext()) {
1963 Tag tag = tagIterator.next();
1964 if (tag.getType() != AccessControlLists.ACL_TAG_TYPE) {
1965
1966 if (LOG.isTraceEnabled()) {
1967 LOG.trace("Carrying forward tag from " + newCell + ": type " + tag.getType() +
1968 " length " + tag.getTagLength());
1969 }
1970 tags.add(tag);
1971 }
1972 }
1973 }
1974 }
1975
1976
1977 tags.add(new Tag(AccessControlLists.ACL_TAG_TYPE, mutation.getACL()));
1978 return new TagRewriteCell(newCell, Tag.fromList(tags));
1979 }
1980
1981 @Override
1982 public RegionScanner preScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
1983 final Scan scan, final RegionScanner s) throws IOException {
1984 internalPreRead(c, scan, OpType.SCAN);
1985 return s;
1986 }
1987
1988 @Override
1989 public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
1990 final Scan scan, final RegionScanner s) throws IOException {
1991 User user = getActiveUser(c);
1992 if (user != null && user.getShortName() != null) {
1993
1994 scannerOwners.put(s, user.getShortName());
1995 }
1996 return s;
1997 }
1998
1999 @Override
2000 public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c,
2001 final InternalScanner s, final List<Result> result,
2002 final int limit, final boolean hasNext) throws IOException {
2003 requireScannerOwner(s);
2004 return hasNext;
2005 }
2006
2007 @Override
2008 public void preScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
2009 final InternalScanner s) throws IOException {
2010 requireScannerOwner(s);
2011 }
2012
2013 @Override
2014 public void postScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
2015 final InternalScanner s) throws IOException {
2016
2017 scannerOwners.remove(s);
2018 }
2019
2020
2021
2022
2023
2024
2025 private void requireScannerOwner(InternalScanner s) throws AccessDeniedException {
2026 if (!RpcServer.isInRpcCallContext())
2027 return;
2028 String requestUserName = RpcServer.getRequestUserName();
2029 String owner = scannerOwners.get(s);
2030 if (authorizationEnabled && owner != null && !owner.equals(requestUserName)) {
2031 throw new AccessDeniedException("User '"+ requestUserName +"' is not the scanner owner!");
2032 }
2033 }
2034
2035
2036
2037
2038
2039
2040
2041 @Override
2042 public void preBulkLoadHFile(ObserverContext<RegionCoprocessorEnvironment> ctx,
2043 List<Pair<byte[], String>> familyPaths) throws IOException {
2044 User user = getActiveUser(ctx);
2045 for(Pair<byte[],String> el : familyPaths) {
2046 requirePermission(user, "preBulkLoadHFile",
2047 ctx.getEnvironment().getRegion().getTableDesc().getTableName(),
2048 el.getFirst(),
2049 null,
2050 Action.CREATE);
2051 }
2052 }
2053
2054
2055
2056
2057
2058
2059
2060
2061 @Override
2062 public void prePrepareBulkLoad(ObserverContext<RegionCoprocessorEnvironment> ctx,
2063 PrepareBulkLoadRequest request) throws IOException {
2064 requireAccess(getActiveUser(ctx), "prePrepareBulkLoad",
2065 ctx.getEnvironment().getRegion().getTableDesc().getTableName(), Action.CREATE);
2066 }
2067
2068
2069
2070
2071
2072
2073
2074
2075 @Override
2076 public void preCleanupBulkLoad(ObserverContext<RegionCoprocessorEnvironment> ctx,
2077 CleanupBulkLoadRequest request) throws IOException {
2078 requireAccess(getActiveUser(ctx), "preCleanupBulkLoad",
2079 ctx.getEnvironment().getRegion().getTableDesc().getTableName(), Action.CREATE);
2080 }
2081
2082
2083
2084 @Override
2085 public Message preEndpointInvocation(ObserverContext<RegionCoprocessorEnvironment> ctx,
2086 Service service, String methodName, Message request) throws IOException {
2087
2088
2089 if (shouldCheckExecPermission && !(service instanceof AccessControlService)) {
2090 requirePermission(getActiveUser(ctx),
2091 "invoke(" + service.getDescriptorForType().getName() + "." + methodName + ")",
2092 getTableName(ctx.getEnvironment()), null, null,
2093 Action.EXEC);
2094 }
2095 return request;
2096 }
2097
2098 @Override
2099 public void postEndpointInvocation(ObserverContext<RegionCoprocessorEnvironment> ctx,
2100 Service service, String methodName, Message request, Message.Builder responseBuilder)
2101 throws IOException { }
2102
2103
2104
2105 @Override
2106 public void grant(RpcController controller,
2107 final AccessControlProtos.GrantRequest request,
2108 RpcCallback<AccessControlProtos.GrantResponse> done) {
2109 final UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission());
2110 AccessControlProtos.GrantResponse response = null;
2111 try {
2112
2113 if (aclRegion) {
2114 if (!initialized) {
2115 throw new CoprocessorException("AccessController not yet initialized");
2116 }
2117 if (LOG.isDebugEnabled()) {
2118 LOG.debug("Received request to grant access permission " + perm.toString());
2119 }
2120 User caller = RpcServer.getRequestUser();
2121
2122 switch(request.getUserPermission().getPermission().getType()) {
2123 case Global :
2124 case Table :
2125 requirePermission(caller, "grant", perm.getTableName(),
2126 perm.getFamily(), perm.getQualifier(), Action.ADMIN);
2127 break;
2128 case Namespace :
2129 requireNamespacePermission(caller, "grant", perm.getNamespace(), Action.ADMIN);
2130 break;
2131 }
2132
2133 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
2134 @Override
2135 public Void run() throws Exception {
2136 AccessControlLists.addUserPermission(regionEnv.getConfiguration(), perm,
2137 regionEnv.getTable(AccessControlLists.ACL_TABLE_NAME),
2138 request.getMergeExistingPermissions());
2139 return null;
2140 }
2141 });
2142
2143 if (AUDITLOG.isTraceEnabled()) {
2144
2145 AUDITLOG.trace("Granted permission " + perm.toString());
2146 }
2147 } else {
2148 throw new CoprocessorException(AccessController.class, "This method "
2149 + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
2150 }
2151 response = AccessControlProtos.GrantResponse.getDefaultInstance();
2152 } catch (IOException ioe) {
2153
2154 ResponseConverter.setControllerException(controller, ioe);
2155 }
2156 done.run(response);
2157 }
2158
2159 @Override
2160 public void revoke(RpcController controller,
2161 AccessControlProtos.RevokeRequest request,
2162 RpcCallback<AccessControlProtos.RevokeResponse> done) {
2163 final UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission());
2164 AccessControlProtos.RevokeResponse response = null;
2165 try {
2166
2167 if (aclRegion) {
2168 if (!initialized) {
2169 throw new CoprocessorException("AccessController not yet initialized");
2170 }
2171 if (LOG.isDebugEnabled()) {
2172 LOG.debug("Received request to revoke access permission " + perm.toString());
2173 }
2174 User caller = RpcServer.getRequestUser();
2175
2176 switch(request.getUserPermission().getPermission().getType()) {
2177 case Global :
2178 case Table :
2179 requirePermission(caller, "revoke", perm.getTableName(), perm.getFamily(),
2180 perm.getQualifier(), Action.ADMIN);
2181 break;
2182 case Namespace :
2183 requireNamespacePermission(caller, "revoke", perm.getNamespace(), Action.ADMIN);
2184 break;
2185 }
2186
2187 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
2188 @Override
2189 public Void run() throws Exception {
2190 AccessControlLists.removeUserPermission(regionEnv.getConfiguration(), perm, null);
2191 return null;
2192 }
2193 });
2194
2195 if (AUDITLOG.isTraceEnabled()) {
2196
2197 AUDITLOG.trace("Revoked permission " + perm.toString());
2198 }
2199 } else {
2200 throw new CoprocessorException(AccessController.class, "This method "
2201 + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
2202 }
2203 response = AccessControlProtos.RevokeResponse.getDefaultInstance();
2204 } catch (IOException ioe) {
2205
2206 ResponseConverter.setControllerException(controller, ioe);
2207 }
2208 done.run(response);
2209 }
2210
2211 @Override
2212 public void getUserPermissions(RpcController controller,
2213 AccessControlProtos.GetUserPermissionsRequest request,
2214 RpcCallback<AccessControlProtos.GetUserPermissionsResponse> done) {
2215 AccessControlProtos.GetUserPermissionsResponse response = null;
2216 try {
2217
2218 if (aclRegion) {
2219 if (!initialized) {
2220 throw new CoprocessorException("AccessController not yet initialized");
2221 }
2222 User caller = RpcServer.getRequestUser();
2223
2224 List<UserPermission> perms = null;
2225 if (request.getType() == AccessControlProtos.Permission.Type.Table) {
2226 final TableName table = request.hasTableName() ?
2227 ProtobufUtil.toTableName(request.getTableName()) : null;
2228 requirePermission(caller, "userPermissions", table, null, null, Action.ADMIN);
2229 perms = User.runAsLoginUser(new PrivilegedExceptionAction<List<UserPermission>>() {
2230 @Override
2231 public List<UserPermission> run() throws Exception {
2232 return AccessControlLists.getUserTablePermissions(regionEnv.getConfiguration(), table);
2233 }
2234 });
2235 } else if (request.getType() == AccessControlProtos.Permission.Type.Namespace) {
2236 final String namespace = request.getNamespaceName().toStringUtf8();
2237 requireNamespacePermission(caller, "userPermissions", namespace, Action.ADMIN);
2238 perms = User.runAsLoginUser(new PrivilegedExceptionAction<List<UserPermission>>() {
2239 @Override
2240 public List<UserPermission> run() throws Exception {
2241 return AccessControlLists.getUserNamespacePermissions(regionEnv.getConfiguration(),
2242 namespace);
2243 }
2244 });
2245 } else {
2246 requirePermission(caller, "userPermissions", Action.ADMIN);
2247 perms = User.runAsLoginUser(new PrivilegedExceptionAction<List<UserPermission>>() {
2248 @Override
2249 public List<UserPermission> run() throws Exception {
2250 return AccessControlLists.getUserPermissions(regionEnv.getConfiguration(), null);
2251 }
2252 });
2253
2254
2255
2256 for (String user: Superusers.getSuperUsers()) {
2257 perms.add(new UserPermission(user.getBytes(), AccessControlLists.ACL_TABLE_NAME, null,
2258 Action.values()));
2259 }
2260 }
2261 response = ResponseConverter.buildGetUserPermissionsResponse(perms);
2262 } else {
2263 throw new CoprocessorException(AccessController.class, "This method "
2264 + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
2265 }
2266 } catch (IOException ioe) {
2267
2268 ResponseConverter.setControllerException(controller, ioe);
2269 }
2270 done.run(response);
2271 }
2272
2273 @Override
2274 public void checkPermissions(RpcController controller,
2275 AccessControlProtos.CheckPermissionsRequest request,
2276 RpcCallback<AccessControlProtos.CheckPermissionsResponse> done) {
2277 Permission[] permissions = new Permission[request.getPermissionCount()];
2278 for (int i=0; i < request.getPermissionCount(); i++) {
2279 permissions[i] = ProtobufUtil.toPermission(request.getPermission(i));
2280 }
2281 AccessControlProtos.CheckPermissionsResponse response = null;
2282 try {
2283 User user = RpcServer.getRequestUser();
2284 TableName tableName = regionEnv.getRegion().getTableDesc().getTableName();
2285 for (Permission permission : permissions) {
2286 if (permission instanceof TablePermission) {
2287
2288
2289 TablePermission tperm = (TablePermission) permission;
2290 for (Action action : permission.getActions()) {
2291 if (!tperm.getTableName().equals(tableName)) {
2292 throw new CoprocessorException(AccessController.class, String.format("This method "
2293 + "can only execute at the table specified in TablePermission. " +
2294 "Table of the region:%s , requested table:%s", tableName,
2295 tperm.getTableName()));
2296 }
2297
2298 Map<byte[], Set<byte[]>> familyMap =
2299 new TreeMap<byte[], Set<byte[]>>(Bytes.BYTES_COMPARATOR);
2300 if (tperm.getFamily() != null) {
2301 if (tperm.getQualifier() != null) {
2302 Set<byte[]> qualifiers = Sets.newTreeSet(Bytes.BYTES_COMPARATOR);
2303 qualifiers.add(tperm.getQualifier());
2304 familyMap.put(tperm.getFamily(), qualifiers);
2305 } else {
2306 familyMap.put(tperm.getFamily(), null);
2307 }
2308 }
2309
2310 AuthResult result = permissionGranted("checkPermissions", user, action, regionEnv,
2311 familyMap);
2312 accessChecker.logResult(result);
2313 if (!result.isAllowed()) {
2314
2315
2316 throw new AccessDeniedException("Insufficient permissions (table=" + tableName +
2317 (familyMap.size() > 0 ? ", family: " + result.toFamilyString() : "") +
2318 ", action=" + action.toString() + ")");
2319 }
2320 }
2321
2322 } else {
2323
2324
2325 for (Action action : permission.getActions()) {
2326 AuthResult result;
2327 if (getAuthManager().authorize(user, action)) {
2328 result = AuthResult.allow("checkPermissions", "Global action allowed", user,
2329 action, null, null);
2330 } else {
2331 result = AuthResult.deny("checkPermissions", "Global action denied", user, action,
2332 null, null);
2333 }
2334 accessChecker.logResult(result);
2335 if (!result.isAllowed()) {
2336
2337
2338 throw new AccessDeniedException("Insufficient permissions (action=" +
2339 action.toString() + ")");
2340 }
2341 }
2342 }
2343 }
2344 response = AccessControlProtos.CheckPermissionsResponse.getDefaultInstance();
2345 } catch (IOException ioe) {
2346 ResponseConverter.setControllerException(controller, ioe);
2347 }
2348 done.run(response);
2349 }
2350
2351 @Override
2352 public Service getService() {
2353 return AccessControlProtos.AccessControlService.newReflectiveService(this);
2354 }
2355
2356 private Region getRegion(RegionCoprocessorEnvironment e) {
2357 return e.getRegion();
2358 }
2359
2360 private TableName getTableName(RegionCoprocessorEnvironment e) {
2361 Region region = e.getRegion();
2362 if (region != null) {
2363 return getTableName(region);
2364 }
2365 return null;
2366 }
2367
2368 private TableName getTableName(Region region) {
2369 HRegionInfo regionInfo = region.getRegionInfo();
2370 if (regionInfo != null) {
2371 return regionInfo.getTable();
2372 }
2373 return null;
2374 }
2375
2376 @Override
2377 public void preClose(ObserverContext<RegionCoprocessorEnvironment> c, boolean abortRequested)
2378 throws IOException {
2379 requirePermission(getActiveUser(c), "preClose", Action.ADMIN);
2380 }
2381
2382 private void checkSystemOrSuperUser(User activeUser) throws IOException {
2383
2384 if (!authorizationEnabled) {
2385 return;
2386 }
2387 if (!Superusers.isSuperUser(activeUser)) {
2388 throw new AccessDeniedException("User '" + (activeUser != null ?
2389 activeUser.getShortName() : "null") + "' is not system or super user.");
2390 }
2391 }
2392
2393 @Override
2394 public void preStopRegionServer(
2395 ObserverContext<RegionServerCoprocessorEnvironment> ctx)
2396 throws IOException {
2397 requirePermission(getActiveUser(ctx), "preStopRegionServer", Action.ADMIN);
2398 }
2399
2400 private Map<byte[], ? extends Collection<byte[]>> makeFamilyMap(byte[] family,
2401 byte[] qualifier) {
2402 if (family == null) {
2403 return null;
2404 }
2405
2406 Map<byte[], Collection<byte[]>> familyMap = new TreeMap<byte[], Collection<byte[]>>(Bytes.BYTES_COMPARATOR);
2407 familyMap.put(family, qualifier != null ? ImmutableSet.of(qualifier) : null);
2408 return familyMap;
2409 }
2410
2411 @Override
2412 public void preGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
2413 List<TableName> tableNamesList, List<HTableDescriptor> descriptors,
2414 String regex) throws IOException {
2415
2416
2417 if (regex == null && tableNamesList != null && !tableNamesList.isEmpty()) {
2418
2419
2420 MasterServices masterServices = ctx.getEnvironment().getMasterServices();
2421 for (TableName tableName: tableNamesList) {
2422
2423 if (masterServices.getTableDescriptors().get(tableName) == null) {
2424 continue;
2425 }
2426 requirePermission(getActiveUser(ctx), "getTableDescriptors", tableName, null, null,
2427 Action.ADMIN, Action.CREATE);
2428 }
2429 }
2430 }
2431
2432 @Override
2433 public void postGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
2434 List<TableName> tableNamesList, List<HTableDescriptor> descriptors,
2435 String regex) throws IOException {
2436
2437 if (regex == null && tableNamesList != null && !tableNamesList.isEmpty()) {
2438 return;
2439 }
2440
2441
2442
2443 Iterator<HTableDescriptor> itr = descriptors.iterator();
2444 while (itr.hasNext()) {
2445 HTableDescriptor htd = itr.next();
2446 try {
2447 requirePermission(getActiveUser(ctx), "getTableDescriptors", htd.getTableName(), null, null,
2448 Action.ADMIN, Action.CREATE);
2449 } catch (AccessDeniedException e) {
2450 itr.remove();
2451 }
2452 }
2453 }
2454
2455 @Override
2456 public void postGetTableNames(ObserverContext<MasterCoprocessorEnvironment> ctx,
2457 List<HTableDescriptor> descriptors, String regex) throws IOException {
2458
2459 Iterator<HTableDescriptor> itr = descriptors.iterator();
2460 while (itr.hasNext()) {
2461 HTableDescriptor htd = itr.next();
2462 try {
2463 requireAccess(getActiveUser(ctx), "getTableNames", htd.getTableName(), Action.values());
2464 } catch (AccessDeniedException e) {
2465 itr.remove();
2466 }
2467 }
2468 }
2469
2470 @Override
2471 public void preDispatchMerge(final ObserverContext<MasterCoprocessorEnvironment> ctx,
2472 HRegionInfo regionA, HRegionInfo regionB) throws IOException {
2473 requirePermission(getActiveUser(ctx), "mergeRegions", regionA.getTable(), null, null,
2474 Action.ADMIN);
2475 }
2476
2477 @Override
2478 public void preClearDeadServers(ObserverContext<MasterCoprocessorEnvironment> ctx)
2479 throws IOException {
2480 requirePermission(getActiveUser(ctx), "clearDeadServers", Action.ADMIN);
2481 }
2482
2483 @Override
2484 public void postClearDeadServers(ObserverContext<MasterCoprocessorEnvironment> ctx,
2485 List<ServerName> servers, List<ServerName> notClearedServers) throws IOException { }
2486
2487 @Override
2488 public void preMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx, Region regionA,
2489 Region regionB) throws IOException {
2490 requirePermission(getActiveUser(ctx), "mergeRegions", regionA.getTableDesc().getTableName(),
2491 null, null, Action.ADMIN);
2492 }
2493
2494 @Override
2495 public void postMerge(ObserverContext<RegionServerCoprocessorEnvironment> c, Region regionA,
2496 Region regionB, Region mergedRegion) throws IOException { }
2497
2498 @Override
2499 public void preMergeCommit(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
2500 Region regionA, Region regionB, List<Mutation> metaEntries) throws IOException { }
2501
2502 @Override
2503 public void postMergeCommit(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
2504 Region regionA, Region regionB, Region mergedRegion) throws IOException { }
2505
2506 @Override
2507 public void preRollBackMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
2508 Region regionA, Region regionB) throws IOException { }
2509
2510 @Override
2511 public void postRollBackMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
2512 Region regionA, Region regionB) throws IOException { }
2513
2514 @Override
2515 public void preRollWALWriterRequest(ObserverContext<RegionServerCoprocessorEnvironment> ctx)
2516 throws IOException {
2517 requirePermission(getActiveUser(ctx), "preRollLogWriterRequest", Permission.Action.ADMIN);
2518 }
2519
2520 @Override
2521 public void postRollWALWriterRequest(ObserverContext<RegionServerCoprocessorEnvironment> ctx)
2522 throws IOException { }
2523
2524 @Override
2525 public ReplicationEndpoint postCreateReplicationEndPoint(
2526 ObserverContext<RegionServerCoprocessorEnvironment> ctx, ReplicationEndpoint endpoint) {
2527 return endpoint;
2528 }
2529
2530 @Override
2531 public void preReplicateLogEntries(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
2532 List<WALEntry> entries, CellScanner cells) throws IOException {
2533 requirePermission(getActiveUser(ctx), "replicateLogEntries", Action.WRITE);
2534 }
2535
2536 @Override
2537 public void postReplicateLogEntries(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
2538 List<WALEntry> entries, CellScanner cells) throws IOException {
2539 }
2540
2541 @Override
2542 public void preSetUserQuota(final ObserverContext<MasterCoprocessorEnvironment> ctx,
2543 final String userName, final Quotas quotas) throws IOException {
2544 requirePermission(getActiveUser(ctx), "setUserQuota", Action.ADMIN);
2545 }
2546
2547 @Override
2548 public void preSetUserQuota(final ObserverContext<MasterCoprocessorEnvironment> ctx,
2549 final String userName, final TableName tableName, final Quotas quotas) throws IOException {
2550 requirePermission(getActiveUser(ctx), "setUserTableQuota", tableName, null, null, Action.ADMIN);
2551 }
2552
2553 @Override
2554 public void preSetUserQuota(final ObserverContext<MasterCoprocessorEnvironment> ctx,
2555 final String userName, final String namespace, final Quotas quotas) throws IOException {
2556 requirePermission(getActiveUser(ctx), "setUserNamespaceQuota", Action.ADMIN);
2557 }
2558
2559 @Override
2560 public void preSetTableQuota(final ObserverContext<MasterCoprocessorEnvironment> ctx,
2561 final TableName tableName, final Quotas quotas) throws IOException {
2562 requirePermission(getActiveUser(ctx), "setTableQuota", tableName, null, null, Action.ADMIN);
2563 }
2564
2565 @Override
2566 public void preSetNamespaceQuota(final ObserverContext<MasterCoprocessorEnvironment> ctx,
2567 final String namespace, final Quotas quotas) throws IOException {
2568 requirePermission(getActiveUser(ctx), "setNamespaceQuota", Action.ADMIN);
2569 }
2570
2571 @Override
2572 public void preMoveServersAndTables(ObserverContext<MasterCoprocessorEnvironment> ctx,
2573 Set<Address> servers, Set<TableName> tables, String targetGroup) throws IOException {
2574 }
2575
2576 @Override
2577 public void preMoveServers(ObserverContext<MasterCoprocessorEnvironment> ctx,
2578 Set<Address> servers, String targetGroup) throws IOException {
2579 }
2580
2581 @Override
2582 public void preMoveTables(ObserverContext<MasterCoprocessorEnvironment> ctx,
2583 Set<TableName> tables, String targetGroup) throws IOException {
2584 }
2585
2586 @Override
2587 public void preRemoveServers(ObserverContext<MasterCoprocessorEnvironment> ctx,
2588 Set<Address> servers) throws IOException {
2589 }
2590
2591 @Override
2592 public void preAddRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
2593 String name) throws IOException {
2594 }
2595
2596 @Override
2597 public void preRemoveRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
2598 String name) throws IOException {
2599 }
2600
2601 @Override
2602 public void preBalanceRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
2603 String groupName) throws IOException {
2604 }
2605 }