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