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.get(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
986 }
987
988 @Override
989 public void preTruncateTable(ObserverContext<MasterCoprocessorEnvironment> c,
990 final TableName tableName) throws IOException {
991 requirePermission("truncateTable", tableName, null, null, Action.ADMIN, Action.CREATE);
992 final Configuration conf = c.getEnvironment().getConfiguration();
993 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
994 @Override
995 public Void run() throws Exception {
996 List<UserPermission> acls = AccessControlLists.getUserTablePermissions(conf, tableName);
997 if (acls != null) {
998 tableAcls.put(tableName, acls);
999 }
1000 return null;
1001 }
1002 });
1003 }
1004
1005 @Override
1006 public void postTruncateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
1007 final TableName tableName) throws IOException {
1008 final Configuration conf = ctx.getEnvironment().getConfiguration();
1009 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
1010 @Override
1011 public Void run() throws Exception {
1012 List<UserPermission> perms = tableAcls.get(tableName);
1013 if (perms != null) {
1014 for (UserPermission perm : perms) {
1015 AccessControlLists.addUserPermission(conf, perm);
1016 }
1017 }
1018 tableAcls.remove(tableName);
1019 return null;
1020 }
1021 });
1022 }
1023
1024 @Override
1025 public void preTruncateTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
1026 TableName tableName) throws IOException {}
1027
1028 @Override
1029 public void postTruncateTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
1030 TableName tableName) throws IOException {}
1031
1032 @Override
1033 public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
1034 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
1035 Set<byte[]> families = desc.getFamiliesKeys();
1036 Map<byte[], Set<byte[]>> familyMap = new TreeMap<byte[], Set<byte[]>>(Bytes.BYTES_COMPARATOR);
1037 for (byte[] family: families) {
1038 familyMap.put(family, null);
1039 }
1040 requireGlobalPermission("createTable", Action.CREATE, desc.getTableName(), familyMap);
1041 }
1042
1043 @Override
1044 public void postCreateTableHandler(final ObserverContext<MasterCoprocessorEnvironment> c,
1045 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061 if (AccessControlLists.isAclTable(desc)) {
1062 this.aclTabAvailable = true;
1063 } else if (!(TableName.NAMESPACE_TABLE_NAME.equals(desc.getTableName()))) {
1064 if (!aclTabAvailable) {
1065 LOG.warn("Not adding owner permission for table " + desc.getTableName() + ". "
1066 + AccessControlLists.ACL_TABLE_NAME + " is not yet created. "
1067 + getClass().getSimpleName() + " should be configured as the first Coprocessor");
1068 } else {
1069 String owner = desc.getOwnerString();
1070
1071 if (owner == null)
1072 owner = getActiveUser().getShortName();
1073 final UserPermission userperm = new UserPermission(Bytes.toBytes(owner),
1074 desc.getTableName(), null, Action.values());
1075
1076 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
1077 @Override
1078 public Void run() throws Exception {
1079 AccessControlLists.addUserPermission(c.getEnvironment().getConfiguration(),
1080 userperm);
1081 return null;
1082 }
1083 });
1084 }
1085 }
1086 }
1087
1088 @Override
1089 public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
1090 throws IOException {
1091 requirePermission("deleteTable", tableName, null, null, Action.ADMIN, Action.CREATE);
1092 }
1093
1094 @Override
1095 public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c,
1096 final TableName tableName) throws IOException {
1097 final Configuration conf = c.getEnvironment().getConfiguration();
1098 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
1099 @Override
1100 public Void run() throws Exception {
1101 AccessControlLists.removeTablePermissions(conf, tableName);
1102 return null;
1103 }
1104 });
1105 this.authManager.getZKPermissionWatcher().deleteTableACLNode(tableName);
1106 }
1107
1108 @Override
1109 public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
1110 HTableDescriptor htd) throws IOException {
1111 requirePermission("modifyTable", tableName, null, null, Action.ADMIN, Action.CREATE);
1112 }
1113
1114 @Override
1115 public void postModifyTable(ObserverContext<MasterCoprocessorEnvironment> c,
1116 TableName tableName, final HTableDescriptor htd) throws IOException {
1117 final Configuration conf = c.getEnvironment().getConfiguration();
1118
1119 final String owner = (htd.getOwnerString() != null) ? htd.getOwnerString() :
1120 getActiveUser().getShortName();
1121 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
1122 @Override
1123 public Void run() throws Exception {
1124 UserPermission userperm = new UserPermission(Bytes.toBytes(owner),
1125 htd.getTableName(), null, Action.values());
1126 AccessControlLists.addUserPermission(conf, userperm);
1127 return null;
1128 }
1129 });
1130 }
1131
1132 @Override
1133 public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
1134 HColumnDescriptor column) throws IOException {
1135 requirePermission("addColumn", tableName, null, null, Action.ADMIN, Action.CREATE);
1136 }
1137
1138 @Override
1139 public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
1140 HColumnDescriptor descriptor) throws IOException {
1141 requirePermission("modifyColumn", tableName, descriptor.getName(), null, Action.ADMIN,
1142 Action.CREATE);
1143 }
1144
1145 @Override
1146 public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
1147 byte[] col) throws IOException {
1148 requirePermission("deleteColumn", tableName, col, null, Action.ADMIN, Action.CREATE);
1149 }
1150
1151 @Override
1152 public void postDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c,
1153 final TableName tableName, final byte[] col) throws IOException {
1154 final Configuration conf = c.getEnvironment().getConfiguration();
1155 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
1156 @Override
1157 public Void run() throws Exception {
1158 AccessControlLists.removeTablePermissions(conf, tableName, col);
1159 return null;
1160 }
1161 });
1162 }
1163
1164 @Override
1165 public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
1166 throws IOException {
1167 requirePermission("enableTable", tableName, null, null, Action.ADMIN, Action.CREATE);
1168 }
1169
1170 @Override
1171 public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
1172 throws IOException {
1173 if (Bytes.equals(tableName.getName(), AccessControlLists.ACL_GLOBAL_NAME)) {
1174
1175
1176
1177
1178 throw new AccessDeniedException("Not allowed to disable "
1179 + AccessControlLists.ACL_TABLE_NAME + " table with AccessController installed");
1180 }
1181 requirePermission("disableTable", tableName, null, null, Action.ADMIN, Action.CREATE);
1182 }
1183
1184 @Override
1185 public void preMove(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo region,
1186 ServerName srcServer, ServerName destServer) throws IOException {
1187 requirePermission("move", region.getTable(), null, null, Action.ADMIN);
1188 }
1189
1190 @Override
1191 public void preAssign(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo regionInfo)
1192 throws IOException {
1193 requirePermission("assign", regionInfo.getTable(), null, null, Action.ADMIN);
1194 }
1195
1196 @Override
1197 public void preUnassign(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo regionInfo,
1198 boolean force) throws IOException {
1199 requirePermission("unassign", regionInfo.getTable(), null, null, Action.ADMIN);
1200 }
1201
1202 @Override
1203 public void preRegionOffline(ObserverContext<MasterCoprocessorEnvironment> c,
1204 HRegionInfo regionInfo) throws IOException {
1205 requirePermission("regionOffline", regionInfo.getTable(), null, null, Action.ADMIN);
1206 }
1207
1208 @Override
1209 public void preBalance(ObserverContext<MasterCoprocessorEnvironment> c)
1210 throws IOException {
1211 requirePermission("balance", Action.ADMIN);
1212 }
1213
1214 @Override
1215 public boolean preBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> c,
1216 boolean newValue) throws IOException {
1217 requirePermission("balanceSwitch", Action.ADMIN);
1218 return newValue;
1219 }
1220
1221 @Override
1222 public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> c)
1223 throws IOException {
1224 requirePermission("shutdown", Action.ADMIN);
1225 }
1226
1227 @Override
1228 public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> c)
1229 throws IOException {
1230 requirePermission("stopMaster", Action.ADMIN);
1231 }
1232
1233 @Override
1234 public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx)
1235 throws IOException {
1236 if (!MetaReader.tableExists(ctx.getEnvironment().getMasterServices().getCatalogTracker(),
1237 AccessControlLists.ACL_TABLE_NAME)) {
1238
1239 AccessControlLists.createACLTable(ctx.getEnvironment().getMasterServices());
1240 } else {
1241 aclTabAvailable = true;
1242 }
1243 }
1244
1245 @Override
1246 public void preSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
1247 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
1248 throws IOException {
1249 requirePermission("snapshot", Action.ADMIN);
1250 }
1251
1252 @Override
1253 public void preCloneSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
1254 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
1255 throws IOException {
1256 requirePermission("clone", Action.ADMIN);
1257 }
1258
1259 @Override
1260 public void preRestoreSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
1261 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
1262 throws IOException {
1263 requirePermission("restore", Action.ADMIN);
1264 }
1265
1266 @Override
1267 public void preDeleteSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
1268 final SnapshotDescription snapshot) throws IOException {
1269 requirePermission("deleteSnapshot", Action.ADMIN);
1270 }
1271
1272 @Override
1273 public void preCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
1274 NamespaceDescriptor ns) throws IOException {
1275 requirePermission("createNamespace", Action.ADMIN);
1276 }
1277
1278 @Override
1279 public void preDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, String namespace)
1280 throws IOException {
1281 requireGlobalPermission("deleteNamespace", Action.ADMIN, namespace);
1282 }
1283
1284 @Override
1285 public void postDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
1286 final String namespace) throws IOException {
1287 final Configuration conf = ctx.getEnvironment().getConfiguration();
1288 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
1289 @Override
1290 public Void run() throws Exception {
1291 AccessControlLists.removeNamespacePermissions(conf, namespace);
1292 return null;
1293 }
1294 });
1295 this.authManager.getZKPermissionWatcher().deleteNamespaceACLNode(namespace);
1296 LOG.info(namespace + " entry deleted in " + AccessControlLists.ACL_TABLE_NAME + " table.");
1297 }
1298
1299 @Override
1300 public void preModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
1301 NamespaceDescriptor ns) throws IOException {
1302 requireGlobalPermission("modifyNamespace", Action.ADMIN, ns.getName());
1303 }
1304
1305
1306
1307 @Override
1308 public void preOpen(ObserverContext<RegionCoprocessorEnvironment> e)
1309 throws IOException {
1310 RegionCoprocessorEnvironment env = e.getEnvironment();
1311 final HRegion region = env.getRegion();
1312 if (region == null) {
1313 LOG.error("NULL region from RegionCoprocessorEnvironment in preOpen()");
1314 } else {
1315 HRegionInfo regionInfo = region.getRegionInfo();
1316 if (regionInfo.getTable().isSystemTable()) {
1317 checkSystemOrSuperUser();
1318 } else {
1319 requirePermission("preOpen", Action.ADMIN);
1320 }
1321 }
1322 }
1323
1324 @Override
1325 public void postOpen(ObserverContext<RegionCoprocessorEnvironment> c) {
1326 RegionCoprocessorEnvironment env = c.getEnvironment();
1327 final HRegion region = env.getRegion();
1328 if (region == null) {
1329 LOG.error("NULL region from RegionCoprocessorEnvironment in postOpen()");
1330 return;
1331 }
1332 if (AccessControlLists.isAclRegion(region)) {
1333 aclRegion = true;
1334
1335 if (!region.isRecovering()) {
1336 try {
1337 initialize(env);
1338 } catch (IOException ex) {
1339
1340
1341 throw new RuntimeException("Failed to initialize permissions cache", ex);
1342 }
1343 }
1344 } else {
1345 initialized = true;
1346 }
1347 }
1348
1349 @Override
1350 public void postLogReplay(ObserverContext<RegionCoprocessorEnvironment> c) {
1351 if (aclRegion) {
1352 try {
1353 initialize(c.getEnvironment());
1354 } catch (IOException ex) {
1355
1356
1357 throw new RuntimeException("Failed to initialize permissions cache", ex);
1358 }
1359 }
1360 }
1361
1362 @Override
1363 public void preFlush(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
1364 requirePermission("flush", getTableName(e.getEnvironment()), null, null, Action.ADMIN,
1365 Action.CREATE);
1366 }
1367
1368 @Override
1369 public void preSplit(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
1370 requirePermission("split", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
1371 }
1372
1373 @Override
1374 public void preSplit(ObserverContext<RegionCoprocessorEnvironment> e,
1375 byte[] splitRow) throws IOException {
1376 requirePermission("split", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
1377 }
1378
1379 @Override
1380 public InternalScanner preCompact(ObserverContext<RegionCoprocessorEnvironment> e,
1381 final Store store, final InternalScanner scanner, final ScanType scanType)
1382 throws IOException {
1383 requirePermission("compact", getTableName(e.getEnvironment()), null, null, Action.ADMIN,
1384 Action.CREATE);
1385 return scanner;
1386 }
1387
1388 @Override
1389 public void preGetClosestRowBefore(final ObserverContext<RegionCoprocessorEnvironment> c,
1390 final byte [] row, final byte [] family, final Result result)
1391 throws IOException {
1392 assert family != null;
1393 RegionCoprocessorEnvironment env = c.getEnvironment();
1394 Map<byte[],? extends Collection<byte[]>> families = makeFamilyMap(family, null);
1395 User user = getActiveUser();
1396 AuthResult authResult = permissionGranted(OpType.GET_CLOSEST_ROW_BEFORE, user, env, families,
1397 Action.READ);
1398 if (!authResult.isAllowed() && cellFeaturesEnabled && !compatibleEarlyTermination) {
1399 authResult.setAllowed(checkCoveringPermission(OpType.GET_CLOSEST_ROW_BEFORE, env, row,
1400 families, HConstants.LATEST_TIMESTAMP, Action.READ));
1401 authResult.setReason("Covering cell set");
1402 }
1403 logResult(authResult);
1404 if (authorizationEnabled && !authResult.isAllowed()) {
1405 throw new AccessDeniedException("Insufficient permissions " +
1406 authResult.toContextString());
1407 }
1408 }
1409
1410 private void internalPreRead(final ObserverContext<RegionCoprocessorEnvironment> c,
1411 final Query query, OpType opType) throws IOException {
1412 Filter filter = query.getFilter();
1413
1414 if (filter != null && filter instanceof AccessControlFilter) {
1415 return;
1416 }
1417 User user = getActiveUser();
1418 RegionCoprocessorEnvironment env = c.getEnvironment();
1419 Map<byte[],? extends Collection<byte[]>> families = null;
1420 switch (opType) {
1421 case GET:
1422 case EXISTS:
1423 families = ((Get)query).getFamilyMap();
1424 break;
1425 case SCAN:
1426 families = ((Scan)query).getFamilyMap();
1427 break;
1428 default:
1429 throw new RuntimeException("Unhandled operation " + opType);
1430 }
1431 AuthResult authResult = permissionGranted(opType, user, env, families, Action.READ);
1432 HRegion region = getRegion(env);
1433 TableName table = getTableName(region);
1434 Map<ByteRange, Integer> cfVsMaxVersions = Maps.newHashMap();
1435 for (HColumnDescriptor hcd : region.getTableDesc().getFamilies()) {
1436 cfVsMaxVersions.put(new SimpleByteRange(hcd.getName()), hcd.getMaxVersions());
1437 }
1438 if (!authResult.isAllowed()) {
1439 if (!cellFeaturesEnabled || compatibleEarlyTermination) {
1440
1441
1442
1443
1444
1445
1446
1447
1448 if (hasFamilyQualifierPermission(user, Action.READ, env, families)) {
1449 authResult.setAllowed(true);
1450 authResult.setReason("Access allowed with filter");
1451
1452 if (authorizationEnabled) {
1453 Filter ourFilter = new AccessControlFilter(authManager, user, table,
1454 AccessControlFilter.Strategy.CHECK_TABLE_AND_CF_ONLY,
1455 cfVsMaxVersions);
1456
1457 if (filter != null) {
1458 ourFilter = new FilterList(FilterList.Operator.MUST_PASS_ALL,
1459 Lists.newArrayList(ourFilter, filter));
1460 }
1461 switch (opType) {
1462 case GET:
1463 case EXISTS:
1464 ((Get)query).setFilter(ourFilter);
1465 break;
1466 case SCAN:
1467 ((Scan)query).setFilter(ourFilter);
1468 break;
1469 default:
1470 throw new RuntimeException("Unhandled operation " + opType);
1471 }
1472 }
1473 }
1474 } else {
1475
1476
1477
1478
1479 authResult.setAllowed(true);
1480 authResult.setReason("Access allowed with filter");
1481
1482 if (authorizationEnabled) {
1483 Filter ourFilter = new AccessControlFilter(authManager, user, table,
1484 AccessControlFilter.Strategy.CHECK_CELL_DEFAULT, cfVsMaxVersions);
1485
1486 if (filter != null) {
1487 ourFilter = new FilterList(FilterList.Operator.MUST_PASS_ALL,
1488 Lists.newArrayList(ourFilter, filter));
1489 }
1490 switch (opType) {
1491 case GET:
1492 case EXISTS:
1493 ((Get)query).setFilter(ourFilter);
1494 break;
1495 case SCAN:
1496 ((Scan)query).setFilter(ourFilter);
1497 break;
1498 default:
1499 throw new RuntimeException("Unhandled operation " + opType);
1500 }
1501 }
1502 }
1503 }
1504
1505 logResult(authResult);
1506 if (authorizationEnabled && !authResult.isAllowed()) {
1507 throw new AccessDeniedException("Insufficient permissions for user '"
1508 + (user != null ? user.getShortName() : "null")
1509 + "' (table=" + table + ", action=READ)");
1510 }
1511 }
1512
1513 @Override
1514 public void preGetOp(final ObserverContext<RegionCoprocessorEnvironment> c,
1515 final Get get, final List<Cell> result) throws IOException {
1516 internalPreRead(c, get, OpType.GET);
1517 }
1518
1519 @Override
1520 public boolean preExists(final ObserverContext<RegionCoprocessorEnvironment> c,
1521 final Get get, final boolean exists) throws IOException {
1522 internalPreRead(c, get, OpType.EXISTS);
1523 return exists;
1524 }
1525
1526 @Override
1527 public void prePut(final ObserverContext<RegionCoprocessorEnvironment> c,
1528 final Put put, final WALEdit edit, final Durability durability)
1529 throws IOException {
1530 User user = getActiveUser();
1531 checkForReservedTagPresence(user, put);
1532
1533
1534
1535
1536
1537
1538
1539 RegionCoprocessorEnvironment env = c.getEnvironment();
1540 Map<byte[],? extends Collection<Cell>> families = put.getFamilyCellMap();
1541 AuthResult authResult = permissionGranted(OpType.PUT, user, env, families, Action.WRITE);
1542 logResult(authResult);
1543 if (!authResult.isAllowed()) {
1544 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1545 put.setAttribute(CHECK_COVERING_PERM, TRUE);
1546 } else if (authorizationEnabled) {
1547 throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
1548 }
1549 }
1550
1551
1552 byte[] bytes = put.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
1553 if (bytes != null) {
1554 if (cellFeaturesEnabled) {
1555 addCellPermissions(bytes, put.getFamilyCellMap());
1556 } else {
1557 throw new DoNotRetryIOException("Cell ACLs cannot be persisted");
1558 }
1559 }
1560 }
1561
1562 @Override
1563 public void postPut(final ObserverContext<RegionCoprocessorEnvironment> c,
1564 final Put put, final WALEdit edit, final Durability durability) {
1565 if (aclRegion) {
1566 updateACL(c.getEnvironment(), put.getFamilyCellMap());
1567 }
1568 }
1569
1570 @Override
1571 public void preDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
1572 final Delete delete, final WALEdit edit, final Durability durability)
1573 throws IOException {
1574
1575 if (delete.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL) != null) {
1576 throw new DoNotRetryIOException("ACL on delete has no effect: " + delete.toString());
1577 }
1578
1579
1580
1581
1582
1583 RegionCoprocessorEnvironment env = c.getEnvironment();
1584 Map<byte[],? extends Collection<Cell>> families = delete.getFamilyCellMap();
1585 User user = getActiveUser();
1586 AuthResult authResult = permissionGranted(OpType.DELETE, user, env, families, Action.WRITE);
1587 logResult(authResult);
1588 if (!authResult.isAllowed()) {
1589 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1590 delete.setAttribute(CHECK_COVERING_PERM, TRUE);
1591 } else if (authorizationEnabled) {
1592 throw new AccessDeniedException("Insufficient permissions " +
1593 authResult.toContextString());
1594 }
1595 }
1596 }
1597
1598 @Override
1599 public void preBatchMutate(ObserverContext<RegionCoprocessorEnvironment> c,
1600 MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException {
1601 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1602 TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
1603 for (int i = 0; i < miniBatchOp.size(); i++) {
1604 Mutation m = miniBatchOp.getOperation(i);
1605 if (m.getAttribute(CHECK_COVERING_PERM) != null) {
1606
1607
1608 OpType opType;
1609 if (m instanceof Put) {
1610 checkForReservedTagPresence(getActiveUser(), m);
1611 opType = OpType.PUT;
1612 } else {
1613 opType = OpType.DELETE;
1614 }
1615 AuthResult authResult = null;
1616 if (checkCoveringPermission(opType, c.getEnvironment(), m.getRow(),
1617 m.getFamilyCellMap(), m.getTimeStamp(), Action.WRITE)) {
1618 authResult = AuthResult.allow(opType.toString(), "Covering cell set",
1619 getActiveUser(), Action.WRITE, table, m.getFamilyCellMap());
1620 } else {
1621 authResult = AuthResult.deny(opType.toString(), "Covering cell set",
1622 getActiveUser(), Action.WRITE, table, m.getFamilyCellMap());
1623 }
1624 logResult(authResult);
1625 if (authorizationEnabled && !authResult.isAllowed()) {
1626 throw new AccessDeniedException("Insufficient permissions "
1627 + authResult.toContextString());
1628 }
1629 }
1630 }
1631 }
1632 }
1633
1634 @Override
1635 public void postDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
1636 final Delete delete, final WALEdit edit, final Durability durability)
1637 throws IOException {
1638 if (aclRegion) {
1639 updateACL(c.getEnvironment(), delete.getFamilyCellMap());
1640 }
1641 }
1642
1643 @Override
1644 public boolean preCheckAndPut(final ObserverContext<RegionCoprocessorEnvironment> c,
1645 final byte [] row, final byte [] family, final byte [] qualifier,
1646 final CompareFilter.CompareOp compareOp,
1647 final ByteArrayComparable comparator, final Put put,
1648 final boolean result) throws IOException {
1649 User user = getActiveUser();
1650 checkForReservedTagPresence(user, put);
1651
1652
1653 RegionCoprocessorEnvironment env = c.getEnvironment();
1654 Map<byte[],? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier);
1655 AuthResult authResult = permissionGranted(OpType.CHECK_AND_PUT, user, env, families,
1656 Action.READ, Action.WRITE);
1657 logResult(authResult);
1658 if (!authResult.isAllowed()) {
1659 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1660 put.setAttribute(CHECK_COVERING_PERM, TRUE);
1661 } else if (authorizationEnabled) {
1662 throw new AccessDeniedException("Insufficient permissions " +
1663 authResult.toContextString());
1664 }
1665 }
1666
1667 byte[] bytes = put.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
1668 if (bytes != null) {
1669 if (cellFeaturesEnabled) {
1670 addCellPermissions(bytes, put.getFamilyCellMap());
1671 } else {
1672 throw new DoNotRetryIOException("Cell ACLs cannot be persisted");
1673 }
1674 }
1675 return result;
1676 }
1677
1678 @Override
1679 public boolean preCheckAndPutAfterRowLock(final ObserverContext<RegionCoprocessorEnvironment> c,
1680 final byte[] row, final byte[] family, final byte[] qualifier,
1681 final CompareFilter.CompareOp compareOp, final ByteArrayComparable comparator, final Put put,
1682 final boolean result) throws IOException {
1683 if (put.getAttribute(CHECK_COVERING_PERM) != null) {
1684
1685
1686 TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
1687 Map<byte[], ? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier);
1688 AuthResult authResult = null;
1689 if (checkCoveringPermission(OpType.CHECK_AND_PUT, c.getEnvironment(), row, families,
1690 HConstants.LATEST_TIMESTAMP, Action.READ)) {
1691 authResult = AuthResult.allow(OpType.CHECK_AND_PUT.toString(), "Covering cell set",
1692 getActiveUser(), Action.READ, table, families);
1693 } else {
1694 authResult = AuthResult.deny(OpType.CHECK_AND_PUT.toString(), "Covering cell set",
1695 getActiveUser(), Action.READ, table, families);
1696 }
1697 logResult(authResult);
1698 if (authorizationEnabled && !authResult.isAllowed()) {
1699 throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
1700 }
1701 }
1702 return result;
1703 }
1704
1705 @Override
1706 public boolean preCheckAndDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
1707 final byte [] row, final byte [] family, final byte [] qualifier,
1708 final CompareFilter.CompareOp compareOp,
1709 final ByteArrayComparable comparator, final Delete delete,
1710 final boolean result) throws IOException {
1711
1712 if (delete.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL) != null) {
1713 throw new DoNotRetryIOException("ACL on checkAndDelete has no effect: " +
1714 delete.toString());
1715 }
1716
1717
1718 RegionCoprocessorEnvironment env = c.getEnvironment();
1719 Map<byte[],? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier);
1720 User user = getActiveUser();
1721 AuthResult authResult = permissionGranted(OpType.CHECK_AND_DELETE, user, env, families,
1722 Action.READ, Action.WRITE);
1723 logResult(authResult);
1724 if (!authResult.isAllowed()) {
1725 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1726 delete.setAttribute(CHECK_COVERING_PERM, TRUE);
1727 } else if (authorizationEnabled) {
1728 throw new AccessDeniedException("Insufficient permissions " +
1729 authResult.toContextString());
1730 }
1731 }
1732 return result;
1733 }
1734
1735 @Override
1736 public boolean preCheckAndDeleteAfterRowLock(
1737 final ObserverContext<RegionCoprocessorEnvironment> c, final byte[] row, final byte[] family,
1738 final byte[] qualifier, final CompareFilter.CompareOp compareOp,
1739 final ByteArrayComparable comparator, final Delete delete, final boolean result)
1740 throws IOException {
1741 if (delete.getAttribute(CHECK_COVERING_PERM) != null) {
1742
1743
1744 TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
1745 Map<byte[], ? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier);
1746 AuthResult authResult = null;
1747 if (checkCoveringPermission(OpType.CHECK_AND_DELETE, c.getEnvironment(), row, families,
1748 HConstants.LATEST_TIMESTAMP, Action.READ)) {
1749 authResult = AuthResult.allow(OpType.CHECK_AND_DELETE.toString(), "Covering cell set",
1750 getActiveUser(), Action.READ, table, families);
1751 } else {
1752 authResult = AuthResult.deny(OpType.CHECK_AND_DELETE.toString(), "Covering cell set",
1753 getActiveUser(), Action.READ, table, families);
1754 }
1755 logResult(authResult);
1756 if (authorizationEnabled && !authResult.isAllowed()) {
1757 throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
1758 }
1759 }
1760 return result;
1761 }
1762
1763 @Override
1764 public long preIncrementColumnValue(final ObserverContext<RegionCoprocessorEnvironment> c,
1765 final byte [] row, final byte [] family, final byte [] qualifier,
1766 final long amount, final boolean writeToWAL)
1767 throws IOException {
1768
1769
1770 RegionCoprocessorEnvironment env = c.getEnvironment();
1771 Map<byte[],? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier);
1772 User user = getActiveUser();
1773 AuthResult authResult = permissionGranted(OpType.INCREMENT_COLUMN_VALUE, user, env, families,
1774 Action.WRITE);
1775 if (!authResult.isAllowed() && cellFeaturesEnabled && !compatibleEarlyTermination) {
1776 authResult.setAllowed(checkCoveringPermission(OpType.INCREMENT_COLUMN_VALUE, env, row,
1777 families, HConstants.LATEST_TIMESTAMP, Action.WRITE));
1778 authResult.setReason("Covering cell set");
1779 }
1780 logResult(authResult);
1781 if (authorizationEnabled && !authResult.isAllowed()) {
1782 throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
1783 }
1784 return -1;
1785 }
1786
1787 @Override
1788 public Result preAppend(ObserverContext<RegionCoprocessorEnvironment> c, Append append)
1789 throws IOException {
1790 User user = getActiveUser();
1791 checkForReservedTagPresence(user, append);
1792
1793
1794 RegionCoprocessorEnvironment env = c.getEnvironment();
1795 Map<byte[],? extends Collection<Cell>> families = append.getFamilyCellMap();
1796 AuthResult authResult = permissionGranted(OpType.APPEND, user, env, families, Action.WRITE);
1797 logResult(authResult);
1798 if (!authResult.isAllowed()) {
1799 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1800 append.setAttribute(CHECK_COVERING_PERM, TRUE);
1801 } else if (authorizationEnabled) {
1802 throw new AccessDeniedException("Insufficient permissions " +
1803 authResult.toContextString());
1804 }
1805 }
1806
1807 byte[] bytes = append.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
1808 if (bytes != null) {
1809 if (cellFeaturesEnabled) {
1810 addCellPermissions(bytes, append.getFamilyCellMap());
1811 } else {
1812 throw new DoNotRetryIOException("Cell ACLs cannot be persisted");
1813 }
1814 }
1815
1816 return null;
1817 }
1818
1819 @Override
1820 public Result preAppendAfterRowLock(final ObserverContext<RegionCoprocessorEnvironment> c,
1821 final Append append) throws IOException {
1822 if (append.getAttribute(CHECK_COVERING_PERM) != null) {
1823
1824
1825 TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
1826 AuthResult authResult = null;
1827 if (checkCoveringPermission(OpType.APPEND, c.getEnvironment(), append.getRow(),
1828 append.getFamilyCellMap(), HConstants.LATEST_TIMESTAMP, Action.WRITE)) {
1829 authResult = AuthResult.allow(OpType.APPEND.toString(), "Covering cell set",
1830 getActiveUser(), Action.WRITE, table, append.getFamilyCellMap());
1831 } else {
1832 authResult = AuthResult.deny(OpType.APPEND.toString(), "Covering cell set",
1833 getActiveUser(), Action.WRITE, table, append.getFamilyCellMap());
1834 }
1835 logResult(authResult);
1836 if (authorizationEnabled && !authResult.isAllowed()) {
1837 throw new AccessDeniedException("Insufficient permissions " +
1838 authResult.toContextString());
1839 }
1840 }
1841 return null;
1842 }
1843
1844 @Override
1845 public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> c,
1846 final Increment increment)
1847 throws IOException {
1848 User user = getActiveUser();
1849 checkForReservedTagPresence(user, increment);
1850
1851
1852
1853 RegionCoprocessorEnvironment env = c.getEnvironment();
1854 Map<byte[],? extends Collection<Cell>> families = increment.getFamilyCellMap();
1855 AuthResult authResult = permissionGranted(OpType.INCREMENT, user, env, families,
1856 Action.WRITE);
1857 logResult(authResult);
1858 if (!authResult.isAllowed()) {
1859 if (cellFeaturesEnabled && !compatibleEarlyTermination) {
1860 increment.setAttribute(CHECK_COVERING_PERM, TRUE);
1861 } else if (authorizationEnabled) {
1862 throw new AccessDeniedException("Insufficient permissions " +
1863 authResult.toContextString());
1864 }
1865 }
1866
1867 byte[] bytes = increment.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
1868 if (bytes != null) {
1869 if (cellFeaturesEnabled) {
1870 addCellPermissions(bytes, increment.getFamilyCellMap());
1871 } else {
1872 throw new DoNotRetryIOException("Cell ACLs cannot be persisted");
1873 }
1874 }
1875
1876 return null;
1877 }
1878
1879 @Override
1880 public Result preIncrementAfterRowLock(final ObserverContext<RegionCoprocessorEnvironment> c,
1881 final Increment increment) throws IOException {
1882 if (increment.getAttribute(CHECK_COVERING_PERM) != null) {
1883
1884
1885 TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
1886 AuthResult authResult = null;
1887 if (checkCoveringPermission(OpType.INCREMENT, c.getEnvironment(), increment.getRow(),
1888 increment.getFamilyCellMap(), increment.getTimeRange().getMax(), Action.WRITE)) {
1889 authResult = AuthResult.allow(OpType.INCREMENT.toString(), "Covering cell set",
1890 getActiveUser(), Action.WRITE, table, increment.getFamilyCellMap());
1891 } else {
1892 authResult = AuthResult.deny(OpType.INCREMENT.toString(), "Covering cell set",
1893 getActiveUser(), Action.WRITE, table, increment.getFamilyCellMap());
1894 }
1895 logResult(authResult);
1896 if (authorizationEnabled && !authResult.isAllowed()) {
1897 throw new AccessDeniedException("Insufficient permissions " +
1898 authResult.toContextString());
1899 }
1900 }
1901 return null;
1902 }
1903
1904 @Override
1905 public Cell postMutationBeforeWAL(ObserverContext<RegionCoprocessorEnvironment> ctx,
1906 MutationType opType, Mutation mutation, Cell oldCell, Cell newCell) throws IOException {
1907
1908
1909 if (!cellFeaturesEnabled) {
1910 return newCell;
1911 }
1912
1913
1914 List<Tag> tags = Lists.newArrayList();
1915 ListMultimap<String,Permission> perms = ArrayListMultimap.create();
1916 if (oldCell != null) {
1917
1918 if (oldCell.getTagsLengthUnsigned() > 0) {
1919 Iterator<Tag> tagIterator = CellUtil.tagsIterator(oldCell.getTagsArray(),
1920 oldCell.getTagsOffset(), oldCell.getTagsLengthUnsigned());
1921 while (tagIterator.hasNext()) {
1922 Tag tag = tagIterator.next();
1923 if (tag.getType() != AccessControlLists.ACL_TAG_TYPE) {
1924
1925 if (LOG.isTraceEnabled()) {
1926 LOG.trace("Carrying forward tag from " + oldCell + ": type " + tag.getType() +
1927 " length " + tag.getTagLength());
1928 }
1929 tags.add(tag);
1930 } else {
1931
1932
1933
1934 AccessControlProtos.UsersAndPermissions.Builder builder =
1935 AccessControlProtos.UsersAndPermissions.newBuilder();
1936 ProtobufUtil.mergeFrom(builder, tag.getBuffer(), tag.getTagOffset(), tag.getTagLength());
1937 ListMultimap<String,Permission> kvPerms =
1938 ProtobufUtil.toUsersAndPermissions(builder.build());
1939 perms.putAll(kvPerms);
1940 }
1941 }
1942 }
1943 }
1944
1945
1946 byte[] aclBytes = mutation.getACL();
1947 if (aclBytes != null) {
1948
1949 tags.add(new Tag(AccessControlLists.ACL_TAG_TYPE, aclBytes));
1950 } else {
1951
1952 if (perms != null) {
1953
1954
1955
1956 if (LOG.isTraceEnabled()) {
1957 LOG.trace("Carrying forward ACLs from " + oldCell + ": " + perms);
1958 }
1959 tags.add(new Tag(AccessControlLists.ACL_TAG_TYPE,
1960 ProtobufUtil.toUsersAndPermissions(perms).toByteArray()));
1961 }
1962 }
1963
1964
1965 if (tags.isEmpty()) {
1966 return newCell;
1967 }
1968
1969
1970
1971 KeyValue newKv = KeyValueUtil.ensureKeyValue(newCell);
1972 byte[] bytes = newKv.getBuffer();
1973 KeyValue rewriteKv = new KeyValue(bytes, newKv.getRowOffset(), newKv.getRowLength(),
1974 bytes, newKv.getFamilyOffset(), newKv.getFamilyLength(),
1975 bytes, newKv.getQualifierOffset(), newKv.getQualifierLength(),
1976 newKv.getTimestamp(), KeyValue.Type.codeToType(newKv.getTypeByte()),
1977 bytes, newKv.getValueOffset(), newKv.getValueLength(),
1978 tags);
1979
1980 rewriteKv.setMvccVersion(newKv.getMvccVersion());
1981 return rewriteKv;
1982 }
1983
1984 @Override
1985 public RegionScanner preScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
1986 final Scan scan, final RegionScanner s) throws IOException {
1987 internalPreRead(c, scan, OpType.SCAN);
1988 return s;
1989 }
1990
1991 @Override
1992 public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
1993 final Scan scan, final RegionScanner s) throws IOException {
1994 User user = getActiveUser();
1995 if (user != null && user.getShortName() != null) {
1996
1997 scannerOwners.put(s, user.getShortName());
1998 }
1999 return s;
2000 }
2001
2002 @Override
2003 public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c,
2004 final InternalScanner s, final List<Result> result,
2005 final int limit, final boolean hasNext) throws IOException {
2006 requireScannerOwner(s);
2007 return hasNext;
2008 }
2009
2010 @Override
2011 public void preScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
2012 final InternalScanner s) throws IOException {
2013 requireScannerOwner(s);
2014 }
2015
2016 @Override
2017 public void postScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
2018 final InternalScanner s) throws IOException {
2019
2020 scannerOwners.remove(s);
2021 }
2022
2023
2024
2025
2026
2027
2028 private void requireScannerOwner(InternalScanner s) throws AccessDeniedException {
2029 if (!RpcServer.isInRpcCallContext())
2030 return;
2031 String requestUserName = RpcServer.getRequestUserName();
2032 String owner = scannerOwners.get(s);
2033 if (authorizationEnabled && owner != null && !owner.equals(requestUserName)) {
2034 throw new AccessDeniedException("User '"+ requestUserName +"' is not the scanner owner!");
2035 }
2036 }
2037
2038
2039
2040
2041
2042
2043
2044 @Override
2045 public void preBulkLoadHFile(ObserverContext<RegionCoprocessorEnvironment> ctx,
2046 List<Pair<byte[], String>> familyPaths) throws IOException {
2047 for(Pair<byte[],String> el : familyPaths) {
2048 requirePermission("preBulkLoadHFile",
2049 ctx.getEnvironment().getRegion().getTableDesc().getTableName(),
2050 el.getFirst(),
2051 null,
2052 Action.CREATE);
2053 }
2054 }
2055
2056
2057
2058
2059
2060
2061
2062
2063 @Override
2064 public void prePrepareBulkLoad(ObserverContext<RegionCoprocessorEnvironment> ctx,
2065 PrepareBulkLoadRequest request) throws IOException {
2066 requireAccess("prePareBulkLoad",
2067 ctx.getEnvironment().getRegion().getTableDesc().getTableName(), Action.CREATE);
2068 }
2069
2070
2071
2072
2073
2074
2075
2076
2077 @Override
2078 public void preCleanupBulkLoad(ObserverContext<RegionCoprocessorEnvironment> ctx,
2079 CleanupBulkLoadRequest request) throws IOException {
2080 requireAccess("preCleanupBulkLoad",
2081 ctx.getEnvironment().getRegion().getTableDesc().getTableName(), Action.CREATE);
2082 }
2083
2084
2085
2086 @Override
2087 public Message preEndpointInvocation(ObserverContext<RegionCoprocessorEnvironment> ctx,
2088 Service service, String methodName, Message request) throws IOException {
2089
2090
2091 if (shouldCheckExecPermission && !(service instanceof AccessControlService)) {
2092 requirePermission("invoke(" + service.getDescriptorForType().getName() + "." +
2093 methodName + ")",
2094 getTableName(ctx.getEnvironment()), null, null,
2095 Action.EXEC);
2096 }
2097 return request;
2098 }
2099
2100 @Override
2101 public void postEndpointInvocation(ObserverContext<RegionCoprocessorEnvironment> ctx,
2102 Service service, String methodName, Message request, Message.Builder responseBuilder)
2103 throws IOException { }
2104
2105
2106
2107 @Override
2108 public void grant(RpcController controller,
2109 AccessControlProtos.GrantRequest request,
2110 RpcCallback<AccessControlProtos.GrantResponse> done) {
2111 final UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission());
2112 AccessControlProtos.GrantResponse response = null;
2113 try {
2114
2115 if (aclRegion) {
2116 if (!initialized) {
2117 throw new CoprocessorException("AccessController not yet initialized");
2118 }
2119 if (LOG.isDebugEnabled()) {
2120 LOG.debug("Received request to grant access permission " + perm.toString());
2121 }
2122
2123 switch(request.getUserPermission().getPermission().getType()) {
2124 case Global :
2125 case Table :
2126 requirePermission("grant", perm.getTableName(), perm.getFamily(),
2127 perm.getQualifier(), Action.ADMIN);
2128 break;
2129 case Namespace :
2130 requireGlobalPermission("grant", Action.ADMIN, perm.getNamespace());
2131 break;
2132 }
2133
2134 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
2135 @Override
2136 public Void run() throws Exception {
2137 AccessControlLists.addUserPermission(regionEnv.getConfiguration(), perm);
2138 return null;
2139 }
2140 });
2141
2142 if (AUDITLOG.isTraceEnabled()) {
2143
2144 AUDITLOG.trace("Granted permission " + perm.toString());
2145 }
2146 } else {
2147 throw new CoprocessorException(AccessController.class, "This method "
2148 + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
2149 }
2150 response = AccessControlProtos.GrantResponse.getDefaultInstance();
2151 } catch (IOException ioe) {
2152
2153 ResponseConverter.setControllerException(controller, ioe);
2154 }
2155 done.run(response);
2156 }
2157
2158 @Override
2159 public void revoke(RpcController controller,
2160 AccessControlProtos.RevokeRequest request,
2161 RpcCallback<AccessControlProtos.RevokeResponse> done) {
2162 final UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission());
2163 AccessControlProtos.RevokeResponse response = null;
2164 try {
2165
2166 if (aclRegion) {
2167 if (!initialized) {
2168 throw new CoprocessorException("AccessController not yet initialized");
2169 }
2170 if (LOG.isDebugEnabled()) {
2171 LOG.debug("Received request to revoke access permission " + perm.toString());
2172 }
2173
2174 switch(request.getUserPermission().getPermission().getType()) {
2175 case Global :
2176 case Table :
2177 requirePermission("revoke", perm.getTableName(), perm.getFamily(),
2178 perm.getQualifier(), Action.ADMIN);
2179 break;
2180 case Namespace :
2181 requireGlobalPermission("revoke", Action.ADMIN, perm.getNamespace());
2182 break;
2183 }
2184
2185 User.runAsLoginUser(new PrivilegedExceptionAction<Void>() {
2186 @Override
2187 public Void run() throws Exception {
2188 AccessControlLists.removeUserPermission(regionEnv.getConfiguration(), perm);
2189 return null;
2190 }
2191 });
2192
2193 if (AUDITLOG.isTraceEnabled()) {
2194
2195 AUDITLOG.trace("Revoked permission " + perm.toString());
2196 }
2197 } else {
2198 throw new CoprocessorException(AccessController.class, "This method "
2199 + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
2200 }
2201 response = AccessControlProtos.RevokeResponse.getDefaultInstance();
2202 } catch (IOException ioe) {
2203
2204 ResponseConverter.setControllerException(controller, ioe);
2205 }
2206 done.run(response);
2207 }
2208
2209 @Override
2210 public void getUserPermissions(RpcController controller,
2211 AccessControlProtos.GetUserPermissionsRequest request,
2212 RpcCallback<AccessControlProtos.GetUserPermissionsResponse> done) {
2213 AccessControlProtos.GetUserPermissionsResponse response = null;
2214 try {
2215
2216 if (aclRegion) {
2217 if (!initialized) {
2218 throw new CoprocessorException("AccessController not yet initialized");
2219 }
2220 List<UserPermission> perms = null;
2221 if (request.getType() == AccessControlProtos.Permission.Type.Table) {
2222 final TableName table = request.hasTableName() ?
2223 ProtobufUtil.toTableName(request.getTableName()) : null;
2224 requirePermission("userPermissions", table, null, null, Action.ADMIN);
2225 perms = User.runAsLoginUser(new PrivilegedExceptionAction<List<UserPermission>>() {
2226 @Override
2227 public List<UserPermission> run() throws Exception {
2228 return AccessControlLists.getUserTablePermissions(regionEnv.getConfiguration(), table);
2229 }
2230 });
2231 } else if (request.getType() == AccessControlProtos.Permission.Type.Namespace) {
2232 final String namespace = request.getNamespaceName().toStringUtf8();
2233 requireGlobalPermission("userPermissions", Action.ADMIN, namespace);
2234 perms = User.runAsLoginUser(new PrivilegedExceptionAction<List<UserPermission>>() {
2235 @Override
2236 public List<UserPermission> run() throws Exception {
2237 return AccessControlLists.getUserNamespacePermissions(regionEnv.getConfiguration(),
2238 namespace);
2239 }
2240 });
2241 } else {
2242 requirePermission("userPermissions", Action.ADMIN);
2243 perms = User.runAsLoginUser(new PrivilegedExceptionAction<List<UserPermission>>() {
2244 @Override
2245 public List<UserPermission> run() throws Exception {
2246 return AccessControlLists.getUserPermissions(regionEnv.getConfiguration(), null);
2247 }
2248 });
2249
2250
2251
2252 for (String user: Superusers.getSuperUsers()) {
2253 perms.add(new UserPermission(user.getBytes(), AccessControlLists.ACL_TABLE_NAME, null,
2254 Action.values()));
2255 }
2256 }
2257 response = ResponseConverter.buildGetUserPermissionsResponse(perms);
2258 } else {
2259 throw new CoprocessorException(AccessController.class, "This method "
2260 + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
2261 }
2262 } catch (IOException ioe) {
2263
2264 ResponseConverter.setControllerException(controller, ioe);
2265 }
2266 done.run(response);
2267 }
2268
2269 @Override
2270 public void checkPermissions(RpcController controller,
2271 AccessControlProtos.CheckPermissionsRequest request,
2272 RpcCallback<AccessControlProtos.CheckPermissionsResponse> done) {
2273 Permission[] permissions = new Permission[request.getPermissionCount()];
2274 for (int i=0; i < request.getPermissionCount(); i++) {
2275 permissions[i] = ProtobufUtil.toPermission(request.getPermission(i));
2276 }
2277 AccessControlProtos.CheckPermissionsResponse response = null;
2278 try {
2279 User user = getActiveUser();
2280 TableName tableName = regionEnv.getRegion().getTableDesc().getTableName();
2281 for (Permission permission : permissions) {
2282 if (permission instanceof TablePermission) {
2283
2284
2285 TablePermission tperm = (TablePermission) permission;
2286 for (Action action : permission.getActions()) {
2287 if (!tperm.getTableName().equals(tableName)) {
2288 throw new CoprocessorException(AccessController.class, String.format("This method "
2289 + "can only execute at the table specified in TablePermission. " +
2290 "Table of the region:%s , requested table:%s", tableName,
2291 tperm.getTableName()));
2292 }
2293
2294 Map<byte[], Set<byte[]>> familyMap =
2295 new TreeMap<byte[], Set<byte[]>>(Bytes.BYTES_COMPARATOR);
2296 if (tperm.getFamily() != null) {
2297 if (tperm.getQualifier() != null) {
2298 Set<byte[]> qualifiers = Sets.newTreeSet(Bytes.BYTES_COMPARATOR);
2299 qualifiers.add(tperm.getQualifier());
2300 familyMap.put(tperm.getFamily(), qualifiers);
2301 } else {
2302 familyMap.put(tperm.getFamily(), null);
2303 }
2304 }
2305
2306 AuthResult result = permissionGranted("checkPermissions", user, action, regionEnv,
2307 familyMap);
2308 logResult(result);
2309 if (!result.isAllowed()) {
2310
2311
2312 throw new AccessDeniedException("Insufficient permissions (table=" + tableName +
2313 (familyMap.size() > 0 ? ", family: " + result.toFamilyString() : "") +
2314 ", action=" + action.toString() + ")");
2315 }
2316 }
2317
2318 } else {
2319
2320
2321 for (Action action : permission.getActions()) {
2322 AuthResult result;
2323 if (authManager.authorize(user, action)) {
2324 result = AuthResult.allow("checkPermissions", "Global action allowed", user,
2325 action, null, null);
2326 } else {
2327 result = AuthResult.deny("checkPermissions", "Global action denied", user, action,
2328 null, null);
2329 }
2330 logResult(result);
2331 if (!result.isAllowed()) {
2332
2333
2334 throw new AccessDeniedException("Insufficient permissions (action=" +
2335 action.toString() + ")");
2336 }
2337 }
2338 }
2339 }
2340 response = AccessControlProtos.CheckPermissionsResponse.getDefaultInstance();
2341 } catch (IOException ioe) {
2342 ResponseConverter.setControllerException(controller, ioe);
2343 }
2344 done.run(response);
2345 }
2346
2347 @Override
2348 public Service getService() {
2349 return AccessControlProtos.AccessControlService.newReflectiveService(this);
2350 }
2351
2352 private HRegion getRegion(RegionCoprocessorEnvironment e) {
2353 return e.getRegion();
2354 }
2355
2356 private TableName getTableName(RegionCoprocessorEnvironment e) {
2357 HRegion region = e.getRegion();
2358 if (region != null) {
2359 return getTableName(region);
2360 }
2361 return null;
2362 }
2363
2364 private TableName getTableName(HRegion region) {
2365 HRegionInfo regionInfo = region.getRegionInfo();
2366 if (regionInfo != null) {
2367 return regionInfo.getTable();
2368 }
2369 return null;
2370 }
2371
2372 @Override
2373 public void preClose(ObserverContext<RegionCoprocessorEnvironment> e, boolean abortRequested)
2374 throws IOException {
2375 requirePermission("preClose", Action.ADMIN);
2376 }
2377
2378 private void checkSystemOrSuperUser() throws IOException {
2379
2380 if (!authorizationEnabled) {
2381 return;
2382 }
2383 User activeUser = getActiveUser();
2384 if (!Superusers.isSuperUser(activeUser)) {
2385 throw new AccessDeniedException("User '" + (activeUser != null ?
2386 activeUser.getShortName() : "null") + "is not system or super user.");
2387 }
2388 }
2389
2390 @Override
2391 public void preStopRegionServer(
2392 ObserverContext<RegionServerCoprocessorEnvironment> env)
2393 throws IOException {
2394 requirePermission("preStopRegionServer", Action.ADMIN);
2395 }
2396
2397 private Map<byte[], ? extends Collection<byte[]>> makeFamilyMap(byte[] family,
2398 byte[] qualifier) {
2399 if (family == null) {
2400 return null;
2401 }
2402
2403 Map<byte[], Collection<byte[]>> familyMap = new TreeMap<byte[], Collection<byte[]>>(Bytes.BYTES_COMPARATOR);
2404 familyMap.put(family, qualifier != null ? ImmutableSet.of(qualifier) : null);
2405 return familyMap;
2406 }
2407
2408 @Override
2409 public void preGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
2410 List<TableName> tableNamesList,
2411 List<HTableDescriptor> descriptors) throws IOException {
2412
2413
2414 if (tableNamesList == null || tableNamesList.isEmpty()) {
2415 requireGlobalPermission("getTableDescriptors", Action.ADMIN, null, null);
2416 }
2417
2418
2419 else {
2420 MasterServices masterServices = ctx.getEnvironment().getMasterServices();
2421 for (TableName tableName: tableNamesList) {
2422
2423 if (masterServices.getTableDescriptors().get(tableName) == null) {
2424 continue;
2425 }
2426 requirePermission("getTableDescriptors", tableName, null, null,
2427 Action.ADMIN, Action.CREATE);
2428 }
2429 }
2430 }
2431
2432 @Override
2433 public void preMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx, HRegion regionA,
2434 HRegion regionB) throws IOException {
2435 requirePermission("mergeRegions", regionA.getTableDesc().getTableName(), null, null,
2436 Action.ADMIN);
2437 }
2438
2439 @Override
2440 public void postMerge(ObserverContext<RegionServerCoprocessorEnvironment> c, HRegion regionA,
2441 HRegion regionB, HRegion mergedRegion) throws IOException { }
2442
2443 @Override
2444 public void preMergeCommit(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
2445 HRegion regionA, HRegion regionB, List<Mutation> metaEntries) throws IOException { }
2446
2447 @Override
2448 public void postMergeCommit(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
2449 HRegion regionA, HRegion regionB, HRegion mergedRegion) throws IOException { }
2450
2451 @Override
2452 public void preRollBackMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
2453 HRegion regionA, HRegion regionB) throws IOException { }
2454
2455 @Override
2456 public void postRollBackMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
2457 HRegion regionA, HRegion regionB) throws IOException { }
2458
2459 @Override
2460 public void preRollWALWriterRequest(ObserverContext<RegionServerCoprocessorEnvironment> ctx)
2461 throws IOException {
2462 requirePermission("preRollLogWriterRequest", Permission.Action.ADMIN);
2463 }
2464
2465 @Override
2466 public void postRollWALWriterRequest(ObserverContext<RegionServerCoprocessorEnvironment> ctx)
2467 throws IOException { }
2468
2469 @Override
2470 public ReplicationEndpoint postCreateReplicationEndPoint(
2471 ObserverContext<RegionServerCoprocessorEnvironment> ctx, ReplicationEndpoint endpoint) {
2472 return endpoint;
2473 }
2474
2475 @Override
2476 public void preReplicateLogEntries(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
2477 List<WALEntry> entries, CellScanner cells) throws IOException {
2478 requirePermission("replicateLogEntries", Action.WRITE);
2479 }
2480
2481 @Override
2482 public void postReplicateLogEntries(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
2483 List<WALEntry> entries, CellScanner cells) throws IOException {
2484 }
2485 }