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