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