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