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