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