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