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