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