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