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.List;
22 import java.util.LinkedList;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.TreeSet;
26
27 import com.google.protobuf.RpcCallback;
28 import com.google.protobuf.RpcController;
29 import com.google.protobuf.Service;
30
31 import org.apache.commons.lang.ArrayUtils;
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.apache.hadoop.conf.Configuration;
35 import org.apache.hadoop.hbase.Cell;
36 import org.apache.hadoop.hbase.CoprocessorEnvironment;
37 import org.apache.hadoop.hbase.TableName;
38 import org.apache.hadoop.hbase.HColumnDescriptor;
39 import org.apache.hadoop.hbase.HRegionInfo;
40 import org.apache.hadoop.hbase.HTableDescriptor;
41 import org.apache.hadoop.hbase.KeyValue;
42 import org.apache.hadoop.hbase.KeyValueUtil;
43 import org.apache.hadoop.hbase.NamespaceDescriptor;
44 import org.apache.hadoop.hbase.ServerName;
45 import org.apache.hadoop.hbase.TableNotDisabledException;
46 import org.apache.hadoop.hbase.TableNotFoundException;
47 import org.apache.hadoop.hbase.client.Append;
48 import org.apache.hadoop.hbase.client.Delete;
49 import org.apache.hadoop.hbase.client.Get;
50 import org.apache.hadoop.hbase.client.Increment;
51 import org.apache.hadoop.hbase.client.Put;
52 import org.apache.hadoop.hbase.client.Result;
53 import org.apache.hadoop.hbase.client.Scan;
54 import org.apache.hadoop.hbase.client.Durability;
55 import org.apache.hadoop.hbase.coprocessor.*;
56 import org.apache.hadoop.hbase.filter.CompareFilter;
57 import org.apache.hadoop.hbase.filter.FilterList;
58 import org.apache.hadoop.hbase.filter.ByteArrayComparable;
59 import org.apache.hadoop.hbase.ipc.RequestContext;
60 import org.apache.hadoop.hbase.master.MasterServices;
61 import org.apache.hadoop.hbase.master.RegionPlan;
62 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
63 import org.apache.hadoop.hbase.protobuf.ResponseConverter;
64 import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos;
65 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
66 import org.apache.hadoop.hbase.regionserver.HRegion;
67 import org.apache.hadoop.hbase.regionserver.InternalScanner;
68 import org.apache.hadoop.hbase.regionserver.RegionScanner;
69 import org.apache.hadoop.hbase.regionserver.Store;
70 import org.apache.hadoop.hbase.regionserver.ScanType;
71 import org.apache.hadoop.hbase.regionserver.StoreFile;
72 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
73 import org.apache.hadoop.hbase.security.AccessDeniedException;
74 import org.apache.hadoop.hbase.security.User;
75 import org.apache.hadoop.hbase.security.UserProvider;
76 import org.apache.hadoop.hbase.security.access.Permission.Action;
77 import org.apache.hadoop.hbase.util.Bytes;
78 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
79 import org.apache.hadoop.hbase.util.Pair;
80
81 import com.google.common.collect.ImmutableSet;
82 import com.google.common.collect.ListMultimap;
83 import com.google.common.collect.Lists;
84 import com.google.common.collect.MapMaker;
85 import com.google.common.collect.Maps;
86 import com.google.common.collect.Sets;
87
88 import static org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 public class AccessController extends BaseRegionObserver
122 implements MasterObserver, RegionServerObserver,
123 AccessControlService.Interface, CoprocessorService {
124
125 public static final Log LOG = LogFactory.getLog(AccessController.class);
126
127 private static final Log AUDITLOG =
128 LogFactory.getLog("SecurityLogger."+AccessController.class.getName());
129
130 TableAuthManager authManager = null;
131
132
133 boolean aclRegion = false;
134
135
136
137 private RegionCoprocessorEnvironment regionEnv;
138
139
140 private Map<InternalScanner,String> scannerOwners =
141 new MapMaker().weakKeys().makeMap();
142
143 private UserProvider userProvider;
144
145 private volatile boolean initialized = false;
146
147 void initialize(RegionCoprocessorEnvironment e) throws IOException {
148 final HRegion region = e.getRegion();
149 Map<byte[], ListMultimap<String,TablePermission>> tables =
150 AccessControlLists.loadAll(region);
151
152
153 for (Map.Entry<byte[], ListMultimap<String,TablePermission>> t:
154 tables.entrySet()) {
155 byte[] entry = t.getKey();
156 ListMultimap<String,TablePermission> perms = t.getValue();
157 byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, e.getConfiguration());
158 this.authManager.getZKPermissionWatcher().writeToZookeeper(entry, serialized);
159 }
160 initialized = true;
161 }
162
163
164
165
166
167
168 void updateACL(RegionCoprocessorEnvironment e,
169 final Map<byte[], List<Cell>> familyMap) {
170 Set<byte[]> entries =
171 new TreeSet<byte[]>(Bytes.BYTES_RAWCOMPARATOR);
172 for (Map.Entry<byte[], List<Cell>> f : familyMap.entrySet()) {
173 List<Cell> cells = f.getValue();
174 for (Cell cell: cells) {
175 KeyValue kv = KeyValueUtil.ensureKeyValue(cell);
176 if (Bytes.equals(kv.getBuffer(), kv.getFamilyOffset(),
177 kv.getFamilyLength(), AccessControlLists.ACL_LIST_FAMILY, 0,
178 AccessControlLists.ACL_LIST_FAMILY.length)) {
179 entries.add(kv.getRow());
180 }
181 }
182 }
183 ZKPermissionWatcher zkw = this.authManager.getZKPermissionWatcher();
184 Configuration conf = regionEnv.getConfiguration();
185 for (byte[] entry: entries) {
186 try {
187 ListMultimap<String,TablePermission> perms =
188 AccessControlLists.getPermissions(conf, entry);
189 byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, conf);
190 zkw.writeToZookeeper(entry, serialized);
191 } catch (IOException ex) {
192 LOG.error("Failed updating permissions mirror for '" + Bytes.toString(entry) + "'",
193 ex);
194 }
195 }
196 }
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212 AuthResult permissionGranted(String request, User user, Permission.Action permRequest,
213 RegionCoprocessorEnvironment e,
214 Map<byte [], ? extends Collection<?>> families) {
215 HRegionInfo hri = e.getRegion().getRegionInfo();
216 TableName tableName = hri.getTable();
217
218
219
220 if (hri.isMetaRegion()) {
221 if (permRequest == Permission.Action.READ) {
222 return AuthResult.allow(request, "All users allowed", user,
223 permRequest, tableName, families);
224 }
225 }
226
227 if (user == null) {
228 return AuthResult.deny(request, "No user associated with request!", null,
229 permRequest, tableName, families);
230 }
231
232
233
234
235
236
237 if (permRequest == Permission.Action.WRITE &&
238 (hri.isMetaRegion() ||
239 Bytes.equals(tableName.getName(), AccessControlLists.ACL_GLOBAL_NAME)) &&
240 (authManager.authorize(user, Permission.Action.CREATE) ||
241 authManager.authorize(user, Permission.Action.ADMIN)))
242 {
243 return AuthResult.allow(request, "Table permission granted", user,
244 permRequest, tableName, families);
245 }
246
247
248 if (authManager.authorize(user, tableName, (byte[])null, permRequest)) {
249 return AuthResult.allow(request, "Table permission granted", user,
250 permRequest, tableName, families);
251 }
252
253
254 if (families != null && families.size() > 0) {
255
256 for (Map.Entry<byte [], ? extends Collection<?>> family : families.entrySet()) {
257
258 if (authManager.authorize(user, tableName, family.getKey(),
259 permRequest)) {
260 continue;
261 }
262
263
264 if ((family.getValue() != null) && (family.getValue().size() > 0)) {
265 if (family.getValue() instanceof Set) {
266
267 Set<byte[]> familySet = (Set<byte[]>)family.getValue();
268 for (byte[] qualifier : familySet) {
269 if (!authManager.authorize(user, tableName, family.getKey(),
270 qualifier, permRequest)) {
271 return AuthResult.deny(request, "Failed qualifier check", user,
272 permRequest, tableName, makeFamilyMap(family.getKey(), qualifier));
273 }
274 }
275 } else if (family.getValue() instanceof List) {
276 List<KeyValue> kvList = (List<KeyValue>)family.getValue();
277 for (KeyValue kv : kvList) {
278 if (!authManager.authorize(user, tableName, family.getKey(),
279 kv.getQualifier(), permRequest)) {
280 return AuthResult.deny(request, "Failed qualifier check", user,
281 permRequest, tableName, makeFamilyMap(family.getKey(), kv.getQualifier()));
282 }
283 }
284 }
285 } else {
286
287 return AuthResult.deny(request, "Failed family check", user, permRequest,
288 tableName, makeFamilyMap(family.getKey(), null));
289 }
290 }
291
292
293 return AuthResult.allow(request, "All family checks passed", user, permRequest,
294 tableName, families);
295 }
296
297
298 return AuthResult.deny(request, "No families to check and table permission failed",
299 user, permRequest, tableName, families);
300 }
301
302 private void logResult(AuthResult result) {
303 if (AUDITLOG.isTraceEnabled()) {
304 RequestContext ctx = RequestContext.get();
305 InetAddress remoteAddr = null;
306 if (ctx != null) {
307 remoteAddr = ctx.getRemoteAddress();
308 }
309 AUDITLOG.trace("Access " + (result.isAllowed() ? "allowed" : "denied") +
310 " for user " + (result.getUser() != null ? result.getUser().getShortName() : "UNKNOWN") +
311 "; reason: " + result.getReason() +
312 "; remote address: " + (remoteAddr != null ? remoteAddr : "") +
313 "; request: " + result.getRequest() +
314 "; context: " + result.toContextString());
315 }
316 }
317
318
319
320
321
322
323 private User getActiveUser() throws IOException {
324 User user = RequestContext.getRequestUser();
325 if (!RequestContext.isInRequestContext()) {
326
327 user = userProvider.getCurrent();
328 }
329 return user;
330 }
331
332
333
334
335
336
337
338
339
340
341 private void requirePermission(String request, TableName tableName, byte[] family, byte[] qualifier,
342 Action... permissions) throws IOException {
343 User user = getActiveUser();
344 AuthResult result = null;
345
346 for (Action permission : permissions) {
347 if (authManager.authorize(user, tableName, family, qualifier, permission)) {
348 result = AuthResult.allow(request, "Table permission granted", user,
349 permission, tableName, family, qualifier);
350 break;
351 } else {
352
353 result = AuthResult.deny(request, "Insufficient permissions", user,
354 permission, tableName, family, qualifier);
355 }
356 }
357 logResult(result);
358 if (!result.isAllowed()) {
359 throw new AccessDeniedException("Insufficient permissions " + result.toContextString());
360 }
361 }
362
363
364
365
366
367
368
369
370 private void requirePermission(String request, String namespace,
371 Action... permissions) throws IOException {
372 User user = getActiveUser();
373 AuthResult result = null;
374
375 for (Action permission : permissions) {
376 if (authManager.authorize(user, namespace, permission)) {
377 result = AuthResult.allow(request, "Table permission granted", user,
378 permission, namespace);
379 break;
380 } else {
381
382 result = AuthResult.deny(request, "Insufficient permissions", user,
383 permission, namespace);
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
469
470
471
472 private boolean hasFamilyQualifierPermission(User user,
473 Permission.Action perm,
474 RegionCoprocessorEnvironment env,
475 Map<byte[], ? extends Set<byte[]>> familyMap)
476 throws IOException {
477 HRegionInfo hri = env.getRegion().getRegionInfo();
478 TableName tableName = hri.getTable();
479
480 if (user == null) {
481 return false;
482 }
483
484 if (familyMap != null && familyMap.size() > 0) {
485
486 for (Map.Entry<byte[], ? extends Set<byte[]>> family :
487 familyMap.entrySet()) {
488 if (family.getValue() != null && !family.getValue().isEmpty()) {
489 for (byte[] qualifier : family.getValue()) {
490 if (authManager.matchPermission(user, tableName,
491 family.getKey(), qualifier, perm)) {
492 return true;
493 }
494 }
495 } else {
496 if (authManager.matchPermission(user, tableName, family.getKey(),
497 perm)) {
498 return true;
499 }
500 }
501 }
502 } else if (LOG.isDebugEnabled()) {
503 LOG.debug("Empty family map passed for permission check");
504 }
505
506 return false;
507 }
508
509
510 public void start(CoprocessorEnvironment env) throws IOException {
511
512 ZooKeeperWatcher zk = null;
513 if (env instanceof MasterCoprocessorEnvironment) {
514
515 MasterCoprocessorEnvironment mEnv = (MasterCoprocessorEnvironment) env;
516 zk = mEnv.getMasterServices().getZooKeeper();
517 } else if (env instanceof RegionServerCoprocessorEnvironment) {
518 RegionServerCoprocessorEnvironment rsEnv = (RegionServerCoprocessorEnvironment) env;
519 zk = rsEnv.getRegionServerServices().getZooKeeper();
520 } else if (env instanceof RegionCoprocessorEnvironment) {
521
522 regionEnv = (RegionCoprocessorEnvironment) env;
523 zk = regionEnv.getRegionServerServices().getZooKeeper();
524 }
525
526
527 this.userProvider = UserProvider.instantiate(env.getConfiguration());
528
529
530
531 if (zk != null) {
532 try {
533 this.authManager = TableAuthManager.get(zk, env.getConfiguration());
534 } catch (IOException ioe) {
535 throw new RuntimeException("Error obtaining TableAuthManager", ioe);
536 }
537 } else {
538 throw new RuntimeException("Error obtaining TableAuthManager, zk found null.");
539 }
540 }
541
542 public void stop(CoprocessorEnvironment env) {
543
544 }
545
546 @Override
547 public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
548 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
549 Set<byte[]> families = desc.getFamiliesKeys();
550 Map<byte[], Set<byte[]>> familyMap = Maps.newTreeMap(Bytes.BYTES_COMPARATOR);
551 for (byte[] family: families) {
552 familyMap.put(family, null);
553 }
554 requireGlobalPermission("createTable", Permission.Action.CREATE, desc.getTableName(), familyMap);
555 }
556
557 @Override
558 public void preCreateTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
559 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {}
560
561 @Override
562 public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
563 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
564 if (!AccessControlLists.isAclTable(desc)) {
565 String owner = desc.getOwnerString();
566
567 if (owner == null) owner = getActiveUser().getShortName();
568 UserPermission userperm = new UserPermission(Bytes.toBytes(owner), desc.getTableName(), null,
569 Action.values());
570 AccessControlLists.addUserPermission(c.getEnvironment().getConfiguration(), userperm);
571 }
572 }
573
574 @Override
575 public void postCreateTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
576 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {}
577
578 @Override
579 public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
580 throws IOException {
581 requirePermission("deleteTable", tableName, null, null, Action.ADMIN, Action.CREATE);
582 }
583
584 @Override
585 public void preDeleteTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
586 TableName tableName) throws IOException {}
587 @Override
588 public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c,
589 TableName tableName) throws IOException {
590 AccessControlLists.removeTablePermissions(c.getEnvironment().getConfiguration(), tableName);
591 }
592 @Override
593 public void postDeleteTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
594 TableName tableName) throws IOException {}
595
596 @Override
597 public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
598 HTableDescriptor htd) throws IOException {
599 requirePermission("modifyTable", tableName, null, null, Action.ADMIN, Action.CREATE);
600 }
601
602 @Override
603 public void preModifyTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
604 TableName tableName, HTableDescriptor htd) throws IOException {}
605
606 @Override
607 public void postModifyTable(ObserverContext<MasterCoprocessorEnvironment> c,
608 TableName tableName, HTableDescriptor htd) throws IOException {
609 String owner = htd.getOwnerString();
610
611 if (owner == null) owner = getActiveUser().getShortName();
612 UserPermission userperm = new UserPermission(Bytes.toBytes(owner), htd.getTableName(), null,
613 Action.values());
614 AccessControlLists.addUserPermission(c.getEnvironment().getConfiguration(), userperm);
615 }
616
617 @Override
618 public void postModifyTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
619 TableName tableName, HTableDescriptor htd) throws IOException {}
620
621
622 @Override
623 public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
624 HColumnDescriptor column) throws IOException {
625 requirePermission("addColumn", tableName, null, null, Action.ADMIN, Action.CREATE);
626 }
627
628 @Override
629 public void preAddColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
630 TableName tableName, HColumnDescriptor column) throws IOException {}
631 @Override
632 public void postAddColumn(ObserverContext<MasterCoprocessorEnvironment> c,
633 TableName tableName, HColumnDescriptor column) throws IOException {}
634 @Override
635 public void postAddColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
636 TableName tableName, HColumnDescriptor column) throws IOException {}
637
638 @Override
639 public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
640 HColumnDescriptor descriptor) throws IOException {
641 requirePermission("modifyColumn", tableName, null, null, Action.ADMIN, Action.CREATE);
642 }
643
644 @Override
645 public void preModifyColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
646 TableName tableName, HColumnDescriptor descriptor) throws IOException {}
647 @Override
648 public void postModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c,
649 TableName tableName, HColumnDescriptor descriptor) throws IOException {}
650 @Override
651 public void postModifyColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
652 TableName tableName, HColumnDescriptor descriptor) throws IOException {}
653
654
655 @Override
656 public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName,
657 byte[] col) throws IOException {
658 requirePermission("deleteColumn", tableName, null, null, Action.ADMIN, Action.CREATE);
659 }
660
661 @Override
662 public void preDeleteColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
663 TableName tableName, byte[] col) throws IOException {}
664 @Override
665 public void postDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c,
666 TableName tableName, byte[] col) throws IOException {
667 AccessControlLists.removeTablePermissions(c.getEnvironment().getConfiguration(),
668 tableName, col);
669 }
670 @Override
671 public void postDeleteColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
672 TableName tableName, byte[] col) throws IOException {}
673
674 @Override
675 public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
676 throws IOException {
677 requirePermission("enableTable", tableName, null, null, Action.ADMIN, Action.CREATE);
678 }
679
680 @Override
681 public void preEnableTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
682 TableName tableName) throws IOException {}
683 @Override
684 public void postEnableTable(ObserverContext<MasterCoprocessorEnvironment> c,
685 TableName tableName) throws IOException {}
686 @Override
687 public void postEnableTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
688 TableName tableName) throws IOException {}
689
690 @Override
691 public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
692 throws IOException {
693 if (Bytes.equals(tableName.getName(), AccessControlLists.ACL_GLOBAL_NAME)) {
694 throw new AccessDeniedException("Not allowed to disable "
695 + AccessControlLists.ACL_TABLE_NAME + " table.");
696 }
697 requirePermission("disableTable", tableName, null, null, Action.ADMIN, Action.CREATE);
698 }
699
700 @Override
701 public void preDisableTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
702 TableName tableName) throws IOException {}
703 @Override
704 public void postDisableTable(ObserverContext<MasterCoprocessorEnvironment> c,
705 TableName tableName) throws IOException {}
706 @Override
707 public void postDisableTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
708 TableName tableName) throws IOException {}
709
710 @Override
711 public void preMove(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo region,
712 ServerName srcServer, ServerName destServer) throws IOException {
713 requirePermission("move", region.getTable(), null, null, Action.ADMIN);
714 }
715
716 @Override
717 public void postMove(ObserverContext<MasterCoprocessorEnvironment> c,
718 HRegionInfo region, ServerName srcServer, ServerName destServer)
719 throws IOException {}
720
721 @Override
722 public void preAssign(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo regionInfo)
723 throws IOException {
724 requirePermission("assign", regionInfo.getTable(), null, null, Action.ADMIN);
725 }
726
727 @Override
728 public void postAssign(ObserverContext<MasterCoprocessorEnvironment> c,
729 HRegionInfo regionInfo) throws IOException {}
730
731 @Override
732 public void preUnassign(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo regionInfo,
733 boolean force) throws IOException {
734 requirePermission("unassign", regionInfo.getTable(), null, null, Action.ADMIN);
735 }
736
737 @Override
738 public void postUnassign(ObserverContext<MasterCoprocessorEnvironment> c,
739 HRegionInfo regionInfo, boolean force) throws IOException {}
740
741 @Override
742 public void preRegionOffline(ObserverContext<MasterCoprocessorEnvironment> c,
743 HRegionInfo regionInfo) throws IOException {
744 requirePermission("regionOffline", regionInfo.getTable(), null, null, Action.ADMIN);
745 }
746
747 @Override
748 public void postRegionOffline(ObserverContext<MasterCoprocessorEnvironment> c,
749 HRegionInfo regionInfo) throws IOException {
750 }
751
752 @Override
753 public void preBalance(ObserverContext<MasterCoprocessorEnvironment> c)
754 throws IOException {
755 requirePermission("balance", Permission.Action.ADMIN);
756 }
757 @Override
758 public void postBalance(ObserverContext<MasterCoprocessorEnvironment> c, List<RegionPlan> plans)
759 throws IOException {}
760
761 @Override
762 public boolean preBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> c,
763 boolean newValue) throws IOException {
764 requirePermission("balanceSwitch", Permission.Action.ADMIN);
765 return newValue;
766 }
767 @Override
768 public void postBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> c,
769 boolean oldValue, boolean newValue) throws IOException {}
770
771 @Override
772 public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> c)
773 throws IOException {
774 requirePermission("shutdown", Permission.Action.ADMIN);
775 }
776
777 @Override
778 public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> c)
779 throws IOException {
780 requirePermission("stopMaster", Permission.Action.ADMIN);
781 }
782
783 @Override
784 public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx)
785 throws IOException {
786
787 AccessControlLists.init(ctx.getEnvironment().getMasterServices());
788 }
789
790 @Override
791 public void preMasterInitialization(
792 ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
793 }
794
795 @Override
796 public void preSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
797 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
798 throws IOException {
799 requirePermission("snapshot", Permission.Action.ADMIN);
800 }
801
802 @Override
803 public void postSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
804 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
805 throws IOException {
806 }
807
808 @Override
809 public void preCloneSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
810 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
811 throws IOException {
812 requirePermission("clone", Permission.Action.ADMIN);
813 }
814
815 @Override
816 public void postCloneSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
817 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
818 throws IOException {
819 }
820
821 @Override
822 public void preRestoreSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
823 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
824 throws IOException {
825 requirePermission("restore", Permission.Action.ADMIN);
826 }
827
828 @Override
829 public void postRestoreSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
830 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
831 throws IOException {
832 }
833
834 @Override
835 public void preDeleteSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
836 final SnapshotDescription snapshot) throws IOException {
837 requirePermission("deleteSnapshot", Permission.Action.ADMIN);
838 }
839
840 @Override
841 public void postDeleteSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
842 final SnapshotDescription snapshot) throws IOException {
843 }
844
845 @Override
846 public void preCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
847 NamespaceDescriptor ns) throws IOException {
848 requireGlobalPermission("createNamespace", Action.ADMIN, ns.getName());
849 }
850
851 @Override
852 public void postCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
853 NamespaceDescriptor ns) throws IOException {
854 }
855
856 @Override
857 public void preDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, String namespace)
858 throws IOException {
859 requireGlobalPermission("deleteNamespace", Action.ADMIN, namespace);
860 }
861
862 @Override
863 public void postDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
864 String namespace) throws IOException {
865 AccessControlLists.removeNamespacePermissions(ctx.getEnvironment().getConfiguration(),
866 namespace);
867 LOG.info(namespace + "entry deleted in "+AccessControlLists.ACL_TABLE_NAME+" table.");
868 }
869
870 @Override
871 public void preModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
872 NamespaceDescriptor ns) throws IOException {
873 requireGlobalPermission("modifyNamespace", Action.ADMIN, ns.getName());
874 }
875
876 @Override
877 public void postModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
878 NamespaceDescriptor ns) throws IOException {
879 }
880
881
882
883 @Override
884 public void preOpen(ObserverContext<RegionCoprocessorEnvironment> e)
885 throws IOException {
886 RegionCoprocessorEnvironment env = e.getEnvironment();
887 final HRegion region = env.getRegion();
888 if (region == null) {
889 LOG.error("NULL region from RegionCoprocessorEnvironment in preOpen()");
890 } else {
891 HRegionInfo regionInfo = region.getRegionInfo();
892 if (isSpecialTable(regionInfo)) {
893 isSystemOrSuperUser(regionEnv.getConfiguration());
894 } else {
895 requirePermission("preOpen", Action.ADMIN);
896 }
897 }
898 }
899
900 @Override
901 public void postOpen(ObserverContext<RegionCoprocessorEnvironment> c) {
902 RegionCoprocessorEnvironment env = c.getEnvironment();
903 final HRegion region = env.getRegion();
904 if (region == null) {
905 LOG.error("NULL region from RegionCoprocessorEnvironment in postOpen()");
906 return;
907 }
908 if (AccessControlLists.isAclRegion(region)) {
909 aclRegion = true;
910
911 if (!region.isRecovering()) {
912 try {
913 initialize(env);
914 } catch (IOException ex) {
915
916
917 throw new RuntimeException("Failed to initialize permissions cache", ex);
918 }
919 }
920 } else {
921 initialized = true;
922 }
923 }
924
925 @Override
926 public void postLogReplay(ObserverContext<RegionCoprocessorEnvironment> c) {
927 if (aclRegion) {
928 try {
929 initialize(c.getEnvironment());
930 } catch (IOException ex) {
931
932
933 throw new RuntimeException("Failed to initialize permissions cache", ex);
934 }
935 }
936 }
937
938 @Override
939 public void preFlush(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
940 requirePermission("flush", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
941 }
942
943 @Override
944 public void preSplit(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
945 requirePermission("split", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
946 }
947
948 @Override
949 public void preSplit(ObserverContext<RegionCoprocessorEnvironment> e,
950 byte[] splitRow) throws IOException {
951 requirePermission("split", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
952 }
953
954 @Override
955 public InternalScanner preCompact(ObserverContext<RegionCoprocessorEnvironment> e,
956 final Store store, final InternalScanner scanner, final ScanType scanType)
957 throws IOException {
958 requirePermission("compact", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
959 return scanner;
960 }
961
962 @Override
963 public void preCompactSelection(final ObserverContext<RegionCoprocessorEnvironment> e,
964 final Store store, final List<StoreFile> candidates) throws IOException {
965 requirePermission("compact", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
966 }
967
968 @Override
969 public void preGetClosestRowBefore(final ObserverContext<RegionCoprocessorEnvironment> c,
970 final byte [] row, final byte [] family, final Result result)
971 throws IOException {
972 assert family != null;
973
974 requirePermission("getClosestRowBefore", Permission.Action.READ, c.getEnvironment(),
975 makeFamilyMap(family, null));
976 }
977
978 @Override
979 public void preGetOp(final ObserverContext<RegionCoprocessorEnvironment> c,
980 final Get get, final List<Cell> result) throws IOException {
981
982
983
984
985 RegionCoprocessorEnvironment e = c.getEnvironment();
986 User requestUser = getActiveUser();
987 AuthResult authResult = permissionGranted("get", requestUser,
988 Permission.Action.READ, e, get.getFamilyMap());
989 if (!authResult.isAllowed()) {
990 if (hasFamilyQualifierPermission(requestUser,
991 Permission.Action.READ, e, get.getFamilyMap())) {
992 TableName table = getTableName(e);
993 AccessControlFilter filter = new AccessControlFilter(authManager,
994 requestUser, table);
995
996
997 if (get.getFilter() != null) {
998 FilterList wrapper = new FilterList(FilterList.Operator.MUST_PASS_ALL,
999 Lists.newArrayList(filter, get.getFilter()));
1000 get.setFilter(wrapper);
1001 } else {
1002 get.setFilter(filter);
1003 }
1004 logResult(AuthResult.allow("get", "Access allowed with filter", requestUser,
1005 Permission.Action.READ, authResult.getTableName(), get.getFamilyMap()));
1006 } else {
1007 logResult(authResult);
1008 throw new AccessDeniedException("Insufficient permissions (table=" +
1009 e.getRegion().getTableDesc().getTableName() + ", action=READ)");
1010 }
1011 } else {
1012
1013 logResult(authResult);
1014 }
1015 }
1016
1017 @Override
1018 public boolean preExists(final ObserverContext<RegionCoprocessorEnvironment> c,
1019 final Get get, final boolean exists) throws IOException {
1020 requirePermission("exists", Permission.Action.READ, c.getEnvironment(),
1021 get.getFamilyMap());
1022 return exists;
1023 }
1024
1025 @Override
1026 public void prePut(final ObserverContext<RegionCoprocessorEnvironment> c,
1027 final Put put, final WALEdit edit, final Durability durability)
1028 throws IOException {
1029 requirePermission("put", Permission.Action.WRITE, c.getEnvironment(),
1030 put.getFamilyCellMap());
1031 }
1032
1033 @Override
1034 public void postPut(final ObserverContext<RegionCoprocessorEnvironment> c,
1035 final Put put, final WALEdit edit, final Durability durability) {
1036 if (aclRegion) {
1037 updateACL(c.getEnvironment(), put.getFamilyCellMap());
1038 }
1039 }
1040
1041 @Override
1042 public void preDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
1043 final Delete delete, final WALEdit edit, final Durability durability)
1044 throws IOException {
1045 requirePermission("delete", Permission.Action.WRITE, c.getEnvironment(),
1046 delete.getFamilyCellMap());
1047 }
1048
1049 @Override
1050 public void postDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
1051 final Delete delete, final WALEdit edit, final Durability durability)
1052 throws IOException {
1053 if (aclRegion) {
1054 updateACL(c.getEnvironment(), delete.getFamilyCellMap());
1055 }
1056 }
1057
1058 @Override
1059 public boolean preCheckAndPut(final ObserverContext<RegionCoprocessorEnvironment> c,
1060 final byte [] row, final byte [] family, final byte [] qualifier,
1061 final CompareFilter.CompareOp compareOp,
1062 final ByteArrayComparable comparator, final Put put,
1063 final boolean result) throws IOException {
1064 Map<byte[], ? extends Collection<byte[]>> familyMap = makeFamilyMap(family, qualifier);
1065 requirePermission("checkAndPut", Permission.Action.READ, c.getEnvironment(), familyMap);
1066 requirePermission("checkAndPut", Permission.Action.WRITE, c.getEnvironment(), familyMap);
1067 return result;
1068 }
1069
1070 @Override
1071 public boolean preCheckAndDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
1072 final byte [] row, final byte [] family, final byte [] qualifier,
1073 final CompareFilter.CompareOp compareOp,
1074 final ByteArrayComparable comparator, final Delete delete,
1075 final boolean result) throws IOException {
1076 Map<byte[], ? extends Collection<byte[]>> familyMap = makeFamilyMap(family, qualifier);
1077 requirePermission("checkAndDelete", Permission.Action.READ, c.getEnvironment(), familyMap);
1078 requirePermission("checkAndDelete", Permission.Action.WRITE, c.getEnvironment(), familyMap);
1079 return result;
1080 }
1081
1082 @Override
1083 public long preIncrementColumnValue(final ObserverContext<RegionCoprocessorEnvironment> c,
1084 final byte [] row, final byte [] family, final byte [] qualifier,
1085 final long amount, final boolean writeToWAL)
1086 throws IOException {
1087 Map<byte[], ? extends Collection<byte[]>> familyMap = makeFamilyMap(family, qualifier);
1088 requirePermission("incrementColumnValue", Permission.Action.WRITE, c.getEnvironment(), familyMap);
1089 return -1;
1090 }
1091
1092 @Override
1093 public Result preAppend(ObserverContext<RegionCoprocessorEnvironment> c, Append append)
1094 throws IOException {
1095 requirePermission("append", Permission.Action.WRITE, c.getEnvironment(), append.getFamilyCellMap());
1096 return null;
1097 }
1098
1099 @Override
1100 public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> c,
1101 final Increment increment)
1102 throws IOException {
1103
1104 Map<byte[], Set<byte[]>> familyMap = Maps.newTreeMap(Bytes.BYTES_COMPARATOR);
1105 for (Map.Entry<byte [], List<Cell>> entry: increment.getFamilyCellMap().entrySet()) {
1106 Set<byte[]> qualifiers = Sets.newTreeSet(Bytes.BYTES_COMPARATOR);
1107 for (Cell cell: entry.getValue()) {
1108 KeyValue kv = KeyValueUtil.ensureKeyValue(cell);
1109 qualifiers.add(kv.getQualifier());
1110 }
1111 familyMap.put(entry.getKey(), qualifiers);
1112 }
1113 requirePermission("increment", Permission.Action.WRITE, c.getEnvironment(), familyMap);
1114 return null;
1115 }
1116
1117 @Override
1118 public RegionScanner preScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
1119 final Scan scan, final RegionScanner s) throws IOException {
1120
1121
1122
1123
1124 RegionCoprocessorEnvironment e = c.getEnvironment();
1125 User user = getActiveUser();
1126 AuthResult authResult = permissionGranted("scannerOpen", user, Permission.Action.READ, e,
1127 scan.getFamilyMap());
1128 if (!authResult.isAllowed()) {
1129 if (hasFamilyQualifierPermission(user, Permission.Action.READ, e,
1130 scan.getFamilyMap())) {
1131 TableName table = getTableName(e);
1132 AccessControlFilter filter = new AccessControlFilter(authManager,
1133 user, table);
1134
1135
1136 if (scan.hasFilter()) {
1137 FilterList wrapper = new FilterList(FilterList.Operator.MUST_PASS_ALL,
1138 Lists.newArrayList(filter, scan.getFilter()));
1139 scan.setFilter(wrapper);
1140 } else {
1141 scan.setFilter(filter);
1142 }
1143 logResult(AuthResult.allow("scannerOpen", "Access allowed with filter", user,
1144 Permission.Action.READ, authResult.getTableName(), scan.getFamilyMap()));
1145 } else {
1146
1147 logResult(authResult);
1148 throw new AccessDeniedException("Insufficient permissions for user '"+
1149 (user != null ? user.getShortName() : "null")+"' "+
1150 "for scanner open on table " + getTableName(e));
1151 }
1152 } else {
1153
1154 logResult(authResult);
1155 }
1156 return s;
1157 }
1158
1159 @Override
1160 public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
1161 final Scan scan, final RegionScanner s) throws IOException {
1162 User user = getActiveUser();
1163 if (user != null && user.getShortName() != null) {
1164 scannerOwners.put(s, user.getShortName());
1165 }
1166 return s;
1167 }
1168
1169 @Override
1170 public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c,
1171 final InternalScanner s, final List<Result> result,
1172 final int limit, final boolean hasNext) throws IOException {
1173 requireScannerOwner(s);
1174 return hasNext;
1175 }
1176
1177 @Override
1178 public void preScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
1179 final InternalScanner s) throws IOException {
1180 requireScannerOwner(s);
1181 }
1182
1183 @Override
1184 public void postScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
1185 final InternalScanner s) throws IOException {
1186
1187 scannerOwners.remove(s);
1188 }
1189
1190
1191
1192
1193
1194
1195 private void requireScannerOwner(InternalScanner s)
1196 throws AccessDeniedException {
1197 if (RequestContext.isInRequestContext()) {
1198 String requestUserName = RequestContext.getRequestUserName();
1199 String owner = scannerOwners.get(s);
1200 if (owner != null && !owner.equals(requestUserName)) {
1201 throw new AccessDeniedException("User '"+ requestUserName +"' is not the scanner owner!");
1202 }
1203 }
1204 }
1205
1206
1207
1208
1209
1210
1211
1212 @Override
1213 public void preBulkLoadHFile(ObserverContext<RegionCoprocessorEnvironment> ctx,
1214 List<Pair<byte[], String>> familyPaths) throws IOException {
1215 List<byte[]> cfs = new LinkedList<byte[]>();
1216 for(Pair<byte[],String> el : familyPaths) {
1217 requirePermission("preBulkLoadHFile",
1218 ctx.getEnvironment().getRegion().getTableDesc().getTableName(),
1219 el.getFirst(),
1220 null,
1221 Permission.Action.WRITE);
1222 }
1223 }
1224
1225 private AuthResult hasSomeAccess(RegionCoprocessorEnvironment e, String method, Action action) throws IOException {
1226 User requestUser = getActiveUser();
1227 TableName tableName = e.getRegion().getTableDesc().getTableName();
1228 AuthResult authResult = permissionGranted(method, requestUser,
1229 action, e, Collections.EMPTY_MAP);
1230 if (!authResult.isAllowed()) {
1231 for(UserPermission userPerm:
1232 AccessControlLists.getUserTablePermissions(regionEnv.getConfiguration(), tableName)) {
1233 for(Permission.Action userAction: userPerm.getActions()) {
1234 if(userAction.equals(action)) {
1235 return AuthResult.allow(method, "Access allowed", requestUser,
1236 action, tableName, null, null);
1237 }
1238 }
1239 }
1240 }
1241 return authResult;
1242 }
1243
1244
1245
1246
1247
1248
1249
1250
1251 public void prePrepareBulkLoad(RegionCoprocessorEnvironment e) throws IOException {
1252 AuthResult authResult = hasSomeAccess(e, "prePrepareBulkLoad", Action.WRITE);
1253 logResult(authResult);
1254 if (!authResult.isAllowed()) {
1255 throw new AccessDeniedException("Insufficient permissions (table=" +
1256 e.getRegion().getTableDesc().getTableName() + ", action=WRITE)");
1257 }
1258 }
1259
1260
1261
1262
1263
1264
1265
1266
1267 public void preCleanupBulkLoad(RegionCoprocessorEnvironment e) throws IOException {
1268 AuthResult authResult = hasSomeAccess(e, "preCleanupBulkLoad", Action.WRITE);
1269 logResult(authResult);
1270 if (!authResult.isAllowed()) {
1271 throw new AccessDeniedException("Insufficient permissions (table=" +
1272 e.getRegion().getTableDesc().getTableName() + ", action=WRITE)");
1273 }
1274 }
1275
1276
1277 @Override
1278 public void grant(RpcController controller,
1279 AccessControlProtos.GrantRequest request,
1280 RpcCallback<AccessControlProtos.GrantResponse> done) {
1281 UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission());
1282 AccessControlProtos.GrantResponse response = null;
1283 try {
1284
1285 if (aclRegion) {
1286 if (!initialized) {
1287 throw new CoprocessorException("AccessController not yet initialized");
1288 }
1289 if (LOG.isDebugEnabled()) {
1290 LOG.debug("Received request to grant access permission " + perm.toString());
1291 }
1292
1293 switch(request.getUserPermission().getPermission().getType()) {
1294 case Global :
1295 case Table :
1296 requirePermission("grant", perm.getTableName(), perm.getFamily(),
1297 perm.getQualifier(), Action.ADMIN);
1298 break;
1299 case Namespace :
1300 requireGlobalPermission("grant", Action.ADMIN, perm.getNamespace());
1301 }
1302
1303 AccessControlLists.addUserPermission(regionEnv.getConfiguration(), perm);
1304 if (AUDITLOG.isTraceEnabled()) {
1305
1306 AUDITLOG.trace("Granted permission " + perm.toString());
1307 }
1308 } else {
1309 throw new CoprocessorException(AccessController.class, "This method "
1310 + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
1311 }
1312 response = AccessControlProtos.GrantResponse.getDefaultInstance();
1313 } catch (IOException ioe) {
1314
1315 ResponseConverter.setControllerException(controller, ioe);
1316 }
1317 done.run(response);
1318 }
1319
1320 @Override
1321 public void revoke(RpcController controller,
1322 AccessControlProtos.RevokeRequest request,
1323 RpcCallback<AccessControlProtos.RevokeResponse> done) {
1324 UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission());
1325 AccessControlProtos.RevokeResponse response = null;
1326 try {
1327
1328 if (aclRegion) {
1329 if (!initialized) {
1330 throw new CoprocessorException("AccessController not yet initialized");
1331 }
1332 if (LOG.isDebugEnabled()) {
1333 LOG.debug("Received request to revoke access permission " + perm.toString());
1334 }
1335
1336 switch(request.getUserPermission().getPermission().getType()) {
1337 case Global :
1338 case Table :
1339 requirePermission("revoke", perm.getTableName(), perm.getFamily(),
1340 perm.getQualifier(), Action.ADMIN);
1341 break;
1342 case Namespace :
1343 requireGlobalPermission("revoke", Action.ADMIN, perm.getNamespace());
1344 }
1345
1346 AccessControlLists.removeUserPermission(regionEnv.getConfiguration(), perm);
1347 if (AUDITLOG.isTraceEnabled()) {
1348
1349 AUDITLOG.trace("Revoked permission " + perm.toString());
1350 }
1351 } else {
1352 throw new CoprocessorException(AccessController.class, "This method "
1353 + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
1354 }
1355 response = AccessControlProtos.RevokeResponse.getDefaultInstance();
1356 } catch (IOException ioe) {
1357
1358 ResponseConverter.setControllerException(controller, ioe);
1359 }
1360 done.run(response);
1361 }
1362
1363 @Override
1364 public void getUserPermissions(RpcController controller,
1365 AccessControlProtos.GetUserPermissionsRequest request,
1366 RpcCallback<AccessControlProtos.GetUserPermissionsResponse> done) {
1367 AccessControlProtos.GetUserPermissionsResponse response = null;
1368 try {
1369
1370 if (aclRegion) {
1371 if (!initialized) {
1372 throw new CoprocessorException("AccessController not yet initialized");
1373 }
1374 List<UserPermission> perms = null;
1375 if(request.getType() == AccessControlProtos.Permission.Type.Table) {
1376 TableName table = null;
1377 if (request.hasTableName()) {
1378 table = ProtobufUtil.toTableName(request.getTableName());
1379 }
1380 requirePermission("userPermissions", table, null, null, Action.ADMIN);
1381
1382 perms = AccessControlLists.getUserTablePermissions(
1383 regionEnv.getConfiguration(), table);
1384 } else if (request.getType() == AccessControlProtos.Permission.Type.Namespace) {
1385 perms = AccessControlLists.getUserNamespacePermissions(
1386 regionEnv.getConfiguration(), request.getNamespaceName().toStringUtf8());
1387 } else {
1388 perms = AccessControlLists.getUserPermissions(
1389 regionEnv.getConfiguration(), null);
1390 }
1391 response = ResponseConverter.buildGetUserPermissionsResponse(perms);
1392 } else {
1393 throw new CoprocessorException(AccessController.class, "This method "
1394 + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
1395 }
1396 } catch (IOException ioe) {
1397
1398 ResponseConverter.setControllerException(controller, ioe);
1399 }
1400 done.run(response);
1401 }
1402
1403 @Override
1404 public void checkPermissions(RpcController controller,
1405 AccessControlProtos.CheckPermissionsRequest request,
1406 RpcCallback<AccessControlProtos.CheckPermissionsResponse> done) {
1407 Permission[] permissions = new Permission[request.getPermissionCount()];
1408 for (int i=0; i < request.getPermissionCount(); i++) {
1409 permissions[i] = ProtobufUtil.toPermission(request.getPermission(i));
1410 }
1411 AccessControlProtos.CheckPermissionsResponse response = null;
1412 try {
1413 TableName tableName = regionEnv.getRegion().getTableDesc().getTableName();
1414 for (Permission permission : permissions) {
1415 if (permission instanceof TablePermission) {
1416 TablePermission tperm = (TablePermission) permission;
1417 for (Permission.Action action : permission.getActions()) {
1418 if (!tperm.getTableName().equals(tableName)) {
1419 throw new CoprocessorException(AccessController.class, String.format("This method "
1420 + "can only execute at the table specified in TablePermission. " +
1421 "Table of the region:%s , requested table:%s", tableName,
1422 tperm.getTableName()));
1423 }
1424
1425 Map<byte[], Set<byte[]>> familyMap = Maps.newTreeMap(Bytes.BYTES_COMPARATOR);
1426 if (tperm.getFamily() != null) {
1427 if (tperm.getQualifier() != null) {
1428 Set<byte[]> qualifiers = Sets.newTreeSet(Bytes.BYTES_COMPARATOR);
1429 qualifiers.add(tperm.getQualifier());
1430 familyMap.put(tperm.getFamily(), qualifiers);
1431 } else {
1432 familyMap.put(tperm.getFamily(), null);
1433 }
1434 }
1435
1436 requirePermission("checkPermissions", action, regionEnv, familyMap);
1437 }
1438
1439 } else {
1440 for (Permission.Action action : permission.getActions()) {
1441 requirePermission("checkPermissions", action);
1442 }
1443 }
1444 }
1445 response = AccessControlProtos.CheckPermissionsResponse.getDefaultInstance();
1446 } catch (IOException ioe) {
1447 ResponseConverter.setControllerException(controller, ioe);
1448 }
1449 done.run(response);
1450 }
1451
1452 @Override
1453 public Service getService() {
1454 return AccessControlProtos.AccessControlService.newReflectiveService(this);
1455 }
1456
1457 private TableName getTableName(RegionCoprocessorEnvironment e) {
1458 HRegion region = e.getRegion();
1459 TableName tableName = null;
1460
1461 if (region != null) {
1462 HRegionInfo regionInfo = region.getRegionInfo();
1463 if (regionInfo != null) {
1464 tableName = regionInfo.getTable();
1465 }
1466 }
1467 return tableName;
1468 }
1469
1470
1471 @Override
1472 public void preClose(ObserverContext<RegionCoprocessorEnvironment> e, boolean abortRequested)
1473 throws IOException {
1474 requirePermission("preClose", Action.ADMIN);
1475 }
1476
1477 private void isSystemOrSuperUser(Configuration conf) throws IOException {
1478 User user = userProvider.getCurrent();
1479 if (user == null) {
1480 throw new IOException("Unable to obtain the current user, " +
1481 "authorization checks for internal operations will not work correctly!");
1482 }
1483
1484 String currentUser = user.getShortName();
1485 List<String> superusers = Lists.asList(currentUser, conf.getStrings(
1486 AccessControlLists.SUPERUSER_CONF_KEY, new String[0]));
1487
1488 User activeUser = getActiveUser();
1489 if (!(superusers.contains(activeUser.getShortName()))) {
1490 throw new AccessDeniedException("User '" + (user != null ? user.getShortName() : "null") +
1491 "is not system or super user.");
1492 }
1493 }
1494
1495 private boolean isSpecialTable(HRegionInfo regionInfo) {
1496 TableName tableName = regionInfo.getTable();
1497 return tableName.equals(AccessControlLists.ACL_TABLE_NAME)
1498 || tableName.equals(TableName.NAMESPACE_TABLE_NAME)
1499 || tableName.equals(TableName.META_TABLE_NAME);
1500 }
1501
1502 @Override
1503 public void preStopRegionServer(
1504 ObserverContext<RegionServerCoprocessorEnvironment> env)
1505 throws IOException {
1506 requirePermission("preStopRegionServer", Permission.Action.ADMIN);
1507 }
1508
1509 private Map<byte[], ? extends Collection<byte[]>> makeFamilyMap(byte[] family,
1510 byte[] qualifier) {
1511 if (family == null) {
1512 return null;
1513 }
1514
1515 Map<byte[], Collection<byte[]>> familyMap = Maps.newTreeMap(Bytes.BYTES_COMPARATOR);
1516 familyMap.put(family, qualifier != null ? ImmutableSet.of(qualifier) : null);
1517 return familyMap;
1518 }
1519
1520 @Override
1521 public void preGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
1522 List<TableName> tableNamesList,
1523 List<HTableDescriptor> descriptors) throws IOException {
1524
1525
1526 if (tableNamesList == null || tableNamesList.isEmpty()) {
1527 requireGlobalPermission("getTableDescriptors", Permission.Action.ADMIN, null, null);
1528 }
1529
1530
1531 else {
1532 MasterServices masterServices = ctx.getEnvironment().getMasterServices();
1533 for (TableName tableName: tableNamesList) {
1534
1535 try {
1536 masterServices.checkTableModifiable(tableName);
1537 } catch (TableNotFoundException ex) {
1538
1539 continue;
1540 } catch (TableNotDisabledException ex) {
1541
1542 }
1543 requirePermission("getTableDescriptors", tableName, null, null,
1544 Permission.Action.ADMIN, Permission.Action.CREATE);
1545 }
1546 }
1547 }
1548
1549 @Override
1550 public void postGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
1551 List<HTableDescriptor> descriptors) throws IOException {
1552 }
1553 }