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