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