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.Arrays;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.LinkedList;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.TreeSet;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.hbase.CoprocessorEnvironment;
33 import org.apache.hadoop.hbase.HColumnDescriptor;
34 import org.apache.hadoop.hbase.HRegionInfo;
35 import org.apache.hadoop.hbase.HTableDescriptor;
36 import org.apache.hadoop.hbase.KeyValue;
37 import org.apache.hadoop.hbase.ServerName;
38 import org.apache.hadoop.hbase.client.Append;
39 import org.apache.hadoop.hbase.client.Delete;
40 import org.apache.hadoop.hbase.client.Get;
41 import org.apache.hadoop.hbase.client.Increment;
42 import org.apache.hadoop.hbase.client.Put;
43 import org.apache.hadoop.hbase.client.Result;
44 import org.apache.hadoop.hbase.client.Scan;
45 import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
46 import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
47 import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
48 import org.apache.hadoop.hbase.coprocessor.MasterObserver;
49 import org.apache.hadoop.hbase.coprocessor.ObserverContext;
50 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
51 import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
52 import org.apache.hadoop.hbase.coprocessor.RegionServerObserver;
53 import org.apache.hadoop.hbase.filter.CompareFilter;
54 import org.apache.hadoop.hbase.filter.FilterList;
55 import org.apache.hadoop.hbase.filter.WritableByteArrayComparable;
56 import org.apache.hadoop.hbase.ipc.HBaseRPC;
57 import org.apache.hadoop.hbase.ipc.ProtocolSignature;
58 import org.apache.hadoop.hbase.ipc.RequestContext;
59 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
60 import org.apache.hadoop.hbase.regionserver.HRegion;
61 import org.apache.hadoop.hbase.regionserver.InternalScanner;
62 import org.apache.hadoop.hbase.regionserver.RegionScanner;
63 import org.apache.hadoop.hbase.regionserver.Store;
64 import org.apache.hadoop.hbase.regionserver.StoreFile;
65 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
66 import org.apache.hadoop.hbase.security.AccessDeniedException;
67 import org.apache.hadoop.hbase.security.User;
68 import org.apache.hadoop.hbase.security.access.Permission.Action;
69 import org.apache.hadoop.hbase.util.Bytes;
70 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
71 import org.apache.hadoop.hbase.util.Pair;
72
73 import com.google.common.collect.ListMultimap;
74 import com.google.common.collect.Lists;
75 import com.google.common.collect.MapMaker;
76 import com.google.common.collect.Maps;
77 import com.google.common.collect.Sets;
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 public class AccessController extends BaseRegionObserver
111 implements MasterObserver, RegionServerObserver, AccessControllerProtocol {
112
113
114
115
116 private static class AuthResult {
117 private final boolean allowed;
118 private final byte[] table;
119 private final byte[] family;
120 private final byte[] qualifier;
121 private final Permission.Action action;
122 private final String request;
123 private final String reason;
124 private final User user;
125
126 public AuthResult(boolean allowed, String request, String reason, User user,
127 Permission.Action action, byte[] table, byte[] family, byte[] qualifier) {
128 this.allowed = allowed;
129 this.request = request;
130 this.reason = reason;
131 this.user = user;
132 this.table = table;
133 this.family = family;
134 this.qualifier = qualifier;
135 this.action = action;
136 }
137
138 public boolean isAllowed() { return allowed; }
139
140 public User getUser() { return user; }
141
142 public String getReason() { return reason; }
143
144 public String getRequest() { return request; }
145
146 public String toContextString() {
147 return "(user=" + (user != null ? user.getName() : "UNKNOWN") + ", " +
148 "scope=" + (table == null ? "GLOBAL" : Bytes.toString(table)) + ", " +
149 "family=" + (family != null ? Bytes.toString(family) : "") + ", " +
150 "qualifer=" + (qualifier != null ? Bytes.toString(qualifier) : "") + ", " +
151 "action=" + (action != null ? action.toString() : "") + ")";
152 }
153
154 public String toString() {
155 return new StringBuilder("AuthResult")
156 .append(toContextString()).toString();
157 }
158
159 public static AuthResult allow(String request, String reason, User user, Permission.Action action,
160 byte[] table, byte[] family, byte[] qualifier) {
161 return new AuthResult(true, request, reason, user, action, table, family, qualifier);
162 }
163
164 public static AuthResult allow(String request, String reason, User user, Permission.Action action, byte[] table) {
165 return new AuthResult(true, request, reason, user, action, table, null, null);
166 }
167
168 public static AuthResult deny(String request, String reason, User user,
169 Permission.Action action, byte[] table) {
170 return new AuthResult(false, request, reason, user, action, table, null, null);
171 }
172
173 public static AuthResult deny(String request, String reason, User user,
174 Permission.Action action, byte[] table, byte[] family, byte[] qualifier) {
175 return new AuthResult(false, request, reason, user, action, table, family, qualifier);
176 }
177 }
178
179 public static final Log LOG = LogFactory.getLog(AccessController.class);
180
181 private static final Log AUDITLOG =
182 LogFactory.getLog("SecurityLogger."+AccessController.class.getName());
183
184
185
186
187 private static final long PROTOCOL_VERSION = 1L;
188
189 TableAuthManager authManager = null;
190
191
192 boolean aclRegion = false;
193
194
195
196 private RegionCoprocessorEnvironment regionEnv;
197
198
199 private Map<InternalScanner,String> scannerOwners =
200 new MapMaker().weakKeys().makeMap();
201
202 void initialize(RegionCoprocessorEnvironment e) throws IOException {
203 final HRegion region = e.getRegion();
204
205 Map<byte[],ListMultimap<String,TablePermission>> tables =
206 AccessControlLists.loadAll(region);
207
208
209 for (Map.Entry<byte[],ListMultimap<String,TablePermission>> t:
210 tables.entrySet()) {
211 byte[] table = t.getKey();
212 ListMultimap<String,TablePermission> perms = t.getValue();
213 byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms,
214 regionEnv.getConfiguration());
215 this.authManager.getZKPermissionWatcher().writeToZookeeper(table, serialized);
216 }
217 }
218
219
220
221
222
223
224 void updateACL(RegionCoprocessorEnvironment e,
225 final Map<byte[], List<KeyValue>> familyMap) {
226 Set<byte[]> tableSet = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
227 for (Map.Entry<byte[], List<KeyValue>> f : familyMap.entrySet()) {
228 List<KeyValue> kvs = f.getValue();
229 for (KeyValue kv: kvs) {
230 if (Bytes.equals(kv.getBuffer(), kv.getFamilyOffset(),
231 kv.getFamilyLength(), AccessControlLists.ACL_LIST_FAMILY, 0,
232 AccessControlLists.ACL_LIST_FAMILY.length)) {
233 tableSet.add(kv.getRow());
234 }
235 }
236 }
237
238 ZKPermissionWatcher zkw = this.authManager.getZKPermissionWatcher();
239 Configuration conf = regionEnv.getConfiguration();
240 for (byte[] tableName: tableSet) {
241 try {
242 ListMultimap<String,TablePermission> perms =
243 AccessControlLists.getTablePermissions(conf, tableName);
244 byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, conf);
245 zkw.writeToZookeeper(tableName, serialized);
246 } catch (IOException ex) {
247 LOG.error("Failed updating permissions mirror for '" + tableName + "'", ex);
248 }
249 }
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266 AuthResult permissionGranted(String request, User user, TablePermission.Action permRequest,
267 RegionCoprocessorEnvironment e,
268 Map<byte [], ? extends Collection<?>> families) {
269 HRegionInfo hri = e.getRegion().getRegionInfo();
270 byte[] tableName = hri.getTableName();
271
272
273
274 if (hri.isRootRegion() || hri.isMetaRegion()) {
275 if (permRequest == TablePermission.Action.READ) {
276 return AuthResult.allow(request, "All users allowed", user, permRequest, tableName);
277 }
278 }
279
280 if (user == null) {
281 return AuthResult.deny(request, "No user associated with request!", null, permRequest, tableName);
282 }
283
284
285
286
287
288
289 if (permRequest == TablePermission.Action.WRITE &&
290 (hri.isRootRegion() || hri.isMetaRegion() ||
291 Bytes.equals(tableName, AccessControlLists.ACL_GLOBAL_NAME)) &&
292 (authManager.authorize(user, Permission.Action.CREATE) ||
293 authManager.authorize(user, Permission.Action.ADMIN)))
294 {
295 return AuthResult.allow(request, "Table permission granted", user, permRequest, tableName);
296 }
297
298
299 if (authManager.authorize(user, tableName, (byte[])null, permRequest)) {
300 return AuthResult.allow(request, "Table permission granted", user, permRequest, tableName);
301 }
302
303
304 if (families != null && families.size() > 0) {
305
306 for (Map.Entry<byte [], ? extends Collection<?>> family : families.entrySet()) {
307
308 if (authManager.authorize(user, tableName, family.getKey(),
309 permRequest)) {
310 continue;
311 }
312
313
314 if ((family.getValue() != null) && (family.getValue().size() > 0)) {
315 if (family.getValue() instanceof Set) {
316
317 Set<byte[]> familySet = (Set<byte[]>)family.getValue();
318 for (byte[] qualifier : familySet) {
319 if (!authManager.authorize(user, tableName, family.getKey(),
320 qualifier, permRequest)) {
321 return AuthResult.deny(request, "Failed qualifier check", user,
322 permRequest, tableName, family.getKey(), qualifier);
323 }
324 }
325 } else if (family.getValue() instanceof List) {
326 List<KeyValue> kvList = (List<KeyValue>)family.getValue();
327 for (KeyValue kv : kvList) {
328 if (!authManager.authorize(user, tableName, family.getKey(),
329 kv.getQualifier(), permRequest)) {
330 return AuthResult.deny(request, "Failed qualifier check", user,
331 permRequest, tableName, family.getKey(), kv.getQualifier());
332 }
333 }
334 }
335 } else {
336
337 return AuthResult.deny(request, "Failed family check", user, permRequest,
338 tableName, family.getKey(), null);
339 }
340 }
341
342
343 return AuthResult.allow(request, "All family checks passed", user, permRequest,
344 tableName);
345 }
346
347
348 return AuthResult.deny(request, "No families to check and table permission failed",
349 user, permRequest, tableName);
350 }
351
352 private void logResult(AuthResult result) {
353 if (AUDITLOG.isTraceEnabled()) {
354 InetAddress remoteAddr = null;
355 RequestContext ctx = RequestContext.get();
356 if (ctx != null) {
357 remoteAddr = ctx.getRemoteAddress();
358 }
359 AUDITLOG.trace("Access " + (result.isAllowed() ? "allowed" : "denied") +
360 " for user " + (result.getUser() != null ? result.getUser().getShortName() : "UNKNOWN") +
361 "; reason: " + result.getReason() +
362 "; remote address: " + (remoteAddr != null ? remoteAddr : "") +
363 "; request: " + result.getRequest() +
364 "; context: " + result.toContextString());
365 }
366 }
367
368
369
370
371
372
373 private User getActiveUser() throws IOException {
374 User user = RequestContext.getRequestUser();
375 if (!RequestContext.isInRequestContext()) {
376
377 user = User.getCurrent();
378 }
379
380 return user;
381 }
382
383
384
385
386
387
388
389
390
391
392 private void requirePermission(String request, byte[] tableName, byte[] family, byte[] qualifier,
393 Action... permissions) throws IOException {
394 User user = getActiveUser();
395 AuthResult result = null;
396
397 for (Action permission : permissions) {
398 if (authManager.authorize(user, tableName, family, qualifier, permission)) {
399 result = AuthResult.allow(request, "Table permission granted", user,
400 permission, tableName, family, qualifier);
401 break;
402 } else {
403
404 result = AuthResult.deny(request, "Insufficient permissions", user,
405 permission, tableName, family, qualifier);
406 }
407 }
408 logResult(result);
409 if (!result.isAllowed()) {
410 throw new AccessDeniedException("Insufficient permissions " + result.toContextString());
411 }
412 }
413
414
415
416
417
418
419
420 private void requirePermission(String request, Permission.Action perm) throws IOException {
421 User user = getActiveUser();
422 if (authManager.authorize(user, perm)) {
423 logResult(AuthResult.allow(request, "Global check allowed", user, perm, null));
424 } else {
425 logResult(AuthResult.deny(request, "Global check failed", user, perm, null));
426 throw new AccessDeniedException("Insufficient permissions for user '" +
427 (user != null ? user.getShortName() : "null") +"' (global, action=" +
428 perm.toString() + ")");
429 }
430 }
431
432
433
434
435
436
437
438
439
440 private void requirePermission(String request, Permission.Action perm,
441 RegionCoprocessorEnvironment env, Collection<byte[]> families)
442 throws IOException {
443
444 HashMap<byte[], Set<byte[]>> familyMap = new HashMap<byte[], Set<byte[]>>();
445 for (byte[] family : families) {
446 familyMap.put(family, null);
447 }
448 requirePermission(request, perm, env, familyMap);
449 }
450
451
452
453
454
455
456
457
458
459 public void requirePermission(String request, Permission.Action perm,
460 RegionCoprocessorEnvironment env,
461 Map<byte[], ? extends Collection<?>> families)
462 throws IOException {
463 User user = getActiveUser();
464 AuthResult result = permissionGranted(request, user, perm, env, families);
465 logResult(result);
466
467 if (!result.isAllowed()) {
468 StringBuffer sb = new StringBuffer("");
469 if ((families != null && families.size() > 0)) {
470 for (byte[] familyName : families.keySet()) {
471 if (sb.length() != 0) {
472 sb.append(", ");
473 }
474 sb.append(Bytes.toString(familyName));
475 }
476 }
477 throw new AccessDeniedException("Insufficient permissions (table=" +
478 env.getRegion().getTableDesc().getNameAsString()+
479 ((families != null && families.size() > 0) ? ", family: " +
480 sb.toString() : "") + ", action=" +
481 perm.toString() + ")");
482 }
483 }
484
485
486
487
488
489 private boolean hasFamilyQualifierPermission(User user,
490 TablePermission.Action perm,
491 RegionCoprocessorEnvironment env,
492 Map<byte[], ? extends Set<byte[]>> familyMap)
493 throws IOException {
494 HRegionInfo hri = env.getRegion().getRegionInfo();
495 byte[] tableName = hri.getTableName();
496
497 if (user == null) {
498 return false;
499 }
500
501 if (familyMap != null && familyMap.size() > 0) {
502
503 for (Map.Entry<byte[], ? extends Set<byte[]>> family :
504 familyMap.entrySet()) {
505 if (family.getValue() != null && !family.getValue().isEmpty()) {
506 for (byte[] qualifier : family.getValue()) {
507 if (authManager.matchPermission(user, tableName,
508 family.getKey(), qualifier, perm)) {
509 return true;
510 }
511 }
512 } else {
513 if (authManager.matchPermission(user, tableName, family.getKey(),
514 perm)) {
515 return true;
516 }
517 }
518 }
519 } else if (LOG.isDebugEnabled()) {
520 LOG.debug("Empty family map passed for permission check");
521 }
522
523 return false;
524 }
525
526
527 public void start(CoprocessorEnvironment env) throws IOException {
528
529 ZooKeeperWatcher zk = null;
530 if (env instanceof MasterCoprocessorEnvironment) {
531
532 MasterCoprocessorEnvironment mEnv = (MasterCoprocessorEnvironment) env;
533 zk = mEnv.getMasterServices().getZooKeeper();
534 } else if (env instanceof RegionServerCoprocessorEnvironment) {
535 RegionServerCoprocessorEnvironment rsEnv = (RegionServerCoprocessorEnvironment) env;
536 zk = rsEnv.getRegionServerServices().getZooKeeper();
537 } else if (env instanceof RegionCoprocessorEnvironment) {
538
539 regionEnv = (RegionCoprocessorEnvironment) env;
540 zk = regionEnv.getRegionServerServices().getZooKeeper();
541 }
542
543
544
545 if (zk != null) {
546 try {
547 this.authManager = TableAuthManager.get(zk, env.getConfiguration());
548 } catch (IOException ioe) {
549 throw new RuntimeException("Error obtaining TableAuthManager", ioe);
550 }
551 } else {
552 throw new RuntimeException("Error obtaining TableAuthManager, zk found null.");
553 }
554 }
555
556 public void stop(CoprocessorEnvironment env) {
557
558 }
559
560 @Override
561 public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
562 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
563 requirePermission("createTable", Permission.Action.CREATE);
564 }
565
566 @Override
567 public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
568 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
569 if (!AccessControlLists.isAclTable(desc)) {
570 String owner = desc.getOwnerString();
571
572 if (owner == null) owner = getActiveUser().getShortName();
573 UserPermission userperm = new UserPermission(Bytes.toBytes(owner), desc.getName(), null,
574 Action.values());
575 AccessControlLists.addUserPermission(c.getEnvironment().getConfiguration(), userperm);
576 }
577 }
578
579 @Override
580 public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName)
581 throws IOException {
582 requirePermission("deleteTable", tableName, null, null, Action.ADMIN, Action.CREATE);
583 }
584
585 @Override
586 public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c,
587 byte[] tableName) throws IOException {
588 AccessControlLists.removeTablePermissions(c.getEnvironment().getConfiguration(), tableName);
589 }
590
591 @Override
592 public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName,
593 HTableDescriptor htd) throws IOException {
594 requirePermission("modifyTable", tableName, null, null, Action.ADMIN, Action.CREATE);
595 }
596
597 @Override
598 public void postModifyTable(ObserverContext<MasterCoprocessorEnvironment> c,
599 byte[] tableName, HTableDescriptor htd) throws IOException {
600 String owner = htd.getOwnerString();
601
602 if (owner == null) owner = getActiveUser().getShortName();
603 UserPermission userperm = new UserPermission(Bytes.toBytes(owner), htd.getName(), null,
604 Action.values());
605 AccessControlLists.addUserPermission(c.getEnvironment().getConfiguration(), userperm);
606 }
607
608 @Override
609 public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName,
610 HColumnDescriptor column) throws IOException {
611 requirePermission("addColumn", tableName, null, null, Action.ADMIN, Action.CREATE);
612 }
613
614 @Override
615 public void postAddColumn(ObserverContext<MasterCoprocessorEnvironment> c,
616 byte[] tableName, HColumnDescriptor column) throws IOException {}
617
618 @Override
619 public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName,
620 HColumnDescriptor descriptor) throws IOException {
621 requirePermission("modifyColumn", tableName, null, null, Action.ADMIN, Action.CREATE);
622 }
623
624 @Override
625 public void postModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c,
626 byte[] tableName, HColumnDescriptor descriptor) throws IOException {}
627
628 @Override
629 public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName,
630 byte[] col) throws IOException {
631 requirePermission("deleteColumn", tableName, null, null, Action.ADMIN, Action.CREATE);
632 }
633
634 @Override
635 public void postDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c,
636 byte[] tableName, byte[] col) throws IOException {
637 AccessControlLists.removeTablePermissions(c.getEnvironment().getConfiguration(),
638 tableName, col);
639 }
640
641 @Override
642 public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName)
643 throws IOException {
644 requirePermission("enableTable", tableName, null, null, Action.ADMIN, Action.CREATE);
645 }
646
647 @Override
648 public void postEnableTable(ObserverContext<MasterCoprocessorEnvironment> c,
649 byte[] tableName) throws IOException {}
650
651 @Override
652 public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName)
653 throws IOException {
654 if (Bytes.equals(tableName, AccessControlLists.ACL_GLOBAL_NAME)) {
655 throw new AccessDeniedException("Not allowed to disable "
656 + AccessControlLists.ACL_TABLE_NAME_STR + " table.");
657 }
658 requirePermission("disableTable", tableName, null, null, Action.ADMIN, Action.CREATE);
659 }
660
661 @Override
662 public void postDisableTable(ObserverContext<MasterCoprocessorEnvironment> c,
663 byte[] tableName) throws IOException {}
664
665 @Override
666 public void preMove(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo region,
667 ServerName srcServer, ServerName destServer) throws IOException {
668 requirePermission("move", region.getTableName(), null, null, Action.ADMIN);
669 }
670
671 @Override
672 public void postMove(ObserverContext<MasterCoprocessorEnvironment> c,
673 HRegionInfo region, ServerName srcServer, ServerName destServer)
674 throws IOException {}
675
676 @Override
677 public void preAssign(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo regionInfo)
678 throws IOException {
679 requirePermission("assign", regionInfo.getTableName(), null, null, Action.ADMIN);
680 }
681
682 @Override
683 public void postAssign(ObserverContext<MasterCoprocessorEnvironment> c,
684 HRegionInfo regionInfo) throws IOException {}
685
686 @Override
687 public void preUnassign(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo regionInfo,
688 boolean force) throws IOException {
689 requirePermission("unassign", regionInfo.getTableName(), null, null, Action.ADMIN);
690 }
691
692 @Override
693 public void postUnassign(ObserverContext<MasterCoprocessorEnvironment> c,
694 HRegionInfo regionInfo, boolean force) throws IOException {}
695
696 @Override
697 public void preBalance(ObserverContext<MasterCoprocessorEnvironment> c)
698 throws IOException {
699 requirePermission("balance", Permission.Action.ADMIN);
700 }
701 @Override
702 public void postBalance(ObserverContext<MasterCoprocessorEnvironment> c)
703 throws IOException {}
704
705 @Override
706 public boolean preBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> c,
707 boolean newValue) throws IOException {
708 requirePermission("balanceSwitch", Permission.Action.ADMIN);
709 return newValue;
710 }
711 @Override
712 public void postBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> c,
713 boolean oldValue, boolean newValue) throws IOException {}
714
715 @Override
716 public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> c)
717 throws IOException {
718 requirePermission("shutdown", Permission.Action.ADMIN);
719 }
720
721 @Override
722 public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> c)
723 throws IOException {
724 requirePermission("stopMaster", Permission.Action.ADMIN);
725 }
726
727 @Override
728 public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx)
729 throws IOException {
730
731 AccessControlLists.init(ctx.getEnvironment().getMasterServices());
732 }
733
734 @Override
735 public void preSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
736 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
737 throws IOException {
738 requirePermission("snapshot", Permission.Action.ADMIN);
739 }
740
741 @Override
742 public void postSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
743 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
744 throws IOException {
745 }
746
747 @Override
748 public void preCloneSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
749 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
750 throws IOException {
751 requirePermission("cloneSnapshot", Permission.Action.ADMIN);
752 }
753
754 @Override
755 public void postCloneSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
756 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
757 throws IOException {
758 }
759
760 @Override
761 public void preRestoreSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
762 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
763 throws IOException {
764 requirePermission("restoreSnapshot", Permission.Action.ADMIN);
765 }
766
767 @Override
768 public void postRestoreSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
769 final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
770 throws IOException {
771 }
772
773 @Override
774 public void preDeleteSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
775 final SnapshotDescription snapshot) throws IOException {
776 requirePermission("deleteSnapshot", Permission.Action.ADMIN);
777 }
778
779 @Override
780 public void postDeleteSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
781 final SnapshotDescription snapshot) throws IOException {
782 }
783
784
785
786 @Override
787 public void preOpen(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
788 RegionCoprocessorEnvironment env = e.getEnvironment();
789 final HRegion region = env.getRegion();
790 if (region == null) {
791 LOG.error("NULL region from RegionCoprocessorEnvironment in preOpen()");
792 return;
793 } else {
794 HRegionInfo regionInfo = region.getRegionInfo();
795 if (isSpecialTable(regionInfo)) {
796 isSystemOrSuperUser(regionEnv.getConfiguration());
797 } else {
798 requirePermission("open", Action.ADMIN);
799 }
800 }
801 }
802
803 @Override
804 public void postOpen(ObserverContext<RegionCoprocessorEnvironment> c) {
805 RegionCoprocessorEnvironment env = c.getEnvironment();
806 final HRegion region = env.getRegion();
807 if (region == null) {
808 LOG.error("NULL region from RegionCoprocessorEnvironment in postOpen()");
809 return;
810 }
811 if (AccessControlLists.isAclRegion(region)) {
812 aclRegion = true;
813 try {
814 initialize(env);
815 } catch (IOException ex) {
816
817
818 throw new RuntimeException("Failed to initialize permissions cache", ex);
819 }
820 }
821 }
822
823 @Override
824 public void preFlush(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
825 requirePermission("flush", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
826 }
827
828 @Override
829 public void preSplit(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
830 requirePermission("split", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
831 }
832
833 @Override
834 public InternalScanner preCompact(ObserverContext<RegionCoprocessorEnvironment> e,
835 final Store store, final InternalScanner scanner) throws IOException {
836 requirePermission("compact", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
837 return scanner;
838 }
839
840 @Override
841 public void preCompactSelection(final ObserverContext<RegionCoprocessorEnvironment> e,
842 final Store store, final List<StoreFile> candidates) throws IOException {
843 requirePermission("compactSelection", getTableName(e.getEnvironment()), null, null, Action.ADMIN);
844 }
845
846 @Override
847 public void preGetClosestRowBefore(final ObserverContext<RegionCoprocessorEnvironment> c,
848 final byte [] row, final byte [] family, final Result result)
849 throws IOException {
850 requirePermission("getClosestRowBefore", TablePermission.Action.READ, c.getEnvironment(),
851 (family != null ? Lists.newArrayList(family) : null));
852 }
853
854 @Override
855 public void preGet(final ObserverContext<RegionCoprocessorEnvironment> c,
856 final Get get, final List<KeyValue> result) throws IOException {
857
858
859
860
861 RegionCoprocessorEnvironment e = c.getEnvironment();
862 User requestUser = getActiveUser();
863 AuthResult authResult = permissionGranted("get", requestUser,
864 TablePermission.Action.READ, e, get.getFamilyMap());
865 if (!authResult.isAllowed()) {
866 if (hasFamilyQualifierPermission(requestUser,
867 TablePermission.Action.READ, e, get.getFamilyMap())) {
868 byte[] table = getTableName(e);
869 AccessControlFilter filter = new AccessControlFilter(authManager,
870 requestUser, table);
871
872
873 if (get.getFilter() != null) {
874 FilterList wrapper = new FilterList(FilterList.Operator.MUST_PASS_ALL,
875 Lists.newArrayList(filter, get.getFilter()));
876 get.setFilter(wrapper);
877 } else {
878 get.setFilter(filter);
879 }
880 logResult(AuthResult.allow("get", "Access allowed with filter", requestUser,
881 TablePermission.Action.READ, authResult.table));
882 } else {
883 logResult(authResult);
884 throw new AccessDeniedException("Insufficient permissions (table=" +
885 e.getRegion().getTableDesc().getNameAsString() + ", action=READ)");
886 }
887 } else {
888
889 logResult(authResult);
890 }
891 }
892
893 @Override
894 public boolean preExists(final ObserverContext<RegionCoprocessorEnvironment> c,
895 final Get get, final boolean exists) throws IOException {
896 requirePermission("exists", TablePermission.Action.READ, c.getEnvironment(),
897 get.familySet());
898 return exists;
899 }
900
901 @Override
902 public void prePut(final ObserverContext<RegionCoprocessorEnvironment> c,
903 final Put put, final WALEdit edit, final boolean writeToWAL)
904 throws IOException {
905 requirePermission("put", TablePermission.Action.WRITE, c.getEnvironment(),
906 put.getFamilyMap());
907 }
908
909 @Override
910 public void postPut(final ObserverContext<RegionCoprocessorEnvironment> c,
911 final Put put, final WALEdit edit, final boolean writeToWAL) {
912 if (aclRegion) {
913 updateACL(c.getEnvironment(), put.getFamilyMap());
914 }
915 }
916
917 @Override
918 public void preDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
919 final Delete delete, final WALEdit edit, final boolean writeToWAL)
920 throws IOException {
921 requirePermission("delete", TablePermission.Action.WRITE, c.getEnvironment(),
922 delete.getFamilyMap());
923 }
924
925 @Override
926 public void postDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
927 final Delete delete, final WALEdit edit, final boolean writeToWAL)
928 throws IOException {
929 if (aclRegion) {
930 updateACL(c.getEnvironment(), delete.getFamilyMap());
931 }
932 }
933
934 @Override
935 public boolean preCheckAndPut(final ObserverContext<RegionCoprocessorEnvironment> c,
936 final byte [] row, final byte [] family, final byte [] qualifier,
937 final CompareFilter.CompareOp compareOp,
938 final WritableByteArrayComparable comparator, final Put put,
939 final boolean result) throws IOException {
940 Collection<byte[]> familyMap = Arrays.asList(new byte[][]{family});
941 requirePermission("checkAndPut", TablePermission.Action.READ, c.getEnvironment(), familyMap);
942 requirePermission("checkAndPut", TablePermission.Action.WRITE, c.getEnvironment(), familyMap);
943 return result;
944 }
945
946 @Override
947 public boolean preCheckAndDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
948 final byte [] row, final byte [] family, final byte [] qualifier,
949 final CompareFilter.CompareOp compareOp,
950 final WritableByteArrayComparable comparator, final Delete delete,
951 final boolean result) throws IOException {
952 Collection<byte[]> familyMap = Arrays.asList(new byte[][]{family});
953 requirePermission("checkAndDelete", TablePermission.Action.READ, c.getEnvironment(), familyMap);
954 requirePermission("checkAndDelete", TablePermission.Action.WRITE, c.getEnvironment(), familyMap);
955 return result;
956 }
957
958 @Override
959 public long preIncrementColumnValue(final ObserverContext<RegionCoprocessorEnvironment> c,
960 final byte [] row, final byte [] family, final byte [] qualifier,
961 final long amount, final boolean writeToWAL)
962 throws IOException {
963 requirePermission("incrementColumnValue", TablePermission.Action.WRITE, c.getEnvironment(),
964 Arrays.asList(new byte[][]{family}));
965 return -1;
966 }
967
968 @Override
969 public Result preAppend(ObserverContext<RegionCoprocessorEnvironment> c, Append append)
970 throws IOException {
971 requirePermission("append", TablePermission.Action.WRITE, c.getEnvironment(), append.getFamilyMap());
972 return null;
973 }
974
975 @Override
976 public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> c,
977 final Increment increment)
978 throws IOException {
979 requirePermission("increment", TablePermission.Action.WRITE, c.getEnvironment(),
980 increment.getFamilyMap().keySet());
981 return null;
982 }
983
984 @Override
985 public RegionScanner preScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
986 final Scan scan, final RegionScanner s) throws IOException {
987
988
989
990
991 RegionCoprocessorEnvironment e = c.getEnvironment();
992 User user = getActiveUser();
993 AuthResult authResult = permissionGranted("scannerOpen", user, TablePermission.Action.READ, e,
994 scan.getFamilyMap());
995 if (!authResult.isAllowed()) {
996 if (hasFamilyQualifierPermission(user, TablePermission.Action.READ, e,
997 scan.getFamilyMap())) {
998 byte[] table = getTableName(e);
999 AccessControlFilter filter = new AccessControlFilter(authManager,
1000 user, table);
1001
1002
1003 if (scan.hasFilter()) {
1004 FilterList wrapper = new FilterList(FilterList.Operator.MUST_PASS_ALL,
1005 Lists.newArrayList(filter, scan.getFilter()));
1006 scan.setFilter(wrapper);
1007 } else {
1008 scan.setFilter(filter);
1009 }
1010 logResult(AuthResult.allow("scannerOpen", "Access allowed with filter", user,
1011 TablePermission.Action.READ, authResult.table));
1012 } else {
1013
1014 logResult(authResult);
1015 throw new AccessDeniedException("Insufficient permissions for user '"+
1016 (user != null ? user.getShortName() : "null")+"' "+
1017 "for scanner open on table " + Bytes.toString(getTableName(e)));
1018 }
1019 } else {
1020
1021 logResult(authResult);
1022 }
1023 return s;
1024 }
1025
1026 @Override
1027 public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
1028 final Scan scan, final RegionScanner s) throws IOException {
1029 User user = getActiveUser();
1030 if (user != null && user.getShortName() != null) {
1031 scannerOwners.put(s, user.getShortName());
1032 }
1033 return s;
1034 }
1035
1036 @Override
1037 public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c,
1038 final InternalScanner s, final List<Result> result,
1039 final int limit, final boolean hasNext) throws IOException {
1040 requireScannerOwner(s);
1041 return hasNext;
1042 }
1043
1044 @Override
1045 public void preScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
1046 final InternalScanner s) throws IOException {
1047 requireScannerOwner(s);
1048 }
1049
1050 @Override
1051 public void postScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
1052 final InternalScanner s) throws IOException {
1053
1054 scannerOwners.remove(s);
1055 }
1056
1057
1058
1059
1060
1061
1062 private void requireScannerOwner(InternalScanner s)
1063 throws AccessDeniedException {
1064 if (RequestContext.isInRequestContext()) {
1065 String requestUserName = RequestContext.getRequestUserName();
1066 String owner = scannerOwners.get(s);
1067 if (owner != null && !owner.equals(requestUserName)) {
1068 throw new AccessDeniedException("User '"+ requestUserName +"' is not the scanner owner!");
1069 }
1070 }
1071 }
1072
1073
1074
1075
1076
1077
1078
1079 @Override
1080 public void preBulkLoadHFile(ObserverContext<RegionCoprocessorEnvironment> ctx,
1081 List<Pair<byte[], String>> familyPaths) throws IOException {
1082 List<byte[]> cfs = new LinkedList<byte[]>();
1083 for(Pair<byte[],String> el : familyPaths) {
1084 cfs.add(el.getFirst());
1085 }
1086 requirePermission("bulkLoadHFile", Permission.Action.WRITE, ctx.getEnvironment(), cfs);
1087 }
1088
1089 private AuthResult hasSomeAccess(RegionCoprocessorEnvironment e, String request, Action action) throws IOException {
1090 User requestUser = getActiveUser();
1091 byte[] tableName = e.getRegion().getTableDesc().getName();
1092 AuthResult authResult = permissionGranted(request, requestUser,
1093 action, e, Collections.EMPTY_MAP);
1094 if (!authResult.isAllowed()) {
1095 for(UserPermission userPerm:
1096 AccessControlLists.getUserPermissions(regionEnv.getConfiguration(), tableName)) {
1097 for(Permission.Action userAction: userPerm.getActions()) {
1098 if(userAction.equals(action)) {
1099 return AuthResult.allow(request, "Access allowed", requestUser,
1100 action, tableName);
1101 }
1102 }
1103 }
1104 }
1105 return authResult;
1106 }
1107
1108
1109
1110
1111
1112
1113
1114 public void prePrepareBulkLoad(RegionCoprocessorEnvironment e) throws IOException {
1115 AuthResult authResult = hasSomeAccess(e, "prepareBulkLoad", Action.WRITE);
1116 logResult(authResult);
1117 if (!authResult.isAllowed()) {
1118 throw new AccessDeniedException("Insufficient permissions (table=" +
1119 e.getRegion().getTableDesc().getNameAsString() + ", action=WRITE)");
1120 }
1121 }
1122
1123
1124
1125
1126
1127
1128
1129
1130 public void preCleanupBulkLoad(RegionCoprocessorEnvironment e) throws IOException {
1131 AuthResult authResult = hasSomeAccess(e, "cleanupBulkLoad", Action.WRITE);
1132 logResult(authResult);
1133 if (!authResult.isAllowed()) {
1134 throw new AccessDeniedException("Insufficient permissions (table=" +
1135 e.getRegion().getTableDesc().getNameAsString() + ", action=WRITE)");
1136 }
1137 }
1138
1139
1140
1141
1142
1143
1144 @Override
1145 public void grant(UserPermission perm) throws IOException {
1146
1147 if (aclRegion) {
1148 if (LOG.isDebugEnabled()) {
1149 LOG.debug("Received request to grant access permission " + perm.toString());
1150 }
1151
1152 requirePermission("grant", perm.getTable(), perm.getFamily(), perm.getQualifier(), Action.ADMIN);
1153
1154 AccessControlLists.addUserPermission(regionEnv.getConfiguration(), perm);
1155 if (AUDITLOG.isTraceEnabled()) {
1156
1157 AUDITLOG.trace("Granted permission " + perm.toString());
1158 }
1159 } else {
1160 throw new CoprocessorException(AccessController.class, "This method "
1161 + "can only execute at " + Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
1162 }
1163 }
1164
1165 @Override
1166 @Deprecated
1167 public void grant(byte[] user, TablePermission permission)
1168 throws IOException {
1169 grant(new UserPermission(user, permission.getTable(),
1170 permission.getFamily(), permission.getQualifier(),
1171 permission.getActions()));
1172 }
1173
1174 @Override
1175 public void revoke(UserPermission perm) throws IOException {
1176
1177 if (aclRegion) {
1178 if (LOG.isDebugEnabled()) {
1179 LOG.debug("Received request to revoke access permission " + perm.toString());
1180 }
1181
1182 requirePermission("revoke", perm.getTable(), perm.getFamily(),
1183 perm.getQualifier(), Action.ADMIN);
1184
1185 AccessControlLists.removeUserPermission(regionEnv.getConfiguration(), perm);
1186 if (AUDITLOG.isTraceEnabled()) {
1187
1188 AUDITLOG.trace("Revoked permission " + perm.toString());
1189 }
1190 } else {
1191 throw new CoprocessorException(AccessController.class, "This method "
1192 + "can only execute at " + Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
1193 }
1194 }
1195
1196 @Override
1197 @Deprecated
1198 public void revoke(byte[] user, TablePermission permission)
1199 throws IOException {
1200 revoke(new UserPermission(user, permission.getTable(),
1201 permission.getFamily(), permission.getQualifier(),
1202 permission.getActions()));
1203 }
1204
1205 @Override
1206 public List<UserPermission> getUserPermissions(final byte[] tableName) throws IOException {
1207
1208 if (aclRegion) {
1209 requirePermission("userPermissions", tableName, null, null, Action.ADMIN);
1210
1211 List<UserPermission> perms = AccessControlLists.getUserPermissions(
1212 regionEnv.getConfiguration(), tableName);
1213 return perms;
1214 } else {
1215 throw new CoprocessorException(AccessController.class, "This method "
1216 + "can only execute at " + Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
1217 }
1218 }
1219
1220 @Override
1221 public void checkPermissions(Permission[] permissions) throws IOException {
1222 byte[] tableName = regionEnv.getRegion().getTableDesc().getName();
1223 for (Permission permission : permissions) {
1224 if (permission instanceof TablePermission) {
1225 TablePermission tperm = (TablePermission) permission;
1226 for (Permission.Action action : permission.getActions()) {
1227 if (!Arrays.equals(tperm.getTable(), tableName)) {
1228 throw new CoprocessorException(AccessController.class, String.format("This method "
1229 + "can only execute at the table specified in TablePermission. " +
1230 "Table of the region:%s , requested table:%s", Bytes.toString(tableName),
1231 Bytes.toString(tperm.getTable())));
1232 }
1233
1234 HashMap<byte[], Set<byte[]>> familyMap = Maps.newHashMapWithExpectedSize(1);
1235 if (tperm.getFamily() != null) {
1236 if (tperm.getQualifier() != null) {
1237 familyMap.put(tperm.getFamily(), Sets.newHashSet(tperm.getQualifier()));
1238 } else {
1239 familyMap.put(tperm.getFamily(), null);
1240 }
1241 }
1242
1243 requirePermission("checkPermissions", action, regionEnv, familyMap);
1244 }
1245
1246 } else {
1247 for (Permission.Action action : permission.getActions()) {
1248 requirePermission("checkPermissions", action);
1249 }
1250 }
1251 }
1252 }
1253
1254 @Override
1255 public long getProtocolVersion(String protocol, long clientVersion) throws IOException {
1256 return PROTOCOL_VERSION;
1257 }
1258
1259 @Override
1260 public ProtocolSignature getProtocolSignature(String protocol,
1261 long clientVersion, int clientMethodsHash) throws IOException {
1262 if (AccessControllerProtocol.class.getName().equals(protocol)) {
1263 return new ProtocolSignature(PROTOCOL_VERSION, null);
1264 }
1265 throw new HBaseRPC.UnknownProtocolException(
1266 "Unexpected protocol requested: "+protocol);
1267 }
1268
1269 private byte[] getTableName(RegionCoprocessorEnvironment e) {
1270 HRegion region = e.getRegion();
1271 byte[] tableName = null;
1272
1273 if (region != null) {
1274 HRegionInfo regionInfo = region.getRegionInfo();
1275 if (regionInfo != null) {
1276 tableName = regionInfo.getTableName();
1277 }
1278 }
1279 return tableName;
1280 }
1281
1282
1283 @Override
1284 public void preClose(ObserverContext<RegionCoprocessorEnvironment> e, boolean abortRequested)
1285 throws IOException {
1286 requirePermission("close", Permission.Action.ADMIN);
1287 }
1288
1289 @Override
1290 public void preLockRow(ObserverContext<RegionCoprocessorEnvironment> ctx, byte[] regionName,
1291 byte[] row) throws IOException {
1292 requirePermission("lockRow", getTableName(ctx.getEnvironment()), null, null,
1293 Permission.Action.WRITE, Permission.Action.CREATE);
1294 }
1295
1296 @Override
1297 public void preUnlockRow(ObserverContext<RegionCoprocessorEnvironment> ctx, byte[] regionName,
1298 long lockId) throws IOException {
1299 requirePermission("unlockRow", getTableName(ctx.getEnvironment()), null, null,
1300 Permission.Action.WRITE, Permission.Action.CREATE);
1301 }
1302
1303 private void isSystemOrSuperUser(Configuration conf) throws IOException {
1304 User user = User.getCurrent();
1305 if (user == null) {
1306 throw new IOException("Unable to obtain the current user, "
1307 + "authorization checks for internal operations will not work correctly!");
1308 }
1309
1310 String currentUser = user.getShortName();
1311 List<String> superusers = Lists.asList(currentUser,
1312 conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0]));
1313
1314 User activeUser = getActiveUser();
1315 if (!(superusers.contains(activeUser.getShortName()))) {
1316 throw new AccessDeniedException("User '" + (user != null ? user.getShortName() : "null")
1317 + "is not system or super user.");
1318 }
1319 }
1320
1321 private boolean isSpecialTable(HRegionInfo regionInfo) {
1322 byte[] tableName = regionInfo.getTableName();
1323 return tableName.equals(AccessControlLists.ACL_TABLE_NAME)
1324 || tableName.equals(Bytes.toBytes("-ROOT-"))
1325 || tableName.equals(Bytes.toBytes(".META."));
1326 }
1327
1328 @Override
1329 public void preStopRegionServer(ObserverContext<RegionServerCoprocessorEnvironment> env)
1330 throws IOException {
1331 requirePermission("stop", Permission.Action.ADMIN);
1332 }
1333 }