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