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