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