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