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