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