View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.security.access;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertTrue;
24  import static org.junit.Assert.fail;
25  
26  import java.io.IOException;
27  import java.lang.reflect.UndeclaredThrowableException;
28  import java.security.PrivilegedActionException;
29  import java.security.PrivilegedExceptionAction;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.NavigableMap;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.hadoop.conf.Configuration;
37  import org.apache.hadoop.fs.FileStatus;
38  import org.apache.hadoop.fs.FileSystem;
39  import org.apache.hadoop.fs.Path;
40  import org.apache.hadoop.fs.permission.FsPermission;
41  import org.apache.hadoop.hbase.Coprocessor;
42  import org.apache.hadoop.hbase.HBaseTestingUtility;
43  import org.apache.hadoop.hbase.HColumnDescriptor;
44  import org.apache.hadoop.hbase.HConstants;
45  import org.apache.hadoop.hbase.HRegionInfo;
46  import org.apache.hadoop.hbase.HTableDescriptor;
47  import org.apache.hadoop.hbase.KeyValue;
48  import org.apache.hadoop.hbase.LargeTests;
49  import org.apache.hadoop.hbase.MiniHBaseCluster;
50  import org.apache.hadoop.hbase.ServerName;
51  import org.apache.hadoop.hbase.TableName;
52  import org.apache.hadoop.hbase.TableNotFoundException;
53  import org.apache.hadoop.hbase.client.Append;
54  import org.apache.hadoop.hbase.client.Delete;
55  import org.apache.hadoop.hbase.client.Get;
56  import org.apache.hadoop.hbase.client.HBaseAdmin;
57  import org.apache.hadoop.hbase.client.HTable;
58  import org.apache.hadoop.hbase.client.Increment;
59  import org.apache.hadoop.hbase.client.Put;
60  import org.apache.hadoop.hbase.client.Result;
61  import org.apache.hadoop.hbase.client.ResultScanner;
62  import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException;
63  import org.apache.hadoop.hbase.client.Scan;
64  import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
65  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
66  import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
67  import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
68  import org.apache.hadoop.hbase.io.hfile.CacheConfig;
69  import org.apache.hadoop.hbase.io.hfile.HFile;
70  import org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles;
71  import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
72  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
73  import org.apache.hadoop.hbase.protobuf.RequestConverter;
74  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos;
75  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
76  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.CheckPermissionsRequest;
77  import org.apache.hadoop.hbase.regionserver.HRegion;
78  import org.apache.hadoop.hbase.regionserver.HRegionServer;
79  import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost;
80  import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
81  import org.apache.hadoop.hbase.regionserver.ScanType;
82  import org.apache.hadoop.hbase.security.AccessDeniedException;
83  import org.apache.hadoop.hbase.security.User;
84  import org.apache.hadoop.hbase.security.access.Permission.Action;
85  import org.apache.hadoop.hbase.util.Bytes;
86  import org.apache.hadoop.hbase.util.JVMClusterUtil;
87  import org.apache.hadoop.hbase.util.TestTableName;
88  import org.junit.After;
89  import org.junit.AfterClass;
90  import org.junit.Before;
91  import org.junit.BeforeClass;
92  import org.junit.Rule;
93  import org.junit.Test;
94  import org.junit.experimental.categories.Category;
95  
96  import com.google.protobuf.BlockingRpcChannel;
97  import com.google.protobuf.ServiceException;
98  
99  /**
100  * Performs authorization checks for common operations, according to different
101  * levels of authorized users.
102  */
103 @Category(LargeTests.class)
104 @SuppressWarnings("rawtypes")
105 public class TestAccessController extends SecureTestUtil {
106   private static final Log LOG = LogFactory.getLog(TestAccessController.class);
107   @Rule public TestTableName TEST_TABLE = new TestTableName();
108   private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
109   private static Configuration conf;
110 
111   // user with all permissions
112   private static User SUPERUSER;
113   // user granted with all global permission
114   private static User USER_ADMIN;
115   // user with rw permissions on column family.
116   private static User USER_RW;
117   // user with read-only permissions
118   private static User USER_RO;
119   // user is table owner. will have all permissions on table
120   private static User USER_OWNER;
121   // user with create table permissions alone
122   private static User USER_CREATE;
123   // user with no permissions
124   private static User USER_NONE;
125 
126   private static TableName TEST_TABLE2 =
127       TableName.valueOf("testtable2");
128   private static byte[] TEST_FAMILY = Bytes.toBytes("f1");
129 
130   private static MasterCoprocessorEnvironment CP_ENV;
131   private static AccessController ACCESS_CONTROLLER;
132   private static RegionServerCoprocessorEnvironment RSCP_ENV;
133   private RegionCoprocessorEnvironment RCP_ENV;
134 
135   @BeforeClass
136   public static void setupBeforeClass() throws Exception {
137     // setup configuration
138     conf = TEST_UTIL.getConfiguration();
139     conf.set("hbase.master.hfilecleaner.plugins",
140       "org.apache.hadoop.hbase.master.cleaner.HFileLinkCleaner," +
141       "org.apache.hadoop.hbase.master.snapshot.SnapshotHFileCleaner");
142     conf.set("hbase.master.logcleaner.plugins",
143       "org.apache.hadoop.hbase.master.snapshot.SnapshotLogCleaner");
144     SecureTestUtil.enableSecurity(conf);
145 
146     TEST_UTIL.startMiniCluster();
147     MasterCoprocessorHost cpHost = TEST_UTIL.getMiniHBaseCluster().getMaster().getCoprocessorHost();
148     cpHost.load(AccessController.class, Coprocessor.PRIORITY_HIGHEST, conf);
149     ACCESS_CONTROLLER = (AccessController) cpHost.findCoprocessor(AccessController.class.getName());
150     CP_ENV = cpHost.createEnvironment(AccessController.class, ACCESS_CONTROLLER,
151       Coprocessor.PRIORITY_HIGHEST, 1, conf);
152     RegionServerCoprocessorHost rsHost = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0)
153         .getCoprocessorHost();
154     RSCP_ENV = rsHost.createEnvironment(AccessController.class, ACCESS_CONTROLLER,
155       Coprocessor.PRIORITY_HIGHEST, 1, conf);
156 
157     // Wait for the ACL table to become available
158     TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME.getName());
159 
160     // create a set of test users
161     SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
162     USER_ADMIN = User.createUserForTesting(conf, "admin2", new String[0]);
163     USER_RW = User.createUserForTesting(conf, "rwuser", new String[0]);
164     USER_RO = User.createUserForTesting(conf, "rouser", new String[0]);
165     USER_OWNER = User.createUserForTesting(conf, "owner", new String[0]);
166     USER_CREATE = User.createUserForTesting(conf, "tbl_create", new String[0]);
167     USER_NONE = User.createUserForTesting(conf, "nouser", new String[0]);
168   }
169 
170   @AfterClass
171   public static void tearDownAfterClass() throws Exception {
172     TEST_UTIL.shutdownMiniCluster();
173   }
174 
175   @Before
176   public void setUp() throws Exception {
177     // Create the test table (owner added to the _acl_ table)
178     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
179     HTableDescriptor htd = new HTableDescriptor(TEST_TABLE.getTableName());
180     htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
181     htd.setOwner(USER_OWNER);
182     admin.createTable(htd);
183     TEST_UTIL.waitTableEnabled(TEST_TABLE.getTableName().getName());
184 
185     HRegion region = TEST_UTIL.getHBaseCluster().getRegions(TEST_TABLE.getTableName()).get(0);
186     RegionCoprocessorHost rcpHost = region.getCoprocessorHost();
187     RCP_ENV = rcpHost.createEnvironment(AccessController.class, ACCESS_CONTROLLER,
188       Coprocessor.PRIORITY_HIGHEST, 1, conf);
189 
190     // initilize access control
191     HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
192     try {
193       BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE.getTableName().getName());
194       AccessControlService.BlockingInterface protocol =
195         AccessControlService.newBlockingStub(service);
196 
197       protocol.grant(null, RequestConverter.buildGrantRequest(USER_ADMIN.getShortName(),
198         AccessControlProtos.Permission.Action.ADMIN,
199         AccessControlProtos.Permission.Action.CREATE,
200         AccessControlProtos.Permission.Action.READ,
201         AccessControlProtos.Permission.Action.WRITE));
202 
203       protocol.grant(null, RequestConverter.buildGrantRequest(USER_RW.getShortName(),
204         TEST_TABLE.getTableName(), TEST_FAMILY, null,
205         AccessControlProtos.Permission.Action.READ,
206         AccessControlProtos.Permission.Action.WRITE));
207 
208       // USER_CREATE is USER_RW plus CREATE permissions
209       protocol.grant(null, RequestConverter.buildGrantRequest(USER_CREATE.getShortName(),
210         TEST_TABLE.getTableName(), null, null,
211         AccessControlProtos.Permission.Action.CREATE,
212         AccessControlProtos.Permission.Action.READ,
213         AccessControlProtos.Permission.Action.WRITE));
214 
215       protocol.grant(null, RequestConverter.buildGrantRequest(USER_RO.getShortName(), TEST_TABLE.getTableName(),
216         TEST_FAMILY, null, AccessControlProtos.Permission.Action.READ));
217 
218       assertEquals(4, AccessControlLists.getTablePermissions(conf, TEST_TABLE.getTableName()).size());
219     } finally {
220       acl.close();
221     }
222   }
223 
224   @After
225   public void tearDown() throws Exception {
226     // Clean the _acl_ table
227     try {
228       TEST_UTIL.deleteTable(TEST_TABLE.getTableName());
229     } catch (TableNotFoundException ex) {
230       // Test deleted the table, no problem
231       LOG.info("Test deleted table " + TEST_TABLE.getTableName());
232     }
233     assertEquals(0, AccessControlLists.getTablePermissions(conf, TEST_TABLE.getTableName()).size());
234   }
235 
236   @Override
237   public void verifyAllowed(PrivilegedExceptionAction action, User... users) throws Exception {
238     for (User user : users) {
239       verifyAllowed(user, action);
240     }
241   }
242 
243   @Override
244   public void verifyDenied(User user, PrivilegedExceptionAction... actions) throws Exception {
245     for (PrivilegedExceptionAction action : actions) {
246       try {
247         user.runAs(action);
248         fail("Expected AccessDeniedException for user '" + user.getShortName() + "'");
249       } catch (IOException e) {
250         boolean isAccessDeniedException = false;
251         if(e instanceof RetriesExhaustedWithDetailsException) {
252           // in case of batch operations, and put, the client assembles a
253           // RetriesExhaustedWithDetailsException instead of throwing an
254           // AccessDeniedException
255           for(Throwable ex : ((RetriesExhaustedWithDetailsException) e).getCauses()) {
256             if (ex instanceof AccessDeniedException) {
257               isAccessDeniedException = true;
258               break;
259             }
260           }
261         }
262         else {
263           // For doBulkLoad calls AccessDeniedException
264           // is buried in the stack trace
265           Throwable ex = e;
266           do {
267             if (ex instanceof AccessDeniedException) {
268               isAccessDeniedException = true;
269               break;
270             }
271           } while((ex = ex.getCause()) != null);
272         }
273         if (!isAccessDeniedException) {
274           fail("Not receiving AccessDeniedException for user '" + user.getShortName() + "'");
275         }
276       } catch (UndeclaredThrowableException ute) {
277         // TODO why we get a PrivilegedActionException, which is unexpected?
278         Throwable ex = ute.getUndeclaredThrowable();
279         if (ex instanceof PrivilegedActionException) {
280           ex = ((PrivilegedActionException) ex).getException();
281         }
282         if (ex instanceof ServiceException) {
283           ServiceException se = (ServiceException)ex;
284           if (se.getCause() != null && se.getCause() instanceof AccessDeniedException) {
285             // expected result
286             return;
287           }
288         }
289         fail("Not receiving AccessDeniedException for user '" + user.getShortName() + "'");
290       }
291     }
292   }
293 
294   @Override
295   public void verifyDenied(PrivilegedExceptionAction action, User... users) throws Exception {
296     for (User user : users) {
297       verifyDenied(user, action);
298     }
299   }
300 
301   @Test
302   public void testTableCreate() throws Exception {
303     PrivilegedExceptionAction createTable = new PrivilegedExceptionAction() {
304       @Override
305       public Object run() throws Exception {
306         HTableDescriptor htd = new HTableDescriptor(TableName.valueOf("testnewtable"));
307         htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
308         ACCESS_CONTROLLER.preCreateTable(ObserverContext.createAndPrepare(CP_ENV, null), htd, null);
309         return null;
310       }
311     };
312 
313     // verify that superuser can create tables
314     verifyAllowed(createTable, SUPERUSER, USER_ADMIN);
315 
316     // all others should be denied
317     verifyDenied(createTable, USER_CREATE, USER_RW, USER_RO, USER_NONE);
318   }
319 
320   @Test
321   public void testTableModify() throws Exception {
322     PrivilegedExceptionAction modifyTable = new PrivilegedExceptionAction() {
323       @Override
324       public Object run() throws Exception {
325         HTableDescriptor htd = new HTableDescriptor(TEST_TABLE.getTableName());
326         htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
327         htd.addFamily(new HColumnDescriptor("fam_" + User.getCurrent().getShortName()));
328         ACCESS_CONTROLLER.preModifyTable(ObserverContext.createAndPrepare(CP_ENV, null),
329           TEST_TABLE.getTableName(), htd);
330         return null;
331       }
332     };
333 
334     verifyAllowed(modifyTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
335     verifyDenied(modifyTable, USER_RW, USER_RO, USER_NONE);
336   }
337 
338   @Test
339   public void testTableDelete() throws Exception {
340     PrivilegedExceptionAction deleteTable = new PrivilegedExceptionAction() {
341       @Override
342       public Object run() throws Exception {
343         ACCESS_CONTROLLER
344             .preDeleteTable(ObserverContext.createAndPrepare(CP_ENV, null), TEST_TABLE.getTableName());
345         return null;
346       }
347     };
348 
349     verifyAllowed(deleteTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
350     verifyDenied(deleteTable, USER_RW, USER_RO, USER_NONE);
351   }
352 
353   @Test
354   public void testAddColumn() throws Exception {
355     final HColumnDescriptor hcd = new HColumnDescriptor("fam_new");
356     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
357       @Override
358       public Object run() throws Exception {
359         ACCESS_CONTROLLER.preAddColumn(ObserverContext.createAndPrepare(CP_ENV, null), TEST_TABLE.getTableName(),
360           hcd);
361         return null;
362       }
363     };
364 
365     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
366     verifyDenied(action, USER_RW, USER_RO, USER_NONE);
367   }
368 
369   @Test
370   public void testModifyColumn() throws Exception {
371     final HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY);
372     hcd.setMaxVersions(10);
373     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
374       @Override
375       public Object run() throws Exception {
376         ACCESS_CONTROLLER.preModifyColumn(ObserverContext.createAndPrepare(CP_ENV, null),
377           TEST_TABLE.getTableName(), hcd);
378         return null;
379       }
380     };
381 
382     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
383     verifyDenied(action, USER_RW, USER_RO, USER_NONE);
384   }
385 
386   @Test
387   public void testDeleteColumn() throws Exception {
388     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
389       @Override
390       public Object run() throws Exception {
391         ACCESS_CONTROLLER.preDeleteColumn(ObserverContext.createAndPrepare(CP_ENV, null),
392           TEST_TABLE.getTableName(), TEST_FAMILY);
393         return null;
394       }
395     };
396 
397     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
398     verifyDenied(action, USER_RW, USER_RO, USER_NONE);
399   }
400 
401   @Test
402   public void testTableDisable() throws Exception {
403     PrivilegedExceptionAction disableTable = new PrivilegedExceptionAction() {
404       @Override
405       public Object run() throws Exception {
406         ACCESS_CONTROLLER.preDisableTable(ObserverContext.createAndPrepare(CP_ENV, null),
407           TEST_TABLE.getTableName());
408         return null;
409       }
410     };
411 
412     PrivilegedExceptionAction disableAclTable = new PrivilegedExceptionAction() {
413       @Override
414       public Object run() throws Exception {
415         ACCESS_CONTROLLER.preDisableTable(ObserverContext.createAndPrepare(CP_ENV, null),
416             AccessControlLists.ACL_TABLE_NAME);
417         return null;
418       }
419     };
420 
421     verifyAllowed(disableTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
422     verifyDenied(disableTable, USER_RW, USER_RO, USER_NONE);
423 
424     // No user should be allowed to disable _acl_ table
425     verifyDenied(disableAclTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, USER_RW, USER_RO);
426   }
427 
428   @Test
429   public void testTableEnable() throws Exception {
430     PrivilegedExceptionAction enableTable = new PrivilegedExceptionAction() {
431       @Override
432       public Object run() throws Exception {
433         ACCESS_CONTROLLER
434             .preEnableTable(ObserverContext.createAndPrepare(CP_ENV, null), TEST_TABLE.getTableName());
435         return null;
436       }
437     };
438 
439     verifyAllowed(enableTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
440     verifyDenied(enableTable, USER_RW, USER_RO, USER_NONE);
441   }
442 
443   @Test
444   public void testMove() throws Exception {
445     Map<HRegionInfo, ServerName> regions;
446     HTable table = new HTable(TEST_UTIL.getConfiguration(), TEST_TABLE.getTableName());
447     try {
448       regions = table.getRegionLocations();
449     } finally {
450       table.close();
451     }
452     final Map.Entry<HRegionInfo, ServerName> firstRegion = regions.entrySet().iterator().next();
453     final ServerName server = TEST_UTIL.getHBaseCluster().getRegionServer(0).getServerName();
454     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
455       @Override
456       public Object run() throws Exception {
457         ACCESS_CONTROLLER.preMove(ObserverContext.createAndPrepare(CP_ENV, null),
458           firstRegion.getKey(), server, server);
459         return null;
460       }
461     };
462 
463     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
464     verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
465   }
466 
467   @Test
468   public void testAssign() throws Exception {
469     Map<HRegionInfo, ServerName> regions;
470     HTable table = new HTable(TEST_UTIL.getConfiguration(), TEST_TABLE.getTableName());
471     try {
472       regions = table.getRegionLocations();
473     } finally {
474       table.close();
475     }
476     final Map.Entry<HRegionInfo, ServerName> firstRegion = regions.entrySet().iterator().next();
477 
478     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
479       @Override
480       public Object run() throws Exception {
481         ACCESS_CONTROLLER.preAssign(ObserverContext.createAndPrepare(CP_ENV, null),
482           firstRegion.getKey());
483         return null;
484       }
485     };
486 
487     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
488     verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
489   }
490 
491   @Test
492   public void testUnassign() throws Exception {
493     Map<HRegionInfo, ServerName> regions;
494     HTable table = new HTable(TEST_UTIL.getConfiguration(), TEST_TABLE.getTableName());
495     try {
496       regions = table.getRegionLocations();
497     } finally {
498       table.close();
499     }
500     final Map.Entry<HRegionInfo, ServerName> firstRegion = regions.entrySet().iterator().next();
501 
502     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
503       @Override
504       public Object run() throws Exception {
505         ACCESS_CONTROLLER.preUnassign(ObserverContext.createAndPrepare(CP_ENV, null),
506           firstRegion.getKey(), false);
507         return null;
508       }
509     };
510 
511     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
512     verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
513   }
514 
515   @Test
516   public void testRegionOffline() throws Exception {
517     Map<HRegionInfo, ServerName> regions;
518     HTable table = new HTable(TEST_UTIL.getConfiguration(), TEST_TABLE.getTableName());
519     try {
520       regions = table.getRegionLocations();
521     } finally {
522       table.close();
523     }
524     final Map.Entry<HRegionInfo, ServerName> firstRegion = regions.entrySet().iterator().next();
525 
526     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
527       @Override
528       public Object run() throws Exception {
529         ACCESS_CONTROLLER.preRegionOffline(ObserverContext.createAndPrepare(CP_ENV, null),
530           firstRegion.getKey());
531         return null;
532       }
533     };
534 
535     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
536     verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
537   }
538 
539   @Test
540   public void testBalance() throws Exception {
541     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
542       @Override
543       public Object run() throws Exception {
544         ACCESS_CONTROLLER.preBalance(ObserverContext.createAndPrepare(CP_ENV, null));
545         return null;
546       }
547     };
548 
549     verifyAllowed(action, SUPERUSER, USER_ADMIN);
550     verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE);
551   }
552 
553   @Test
554   public void testBalanceSwitch() throws Exception {
555     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
556       @Override
557       public Object run() throws Exception {
558         ACCESS_CONTROLLER.preBalanceSwitch(ObserverContext.createAndPrepare(CP_ENV, null), true);
559         return null;
560       }
561     };
562 
563     verifyAllowed(action, SUPERUSER, USER_ADMIN);
564     verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE);
565   }
566 
567   @Test
568   public void testShutdown() throws Exception {
569     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
570       @Override
571       public Object run() throws Exception {
572         ACCESS_CONTROLLER.preShutdown(ObserverContext.createAndPrepare(CP_ENV, null));
573         return null;
574       }
575     };
576 
577     verifyAllowed(action, SUPERUSER, USER_ADMIN);
578     verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE);
579   }
580 
581   @Test
582   public void testStopMaster() throws Exception {
583     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
584       @Override
585       public Object run() throws Exception {
586         ACCESS_CONTROLLER.preStopMaster(ObserverContext.createAndPrepare(CP_ENV, null));
587         return null;
588       }
589     };
590 
591     verifyAllowed(action, SUPERUSER, USER_ADMIN);
592     verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE);
593   }
594 
595   private void verifyWrite(PrivilegedExceptionAction action) throws Exception {
596     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_RW);
597     verifyDenied(action, USER_NONE, USER_RO);
598   }
599 
600   @Test
601   public void testSplit() throws Exception {
602     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
603       @Override
604       public Object run() throws Exception {
605         ACCESS_CONTROLLER.preSplit(ObserverContext.createAndPrepare(RCP_ENV, null));
606         return null;
607       }
608     };
609 
610     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
611     verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
612   }
613 
614   @Test
615   public void testSplitWithSplitRow() throws Exception {
616     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
617       @Override
618       public Object run() throws Exception {
619         ACCESS_CONTROLLER.preSplit(
620             ObserverContext.createAndPrepare(RCP_ENV, null),
621             Bytes.toBytes("row2"));
622         return null;
623       }
624     };
625 
626     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
627     verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
628   }
629 
630 
631   @Test
632   public void testFlush() throws Exception {
633     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
634       @Override
635       public Object run() throws Exception {
636         ACCESS_CONTROLLER.preFlush(ObserverContext.createAndPrepare(RCP_ENV, null));
637         return null;
638       }
639     };
640 
641     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
642     verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
643   }
644 
645   @Test
646   public void testCompact() throws Exception {
647     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
648       @Override
649       public Object run() throws Exception {
650         ACCESS_CONTROLLER.preCompact(ObserverContext.createAndPrepare(RCP_ENV, null), null, null,
651           ScanType.COMPACT_RETAIN_DELETES);
652         return null;
653       }
654     };
655 
656     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
657     verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
658   }
659 
660   @Test
661   public void testPreCompactSelection() throws Exception {
662     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
663       @Override
664       public Object run() throws Exception {
665         ACCESS_CONTROLLER.preCompactSelection(ObserverContext.createAndPrepare(RCP_ENV, null), null, null);
666         return null;
667       }
668     };
669 
670     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
671     verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
672   }
673 
674   private void verifyRead(PrivilegedExceptionAction action) throws Exception {
675     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_RW, USER_RO);
676     verifyDenied(action, USER_NONE);
677   }
678 
679   private void verifyReadWrite(PrivilegedExceptionAction action) throws Exception {
680     verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_RW);
681     verifyDenied(action, USER_NONE, USER_RO);
682   }
683 
684   @Test
685   public void testRead() throws Exception {
686     // get action
687     PrivilegedExceptionAction getAction = new PrivilegedExceptionAction() {
688       @Override
689       public Object run() throws Exception {
690         Get g = new Get(Bytes.toBytes("random_row"));
691         g.addFamily(TEST_FAMILY);
692         HTable t = new HTable(conf, TEST_TABLE.getTableName());
693         try {
694           t.get(g);
695         } finally {
696           t.close();
697         }
698         return null;
699       }
700     };
701     verifyRead(getAction);
702 
703     // action for scanning
704     PrivilegedExceptionAction scanAction = new PrivilegedExceptionAction() {
705       @Override
706       public Object run() throws Exception {
707         Scan s = new Scan();
708         s.addFamily(TEST_FAMILY);
709 
710         HTable table = new HTable(conf, TEST_TABLE.getTableName());
711         try {
712           ResultScanner scanner = table.getScanner(s);
713           try {
714             for (Result r = scanner.next(); r != null; r = scanner.next()) {
715               // do nothing
716             }
717           } catch (IOException e) {
718           } finally {
719             scanner.close();
720           }
721         } finally {
722           table.close();
723         }
724         return null;
725       }
726     };
727     verifyRead(scanAction);
728   }
729 
730   @Test
731   // test put, delete, increment
732   public void testWrite() throws Exception {
733     // put action
734     PrivilegedExceptionAction putAction = new PrivilegedExceptionAction() {
735       @Override
736       public Object run() throws Exception {
737         Put p = new Put(Bytes.toBytes("random_row"));
738         p.add(TEST_FAMILY, Bytes.toBytes("Qualifier"), Bytes.toBytes(1));
739         HTable t = new HTable(conf, TEST_TABLE.getTableName());
740         try {
741           t.put(p);
742         } finally {
743           t.close();
744         }
745         return null;
746       }
747     };
748     verifyWrite(putAction);
749 
750     // delete action
751     PrivilegedExceptionAction deleteAction = new PrivilegedExceptionAction() {
752       @Override
753       public Object run() throws Exception {
754         Delete d = new Delete(Bytes.toBytes("random_row"));
755         d.deleteFamily(TEST_FAMILY);
756         HTable t = new HTable(conf, TEST_TABLE.getTableName());
757         try {
758           t.delete(d);
759         } finally {
760           t.close();
761         }
762         return null;
763       }
764     };
765     verifyWrite(deleteAction);
766 
767     // increment action
768     PrivilegedExceptionAction incrementAction = new PrivilegedExceptionAction() {
769       @Override
770       public Object run() throws Exception {
771         Increment inc = new Increment(Bytes.toBytes("random_row"));
772         inc.addColumn(TEST_FAMILY, Bytes.toBytes("Qualifier"), 1);
773         HTable t = new HTable(conf, TEST_TABLE.getTableName());
774         try {
775           t.increment(inc);
776         } finally {
777           t.close();
778         }
779         return null;
780       }
781     };
782     verifyWrite(incrementAction);
783   }
784 
785   @Test
786   public void testReadWrite() throws Exception {
787     // action for checkAndDelete
788     PrivilegedExceptionAction checkAndDeleteAction = new PrivilegedExceptionAction() {
789       @Override
790       public Object run() throws Exception {
791         Delete d = new Delete(Bytes.toBytes("random_row"));
792         d.deleteFamily(TEST_FAMILY);
793         HTable t = new HTable(conf, TEST_TABLE.getTableName());
794         try {
795           t.checkAndDelete(Bytes.toBytes("random_row"), TEST_FAMILY, Bytes.toBytes("q"),
796             Bytes.toBytes("test_value"), d);
797         } finally {
798           t.close();
799         }
800         return null;
801       }
802     };
803     verifyReadWrite(checkAndDeleteAction);
804 
805     // action for checkAndPut()
806     PrivilegedExceptionAction checkAndPut = new PrivilegedExceptionAction() {
807       @Override
808       public Object run() throws Exception {
809         Put p = new Put(Bytes.toBytes("random_row"));
810         p.add(TEST_FAMILY, Bytes.toBytes("Qualifier"), Bytes.toBytes(1));
811         HTable t = new HTable(conf, TEST_TABLE.getTableName());
812         try {
813           t.checkAndPut(Bytes.toBytes("random_row"), TEST_FAMILY, Bytes.toBytes("q"),
814            Bytes.toBytes("test_value"), p);
815         } finally {
816           t.close();
817         }
818         return null;
819       }
820     };
821     verifyReadWrite(checkAndPut);
822   }
823 
824   @Test
825   public void testBulkLoad() throws Exception {
826     FileSystem fs = TEST_UTIL.getTestFileSystem();
827     final Path dir = TEST_UTIL.getDataTestDirOnTestFS("testBulkLoad");
828     fs.mkdirs(dir);
829     //need to make it globally writable
830     //so users creating HFiles have write permissions
831     fs.setPermission(dir, FsPermission.valueOf("-rwxrwxrwx"));
832 
833     PrivilegedExceptionAction bulkLoadAction = new PrivilegedExceptionAction() {
834       @Override
835       public Object run() throws Exception {
836         int numRows = 3;
837 
838         //Making the assumption that the test table won't split between the range
839         byte[][][] hfileRanges = {{{(byte)0}, {(byte)9}}};
840 
841         Path bulkLoadBasePath = new Path(dir, new Path(User.getCurrent().getName()));
842         new BulkLoadHelper(bulkLoadBasePath)
843             .bulkLoadHFile(TEST_TABLE.getTableName(), TEST_FAMILY, Bytes.toBytes("q"), hfileRanges, numRows);
844 
845         return null;
846       }
847     };
848 
849     // User performing bulk loads must have privilege to read table metadata
850     // (ADMIN or CREATE)
851     verifyAllowed(bulkLoadAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE);
852     verifyDenied(bulkLoadAction, USER_RW, USER_NONE, USER_RO);
853 
854     // Reinit after the bulk upload
855     TEST_UTIL.getHBaseAdmin().disableTable(TEST_TABLE.getTableName());
856     TEST_UTIL.getHBaseAdmin().enableTable(TEST_TABLE.getTableName());
857   }
858 
859   public class BulkLoadHelper {
860     private final FileSystem fs;
861     private final Path loadPath;
862     private final Configuration conf;
863 
864     public BulkLoadHelper(Path loadPath) throws IOException {
865       fs = TEST_UTIL.getTestFileSystem();
866       conf = TEST_UTIL.getConfiguration();
867       loadPath = loadPath.makeQualified(fs);
868       this.loadPath = loadPath;
869     }
870 
871     private void createHFile(Path path,
872         byte[] family, byte[] qualifier,
873         byte[] startKey, byte[] endKey, int numRows) throws IOException {
874 
875       HFile.Writer writer = null;
876       long now = System.currentTimeMillis();
877       try {
878         writer = HFile.getWriterFactory(conf, new CacheConfig(conf))
879             .withPath(fs, path)
880             .create();
881         // subtract 2 since numRows doesn't include boundary keys
882         for (byte[] key : Bytes.iterateOnSplits(startKey, endKey, true, numRows-2)) {
883           KeyValue kv = new KeyValue(key, family, qualifier, now, key);
884           writer.append(kv);
885         }
886       } finally {
887         if(writer != null)
888           writer.close();
889       }
890     }
891 
892     private void bulkLoadHFile(
893         TableName tableName,
894         byte[] family,
895         byte[] qualifier,
896         byte[][][] hfileRanges,
897         int numRowsPerRange) throws Exception {
898 
899       Path familyDir = new Path(loadPath, Bytes.toString(family));
900       fs.mkdirs(familyDir);
901       int hfileIdx = 0;
902       for (byte[][] range : hfileRanges) {
903         byte[] from = range[0];
904         byte[] to = range[1];
905         createHFile(new Path(familyDir, "hfile_"+(hfileIdx++)),
906             family, qualifier, from, to, numRowsPerRange);
907       }
908       //set global read so RegionServer can move it
909       setPermission(loadPath, FsPermission.valueOf("-rwxrwxrwx"));
910 
911       HTable table = new HTable(conf, tableName);
912       try {
913         HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
914         TEST_UTIL.waitTableEnabled(admin, tableName.getName());
915         LoadIncrementalHFiles loader = new LoadIncrementalHFiles(conf);
916         loader.doBulkLoad(loadPath, table);
917       } finally {
918         table.close();
919       }
920     }
921 
922     public void setPermission(Path dir, FsPermission perm) throws IOException {
923       if(!fs.getFileStatus(dir).isDir()) {
924         fs.setPermission(dir,perm);
925       }
926       else {
927         for(FileStatus el : fs.listStatus(dir)) {
928           fs.setPermission(el.getPath(), perm);
929           setPermission(el.getPath() , perm);
930         }
931       }
932     }
933   }
934 
935   @Test
936   public void testAppend() throws Exception {
937 
938     PrivilegedExceptionAction appendAction = new PrivilegedExceptionAction() {
939       @Override
940       public Object run() throws Exception {
941         byte[] row = Bytes.toBytes("random_row");
942         byte[] qualifier = Bytes.toBytes("q");
943         Put put = new Put(row);
944         put.add(TEST_FAMILY, qualifier, Bytes.toBytes(1));
945         Append append = new Append(row);
946         append.add(TEST_FAMILY, qualifier, Bytes.toBytes(2));
947         HTable t = new HTable(conf, TEST_TABLE.getTableName());
948         try {
949           t.put(put);
950           t.append(append);
951         } finally {
952           t.close();
953         }
954         return null;
955       }
956     };
957 
958     verifyAllowed(appendAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_RW);
959     verifyDenied(appendAction, USER_RO, USER_NONE);
960   }
961 
962   @Test
963   public void testGrantRevoke() throws Exception {
964 
965     PrivilegedExceptionAction grantAction = new PrivilegedExceptionAction() {
966       @Override
967       public Object run() throws Exception {
968         HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
969         try {
970           BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE.getTableName().getName());
971           AccessControlService.BlockingInterface protocol =
972             AccessControlService.newBlockingStub(service);
973           ProtobufUtil.grant(protocol, USER_RO.getShortName(), TEST_TABLE.getTableName(),
974             TEST_FAMILY, null, Action.READ);
975         } finally {
976           acl.close();
977         }
978         return null;
979       }
980     };
981 
982     PrivilegedExceptionAction revokeAction = new PrivilegedExceptionAction() {
983       @Override
984       public Object run() throws Exception {
985         HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
986         try {
987           BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE.getTableName().getName());
988           AccessControlService.BlockingInterface protocol =
989             AccessControlService.newBlockingStub(service);
990           ProtobufUtil.revoke(protocol, USER_RO.getShortName(), TEST_TABLE.getTableName(),
991             TEST_FAMILY, null, Action.READ);
992         } finally {
993           acl.close();
994         }
995         return null;
996       }
997     };
998 
999     PrivilegedExceptionAction getPermissionsAction = new PrivilegedExceptionAction() {
1000       @Override
1001       public Object run() throws Exception {
1002         HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1003         try {
1004           BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE.getTableName().getName());
1005           AccessControlService.BlockingInterface protocol =
1006             AccessControlService.newBlockingStub(service);
1007           ProtobufUtil.getUserPermissions(protocol, TEST_TABLE.getTableName());
1008         } finally {
1009           acl.close();
1010         }
1011         return null;
1012       }
1013     };
1014 
1015     verifyAllowed(grantAction, SUPERUSER, USER_ADMIN, USER_OWNER);
1016     verifyDenied(grantAction, USER_CREATE, USER_RW, USER_RO, USER_NONE);
1017 
1018     verifyAllowed(revokeAction, SUPERUSER, USER_ADMIN, USER_OWNER);
1019     verifyDenied(revokeAction, USER_CREATE, USER_RW, USER_RO, USER_NONE);
1020 
1021     verifyAllowed(getPermissionsAction, SUPERUSER, USER_ADMIN, USER_OWNER);
1022     verifyDenied(getPermissionsAction, USER_CREATE, USER_RW, USER_RO, USER_NONE);
1023   }
1024 
1025   @Test
1026   public void testPostGrantRevoke() throws Exception {
1027     final TableName tableName =
1028         TableName.valueOf("TempTable");
1029     final byte[] family1 = Bytes.toBytes("f1");
1030     final byte[] family2 = Bytes.toBytes("f2");
1031     final byte[] qualifier = Bytes.toBytes("q");
1032 
1033     // create table
1034     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
1035     if (admin.tableExists(tableName)) {
1036       admin.disableTable(tableName);
1037       admin.deleteTable(tableName);
1038     }
1039     HTableDescriptor htd = new HTableDescriptor(tableName);
1040     htd.addFamily(new HColumnDescriptor(family1));
1041     htd.addFamily(new HColumnDescriptor(family2));
1042     admin.createTable(htd);
1043 
1044     // create temp users
1045     User tblUser = User
1046         .createUserForTesting(TEST_UTIL.getConfiguration(), "tbluser", new String[0]);
1047     User gblUser = User
1048         .createUserForTesting(TEST_UTIL.getConfiguration(), "gbluser", new String[0]);
1049 
1050     // prepare actions:
1051     PrivilegedExceptionAction putActionAll = new PrivilegedExceptionAction() {
1052       @Override
1053       public Object run() throws Exception {
1054         Put p = new Put(Bytes.toBytes("a"));
1055         p.add(family1, qualifier, Bytes.toBytes("v1"));
1056         p.add(family2, qualifier, Bytes.toBytes("v2"));
1057         HTable t = new HTable(conf, tableName);
1058         try {
1059           t.put(p);
1060         } finally {
1061           t.close();
1062         }
1063         return null;
1064       }
1065     };
1066     PrivilegedExceptionAction putAction1 = new PrivilegedExceptionAction() {
1067       @Override
1068       public Object run() throws Exception {
1069         Put p = new Put(Bytes.toBytes("a"));
1070         p.add(family1, qualifier, Bytes.toBytes("v1"));
1071         HTable t = new HTable(conf, tableName);
1072         try {
1073           t.put(p);
1074         } finally {
1075           t.close();
1076         }
1077         return null;
1078       }
1079     };
1080     PrivilegedExceptionAction putAction2 = new PrivilegedExceptionAction() {
1081       @Override
1082       public Object run() throws Exception {
1083         Put p = new Put(Bytes.toBytes("a"));
1084         p.add(family2, qualifier, Bytes.toBytes("v2"));
1085         HTable t = new HTable(conf, tableName);
1086         try {
1087           t.put(p);
1088         } finally {
1089           t.close();
1090         }
1091         return null;
1092       }
1093     };
1094     PrivilegedExceptionAction getActionAll = new PrivilegedExceptionAction() {
1095       @Override
1096       public Object run() throws Exception {
1097         Get g = new Get(Bytes.toBytes("random_row"));
1098         g.addFamily(family1);
1099         g.addFamily(family2);
1100         HTable t = new HTable(conf, tableName);
1101         try {
1102           t.get(g);
1103         } finally {
1104           t.close();
1105         }
1106         return null;
1107       }
1108     };
1109     PrivilegedExceptionAction getAction1 = new PrivilegedExceptionAction() {
1110       @Override
1111       public Object run() throws Exception {
1112         Get g = new Get(Bytes.toBytes("random_row"));
1113         g.addFamily(family1);
1114         HTable t = new HTable(conf, tableName);
1115         try {
1116           t.get(g);
1117         } finally {
1118           t.close();
1119         }
1120         return null;
1121       }
1122     };
1123     PrivilegedExceptionAction getAction2 = new PrivilegedExceptionAction() {
1124       @Override
1125       public Object run() throws Exception {
1126         Get g = new Get(Bytes.toBytes("random_row"));
1127         g.addFamily(family2);
1128         HTable t = new HTable(conf, tableName);
1129         try {
1130           t.get(g);
1131         } finally {
1132           t.close();
1133         }
1134         return null;
1135       }
1136     };
1137     PrivilegedExceptionAction deleteActionAll = new PrivilegedExceptionAction() {
1138       @Override
1139       public Object run() throws Exception {
1140         Delete d = new Delete(Bytes.toBytes("random_row"));
1141         d.deleteFamily(family1);
1142         d.deleteFamily(family2);
1143         HTable t = new HTable(conf, tableName);
1144         try {
1145           t.delete(d);
1146         } finally {
1147           t.close();
1148         }
1149         return null;
1150       }
1151     };
1152     PrivilegedExceptionAction deleteAction1 = new PrivilegedExceptionAction() {
1153       @Override
1154       public Object run() throws Exception {
1155         Delete d = new Delete(Bytes.toBytes("random_row"));
1156         d.deleteFamily(family1);
1157         HTable t = new HTable(conf, tableName);
1158         try {
1159           t.delete(d);
1160         } finally {
1161           t.close();
1162         }
1163         return null;
1164       }
1165     };
1166     PrivilegedExceptionAction deleteAction2 = new PrivilegedExceptionAction() {
1167       @Override
1168       public Object run() throws Exception {
1169         Delete d = new Delete(Bytes.toBytes("random_row"));
1170         d.deleteFamily(family2);
1171         HTable t = new HTable(conf, tableName);
1172         try {
1173           t.delete(d);
1174         } finally {
1175           t.close();
1176         }
1177         return null;
1178       }
1179     };
1180 
1181     // initial check:
1182     verifyDenied(tblUser, getActionAll, getAction1, getAction2);
1183     verifyDenied(tblUser, putActionAll, putAction1, putAction2);
1184     verifyDenied(tblUser, deleteActionAll, deleteAction1, deleteAction2);
1185 
1186     verifyDenied(gblUser, getActionAll, getAction1, getAction2);
1187     verifyDenied(gblUser, putActionAll, putAction1, putAction2);
1188     verifyDenied(gblUser, deleteActionAll, deleteAction1, deleteAction2);
1189 
1190     // grant table read permission
1191     HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1192     try {
1193       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1194       AccessControlService.BlockingInterface protocol =
1195         AccessControlService.newBlockingStub(service);
1196       ProtobufUtil.grant(protocol, tblUser.getShortName(),
1197         tableName, null, null, Permission.Action.READ);
1198       ProtobufUtil.grant(protocol, gblUser.getShortName(),
1199           Permission.Action.READ);
1200     } finally {
1201       acl.close();
1202     }
1203 
1204     Thread.sleep(100);
1205     // check
1206     verifyAllowed(tblUser, getActionAll, getAction1, getAction2);
1207     verifyDenied(tblUser, putActionAll, putAction1, putAction2);
1208     verifyDenied(tblUser, deleteActionAll, deleteAction1, deleteAction2);
1209 
1210     verifyAllowed(gblUser, getActionAll, getAction1, getAction2);
1211     verifyDenied(gblUser, putActionAll, putAction1, putAction2);
1212     verifyDenied(gblUser, deleteActionAll, deleteAction1, deleteAction2);
1213 
1214     // grant table write permission
1215     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1216     try {
1217       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1218       AccessControlService.BlockingInterface protocol =
1219         AccessControlService.newBlockingStub(service);
1220       ProtobufUtil.grant(protocol, tblUser.getShortName(),
1221         tableName, null, null, Permission.Action.WRITE);
1222       ProtobufUtil.grant(protocol, gblUser.getShortName(),
1223           Permission.Action.WRITE);
1224     } finally {
1225       acl.close();
1226     }
1227 
1228     Thread.sleep(100);
1229 
1230     verifyDenied(tblUser, getActionAll, getAction1, getAction2);
1231     verifyAllowed(tblUser, putActionAll, putAction1, putAction2);
1232     verifyAllowed(tblUser, deleteActionAll, deleteAction1, deleteAction2);
1233 
1234     verifyDenied(gblUser, getActionAll, getAction1, getAction2);
1235     verifyAllowed(gblUser, putActionAll, putAction1, putAction2);
1236     verifyAllowed(gblUser, deleteActionAll, deleteAction1, deleteAction2);
1237 
1238     // revoke table permission
1239     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1240     try {
1241       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1242       AccessControlService.BlockingInterface protocol =
1243         AccessControlService.newBlockingStub(service);
1244       ProtobufUtil.grant(protocol, tblUser.getShortName(), tableName, null, null,
1245         Permission.Action.READ, Permission.Action.WRITE);
1246       ProtobufUtil.revoke(protocol, tblUser.getShortName(), tableName, null, null);
1247       ProtobufUtil.revoke(protocol, gblUser.getShortName());
1248     } finally {
1249       acl.close();
1250     }
1251 
1252     Thread.sleep(100);
1253 
1254     verifyDenied(tblUser, getActionAll, getAction1, getAction2);
1255     verifyDenied(tblUser, putActionAll, putAction1, putAction2);
1256     verifyDenied(tblUser, deleteActionAll, deleteAction1, deleteAction2);
1257 
1258     verifyDenied(gblUser, getActionAll, getAction1, getAction2);
1259     verifyDenied(gblUser, putActionAll, putAction1, putAction2);
1260     verifyDenied(gblUser, deleteActionAll, deleteAction1, deleteAction2);
1261 
1262     // grant column family read permission
1263     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1264     try {
1265       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1266       AccessControlService.BlockingInterface protocol =
1267         AccessControlService.newBlockingStub(service);
1268       ProtobufUtil.grant(protocol, tblUser.getShortName(),
1269         tableName, family1, null, Permission.Action.READ);
1270       ProtobufUtil.grant(protocol, gblUser.getShortName(),
1271           Permission.Action.READ);
1272     } finally {
1273       acl.close();
1274     }
1275 
1276     Thread.sleep(100);
1277 
1278     // Access should be denied for family2
1279     verifyAllowed(tblUser, getActionAll, getAction1);
1280     verifyDenied(tblUser, getAction2);
1281     verifyDenied(tblUser, putActionAll, putAction1, putAction2);
1282     verifyDenied(tblUser, deleteActionAll, deleteAction1, deleteAction2);
1283 
1284     verifyAllowed(gblUser, getActionAll, getAction1, getAction2);
1285     verifyDenied(gblUser, putActionAll, putAction1, putAction2);
1286     verifyDenied(gblUser, deleteActionAll, deleteAction1, deleteAction2);
1287 
1288     // grant column family write permission
1289     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1290     try {
1291       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1292       AccessControlService.BlockingInterface protocol =
1293         AccessControlService.newBlockingStub(service);
1294       ProtobufUtil.grant(protocol, tblUser.getShortName(),
1295         tableName, family2, null, Permission.Action.WRITE);
1296       ProtobufUtil.grant(protocol, gblUser.getShortName(),
1297           Permission.Action.WRITE);
1298     } finally {
1299       acl.close();
1300     }
1301 
1302     Thread.sleep(100);
1303 
1304     // READ from family1, WRITE to family2 are allowed
1305     verifyAllowed(tblUser, getActionAll, getAction1);
1306     verifyAllowed(tblUser, putAction2, deleteAction2);
1307     verifyDenied(tblUser, getAction2);
1308     verifyDenied(tblUser, putActionAll, putAction1);
1309     verifyDenied(tblUser, deleteActionAll, deleteAction1);
1310 
1311     verifyDenied(gblUser, getActionAll, getAction1, getAction2);
1312     verifyAllowed(gblUser, putActionAll, putAction1, putAction2);
1313     verifyAllowed(gblUser, deleteActionAll, deleteAction1, deleteAction2);
1314 
1315     // revoke column family permission
1316     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1317     try {
1318       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1319       AccessControlService.BlockingInterface protocol =
1320         AccessControlService.newBlockingStub(service);
1321       ProtobufUtil.revoke(protocol, tblUser.getShortName(), tableName, family2, null);
1322       ProtobufUtil.revoke(protocol, gblUser.getShortName());
1323     } finally {
1324       acl.close();
1325     }
1326 
1327     Thread.sleep(100);
1328 
1329     // Revoke on family2 should not have impact on family1 permissions
1330     verifyAllowed(tblUser, getActionAll, getAction1);
1331     verifyDenied(tblUser, getAction2);
1332     verifyDenied(tblUser, putActionAll, putAction1, putAction2);
1333     verifyDenied(tblUser, deleteActionAll, deleteAction1, deleteAction2);
1334 
1335     // Should not have access as global permissions are completely revoked
1336     verifyDenied(gblUser, getActionAll, getAction1, getAction2);
1337     verifyDenied(gblUser, putActionAll, putAction1, putAction2);
1338     verifyDenied(gblUser, deleteActionAll, deleteAction1, deleteAction2);
1339 
1340     // delete table
1341     admin.disableTable(tableName);
1342     admin.deleteTable(tableName);
1343   }
1344 
1345   private boolean hasFoundUserPermission(UserPermission userPermission, List<UserPermission> perms) {
1346     return perms.contains(userPermission);
1347   }
1348 
1349   @Test
1350   public void testPostGrantRevokeAtQualifierLevel() throws Exception {
1351     final TableName tableName =
1352         TableName.valueOf("testGrantRevokeAtQualifierLevel");
1353     final byte[] family1 = Bytes.toBytes("f1");
1354     final byte[] family2 = Bytes.toBytes("f2");
1355     final byte[] qualifier = Bytes.toBytes("q");
1356 
1357     // create table
1358     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
1359     if (admin.tableExists(tableName)) {
1360       admin.disableTable(tableName);
1361       admin.deleteTable(tableName);
1362     }
1363     HTableDescriptor htd = new HTableDescriptor(tableName);
1364     htd.addFamily(new HColumnDescriptor(family1));
1365     htd.addFamily(new HColumnDescriptor(family2));
1366     admin.createTable(htd);
1367 
1368     // create temp users
1369     User user = User.createUserForTesting(TEST_UTIL.getConfiguration(), "user", new String[0]);
1370 
1371     PrivilegedExceptionAction getQualifierAction = new PrivilegedExceptionAction() {
1372       @Override
1373       public Object run() throws Exception {
1374         Get g = new Get(Bytes.toBytes("random_row"));
1375         g.addColumn(family1, qualifier);
1376         HTable t = new HTable(conf, tableName);
1377         try {
1378           t.get(g);
1379         } finally {
1380           t.close();
1381         }
1382         return null;
1383       }
1384     };
1385     PrivilegedExceptionAction putQualifierAction = new PrivilegedExceptionAction() {
1386       @Override
1387       public Object run() throws Exception {
1388         Put p = new Put(Bytes.toBytes("random_row"));
1389         p.add(family1, qualifier, Bytes.toBytes("v1"));
1390         HTable t = new HTable(conf, tableName);
1391         try {
1392           t.put(p);
1393         } finally {
1394           t.close();
1395         }
1396         return null;
1397       }
1398     };
1399     PrivilegedExceptionAction deleteQualifierAction = new PrivilegedExceptionAction() {
1400       @Override
1401       public Object run() throws Exception {
1402         Delete d = new Delete(Bytes.toBytes("random_row"));
1403         d.deleteColumn(family1, qualifier);
1404         // d.deleteFamily(family1);
1405         HTable t = new HTable(conf, tableName);
1406         try {
1407           t.delete(d);
1408         } finally {
1409           t.close();
1410         }
1411         return null;
1412       }
1413     };
1414 
1415     HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1416     try {
1417       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1418       AccessControlService.BlockingInterface protocol =
1419         AccessControlService.newBlockingStub(service);
1420       ProtobufUtil.revoke(protocol, user.getShortName(), tableName, family1, null);
1421     } finally {
1422       acl.close();
1423     }
1424 
1425     Thread.sleep(100);
1426 
1427     verifyDenied(user, getQualifierAction);
1428     verifyDenied(user, putQualifierAction);
1429     verifyDenied(user, deleteQualifierAction);
1430 
1431     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1432     try {
1433       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1434       AccessControlService.BlockingInterface protocol =
1435         AccessControlService.newBlockingStub(service);
1436       ProtobufUtil.grant(protocol, user.getShortName(),
1437         tableName, family1, qualifier, Permission.Action.READ);
1438     } finally {
1439       acl.close();
1440     }
1441 
1442     Thread.sleep(100);
1443 
1444     verifyAllowed(user, getQualifierAction);
1445     verifyDenied(user, putQualifierAction);
1446     verifyDenied(user, deleteQualifierAction);
1447 
1448     // only grant write permission
1449     // TODO: comment this portion after HBASE-3583
1450     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1451     try {
1452       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1453       AccessControlService.BlockingInterface protocol =
1454         AccessControlService.newBlockingStub(service);
1455       ProtobufUtil.grant(protocol, user.getShortName(),
1456         tableName, family1, qualifier, Permission.Action.WRITE);
1457     } finally {
1458       acl.close();
1459     }
1460 
1461     Thread.sleep(100);
1462 
1463     verifyDenied(user, getQualifierAction);
1464     verifyAllowed(user, putQualifierAction);
1465     verifyAllowed(user, deleteQualifierAction);
1466 
1467     // grant both read and write permission.
1468     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1469     try {
1470       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1471       AccessControlService.BlockingInterface protocol =
1472         AccessControlService.newBlockingStub(service);
1473       ProtobufUtil.grant(protocol, user.getShortName(),
1474         tableName, family1, qualifier,
1475           Permission.Action.READ, Permission.Action.WRITE);
1476     } finally {
1477       acl.close();
1478     }
1479 
1480     Thread.sleep(100);
1481 
1482     verifyAllowed(user, getQualifierAction);
1483     verifyAllowed(user, putQualifierAction);
1484     verifyAllowed(user, deleteQualifierAction);
1485 
1486     // revoke family level permission won't impact column level.
1487     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1488     try {
1489       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1490       AccessControlService.BlockingInterface protocol =
1491         AccessControlService.newBlockingStub(service);
1492       ProtobufUtil.revoke(protocol, user.getShortName(),
1493         tableName, family1, qualifier);
1494     } finally {
1495       acl.close();
1496     }
1497 
1498     Thread.sleep(100);
1499 
1500     verifyDenied(user, getQualifierAction);
1501     verifyDenied(user, putQualifierAction);
1502     verifyDenied(user, deleteQualifierAction);
1503 
1504     // delete table
1505     admin.disableTable(tableName);
1506     admin.deleteTable(tableName);
1507   }
1508 
1509   @Test
1510   public void testPermissionList() throws Exception {
1511     final TableName tableName =
1512         TableName.valueOf("testPermissionList");
1513     final byte[] family1 = Bytes.toBytes("f1");
1514     final byte[] family2 = Bytes.toBytes("f2");
1515     final byte[] qualifier = Bytes.toBytes("q");
1516 
1517     // create table
1518     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
1519     if (admin.tableExists(tableName)) {
1520       admin.disableTable(tableName);
1521       admin.deleteTable(tableName);
1522     }
1523     HTableDescriptor htd = new HTableDescriptor(tableName);
1524     htd.addFamily(new HColumnDescriptor(family1));
1525     htd.addFamily(new HColumnDescriptor(family2));
1526     htd.setOwner(USER_OWNER);
1527     admin.createTable(htd);
1528 
1529     List<UserPermission> perms;
1530 
1531     HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1532     try {
1533       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1534       AccessControlService.BlockingInterface protocol =
1535         AccessControlService.newBlockingStub(service);
1536       perms = ProtobufUtil.getUserPermissions(protocol, tableName);
1537     } finally {
1538       acl.close();
1539     }
1540 
1541     UserPermission ownerperm = new UserPermission(
1542       Bytes.toBytes(USER_OWNER.getName()), tableName, null, Action.values());
1543     assertTrue("Owner should have all permissions on table",
1544       hasFoundUserPermission(ownerperm, perms));
1545 
1546     User user = User.createUserForTesting(TEST_UTIL.getConfiguration(), "user", new String[0]);
1547     byte[] userName = Bytes.toBytes(user.getShortName());
1548 
1549     UserPermission up = new UserPermission(userName,
1550       tableName, family1, qualifier, Permission.Action.READ);
1551     assertFalse("User should not be granted permission: " + up.toString(),
1552       hasFoundUserPermission(up, perms));
1553 
1554     // grant read permission
1555     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1556     try {
1557       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1558       AccessControlService.BlockingInterface protocol =
1559         AccessControlService.newBlockingStub(service);
1560       ProtobufUtil.grant(protocol, user.getShortName(),
1561         tableName, family1, qualifier, Permission.Action.READ);
1562       perms = ProtobufUtil.getUserPermissions(protocol, tableName);
1563     } finally {
1564       acl.close();
1565     }
1566 
1567     UserPermission upToVerify = new UserPermission(
1568       userName, tableName, family1, qualifier, Permission.Action.READ);
1569     assertTrue("User should be granted permission: " + upToVerify.toString(),
1570       hasFoundUserPermission(upToVerify, perms));
1571 
1572     upToVerify = new UserPermission(
1573       userName, tableName, family1, qualifier, Permission.Action.WRITE);
1574     assertFalse("User should not be granted permission: " + upToVerify.toString(),
1575       hasFoundUserPermission(upToVerify, perms));
1576 
1577     // grant read+write
1578     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1579     try {
1580       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1581       AccessControlService.BlockingInterface protocol =
1582         AccessControlService.newBlockingStub(service);
1583       ProtobufUtil.grant(protocol, user.getShortName(),
1584         tableName, family1, qualifier,
1585           Permission.Action.WRITE, Permission.Action.READ);
1586       perms = ProtobufUtil.getUserPermissions(protocol, tableName);
1587     } finally {
1588       acl.close();
1589     }
1590 
1591     upToVerify = new UserPermission(userName, tableName, family1,
1592       qualifier, Permission.Action.WRITE, Permission.Action.READ);
1593     assertTrue("User should be granted permission: " + upToVerify.toString(),
1594       hasFoundUserPermission(upToVerify, perms));
1595 
1596     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1597     try {
1598       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1599       AccessControlService.BlockingInterface protocol =
1600         AccessControlService.newBlockingStub(service);
1601       ProtobufUtil.revoke(protocol, user.getShortName(), tableName, family1, qualifier,
1602         Permission.Action.WRITE, Permission.Action.READ);
1603       perms = ProtobufUtil.getUserPermissions(protocol, tableName);
1604     } finally {
1605       acl.close();
1606     }
1607 
1608     assertFalse("User should not be granted permission: " + upToVerify.toString(),
1609       hasFoundUserPermission(upToVerify, perms));
1610 
1611     // disable table before modification
1612     admin.disableTable(tableName);
1613 
1614     User newOwner = User.createUserForTesting(conf, "new_owner", new String[] {});
1615     htd.setOwner(newOwner);
1616     admin.modifyTable(tableName, htd);
1617 
1618     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1619     try {
1620       BlockingRpcChannel service = acl.coprocessorService(tableName.getName());
1621       AccessControlService.BlockingInterface protocol =
1622         AccessControlService.newBlockingStub(service);
1623       perms = ProtobufUtil.getUserPermissions(protocol, tableName);
1624     } finally {
1625       acl.close();
1626     }
1627 
1628     UserPermission newOwnerperm = new UserPermission(
1629       Bytes.toBytes(newOwner.getName()), tableName, null, Action.values());
1630     assertTrue("New owner should have all permissions on table",
1631       hasFoundUserPermission(newOwnerperm, perms));
1632 
1633     // delete table
1634     admin.deleteTable(tableName);
1635   }
1636 
1637   @Test
1638   public void testGlobalPermissionList() throws Exception {
1639     List<UserPermission> perms;
1640     HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1641     try {
1642       BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
1643       AccessControlService.BlockingInterface protocol =
1644         AccessControlService.newBlockingStub(service);
1645       perms = ProtobufUtil.getUserPermissions(protocol);
1646     } finally {
1647       acl.close();
1648     }
1649     UserPermission adminPerm = new UserPermission(Bytes.toBytes(USER_ADMIN.getShortName()),
1650       AccessControlLists.ACL_TABLE_NAME, null, null, Bytes.toBytes("ACRW"));
1651     assertTrue("Only user admin has permission on table _acl_ per setup",
1652       perms.size() == 1 && hasFoundUserPermission(adminPerm, perms));
1653   }
1654 
1655   /** global operations */
1656   private void verifyGlobal(PrivilegedExceptionAction<?> action) throws Exception {
1657     verifyAllowed(action, SUPERUSER);
1658 
1659     verifyDenied(action, USER_CREATE, USER_RW, USER_NONE, USER_RO);
1660   }
1661 
1662   public void checkGlobalPerms(Permission.Action... actions) throws IOException {
1663     Permission[] perms = new Permission[actions.length];
1664     for (int i = 0; i < actions.length; i++) {
1665       perms[i] = new Permission(actions[i]);
1666     }
1667     CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder();
1668     for (Action a : actions) {
1669       request.addPermission(AccessControlProtos.Permission.newBuilder()
1670           .setType(AccessControlProtos.Permission.Type.Global)
1671           .setGlobalPermission(
1672               AccessControlProtos.GlobalPermission.newBuilder()
1673                   .addAction(ProtobufUtil.toPermissionAction(a)).build()));
1674     }
1675     HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1676     try {
1677       BlockingRpcChannel channel = acl.coprocessorService(new byte[0]);
1678       AccessControlService.BlockingInterface protocol =
1679         AccessControlService.newBlockingStub(channel);
1680       try {
1681         protocol.checkPermissions(null, request.build());
1682       } catch (ServiceException se) {
1683         ProtobufUtil.toIOException(se);
1684       }
1685     } finally {
1686       acl.close();
1687     }
1688   }
1689 
1690   public void checkTablePerms(TableName table, byte[] family, byte[] column,
1691       Permission.Action... actions) throws IOException {
1692     Permission[] perms = new Permission[actions.length];
1693     for (int i = 0; i < actions.length; i++) {
1694       perms[i] = new TablePermission(table, family, column, actions[i]);
1695     }
1696 
1697     checkTablePerms(table, perms);
1698   }
1699 
1700   public void checkTablePerms(TableName table, Permission... perms) throws IOException {
1701     CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder();
1702     for (Permission p : perms) {
1703       request.addPermission(ProtobufUtil.toPermission(p));
1704     }
1705     HTable acl = new HTable(conf, table);
1706     try {
1707       AccessControlService.BlockingInterface protocol =
1708         AccessControlService.newBlockingStub(acl.coprocessorService(new byte[0]));
1709       try {
1710         protocol.checkPermissions(null, request.build());
1711       } catch (ServiceException se) {
1712         ProtobufUtil.toIOException(se);
1713       }
1714     } finally {
1715       acl.close();
1716     }
1717   }
1718 
1719   @Test
1720   public void testCheckPermissions() throws Exception {
1721     // --------------------------------------
1722     // test global permissions
1723     PrivilegedExceptionAction<Void> globalAdmin = new PrivilegedExceptionAction<Void>() {
1724       @Override
1725       public Void run() throws Exception {
1726         checkGlobalPerms(Permission.Action.ADMIN);
1727         return null;
1728       }
1729     };
1730     // verify that only superuser can admin
1731     verifyGlobal(globalAdmin);
1732 
1733     // --------------------------------------
1734     // test multiple permissions
1735     PrivilegedExceptionAction<Void> globalReadWrite = new PrivilegedExceptionAction<Void>() {
1736       @Override
1737       public Void run() throws Exception {
1738         checkGlobalPerms(Permission.Action.READ, Permission.Action.WRITE);
1739         return null;
1740       }
1741     };
1742 
1743     verifyGlobal(globalReadWrite);
1744 
1745     // --------------------------------------
1746     // table/column/qualifier level permissions
1747     final byte[] TEST_Q1 = Bytes.toBytes("q1");
1748     final byte[] TEST_Q2 = Bytes.toBytes("q2");
1749 
1750     User userTable = User.createUserForTesting(conf, "user_check_perms_table", new String[0]);
1751     User userColumn = User.createUserForTesting(conf, "user_check_perms_family", new String[0]);
1752     User userQualifier = User.createUserForTesting(conf, "user_check_perms_q", new String[0]);
1753 
1754     HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1755     try {
1756       BlockingRpcChannel channel = acl.coprocessorService(new byte[0]);
1757       AccessControlService.BlockingInterface protocol =
1758         AccessControlService.newBlockingStub(channel);
1759       ProtobufUtil.grant(protocol, userTable.getShortName(),
1760         TEST_TABLE.getTableName(), null, null, Permission.Action.READ);
1761       ProtobufUtil.grant(protocol, userColumn.getShortName(),
1762         TEST_TABLE.getTableName(), TEST_FAMILY, null, Permission.Action.READ);
1763       ProtobufUtil.grant(protocol, userQualifier.getShortName(),
1764         TEST_TABLE.getTableName(), TEST_FAMILY, TEST_Q1, Permission.Action.READ);
1765     } finally {
1766       acl.close();
1767     }
1768 
1769     PrivilegedExceptionAction<Void> tableRead = new PrivilegedExceptionAction<Void>() {
1770       @Override
1771       public Void run() throws Exception {
1772         checkTablePerms(TEST_TABLE.getTableName(), null, null, Permission.Action.READ);
1773         return null;
1774       }
1775     };
1776 
1777     PrivilegedExceptionAction<Void> columnRead = new PrivilegedExceptionAction<Void>() {
1778       @Override
1779       public Void run() throws Exception {
1780         checkTablePerms(TEST_TABLE.getTableName(), TEST_FAMILY, null, Permission.Action.READ);
1781         return null;
1782       }
1783     };
1784 
1785     PrivilegedExceptionAction<Void> qualifierRead = new PrivilegedExceptionAction<Void>() {
1786       @Override
1787       public Void run() throws Exception {
1788         checkTablePerms(TEST_TABLE.getTableName(), TEST_FAMILY, TEST_Q1, Permission.Action.READ);
1789         return null;
1790       }
1791     };
1792 
1793     PrivilegedExceptionAction<Void> multiQualifierRead = new PrivilegedExceptionAction<Void>() {
1794       @Override
1795       public Void run() throws Exception {
1796         checkTablePerms(TEST_TABLE.getTableName(), new Permission[] {
1797             new TablePermission(TEST_TABLE.getTableName(), TEST_FAMILY, TEST_Q1, Permission.Action.READ),
1798             new TablePermission(TEST_TABLE.getTableName(), TEST_FAMILY, TEST_Q2, Permission.Action.READ), });
1799         return null;
1800       }
1801     };
1802 
1803     PrivilegedExceptionAction<Void> globalAndTableRead = new PrivilegedExceptionAction<Void>() {
1804       @Override
1805       public Void run() throws Exception {
1806         checkTablePerms(TEST_TABLE.getTableName(), new Permission[] { new Permission(Permission.Action.READ),
1807             new TablePermission(TEST_TABLE.getTableName(), null, (byte[]) null, Permission.Action.READ), });
1808         return null;
1809       }
1810     };
1811 
1812     PrivilegedExceptionAction<Void> noCheck = new PrivilegedExceptionAction<Void>() {
1813       @Override
1814       public Void run() throws Exception {
1815         checkTablePerms(TEST_TABLE.getTableName(), new Permission[0]);
1816         return null;
1817       }
1818     };
1819 
1820     verifyAllowed(tableRead, SUPERUSER, userTable);
1821     verifyDenied(tableRead, userColumn, userQualifier);
1822 
1823     verifyAllowed(columnRead, SUPERUSER, userTable, userColumn);
1824     verifyDenied(columnRead, userQualifier);
1825 
1826     verifyAllowed(qualifierRead, SUPERUSER, userTable, userColumn, userQualifier);
1827 
1828     verifyAllowed(multiQualifierRead, SUPERUSER, userTable, userColumn);
1829     verifyDenied(multiQualifierRead, userQualifier);
1830 
1831     verifyAllowed(globalAndTableRead, SUPERUSER);
1832     verifyDenied(globalAndTableRead, userTable, userColumn, userQualifier);
1833 
1834     verifyAllowed(noCheck, SUPERUSER, userTable, userColumn, userQualifier);
1835 
1836     // --------------------------------------
1837     // test family level multiple permissions
1838     PrivilegedExceptionAction<Void> familyReadWrite = new PrivilegedExceptionAction<Void>() {
1839       @Override
1840       public Void run() throws Exception {
1841         checkTablePerms(TEST_TABLE.getTableName(), TEST_FAMILY, null, Permission.Action.READ,
1842           Permission.Action.WRITE);
1843         return null;
1844       }
1845     };
1846 
1847     verifyAllowed(familyReadWrite, SUPERUSER, USER_OWNER, USER_CREATE, USER_RW);
1848     verifyDenied(familyReadWrite, USER_NONE, USER_RO);
1849 
1850     // --------------------------------------
1851     // check for wrong table region
1852     CheckPermissionsRequest checkRequest = CheckPermissionsRequest.newBuilder()
1853       .addPermission(AccessControlProtos.Permission.newBuilder()
1854           .setType(AccessControlProtos.Permission.Type.Table)
1855           .setTablePermission(
1856               AccessControlProtos.TablePermission.newBuilder()
1857                   .setTableName(ProtobufUtil.toProtoTableName(TEST_TABLE.getTableName()))
1858                   .addAction(AccessControlProtos.Permission.Action.CREATE))
1859       ).build();
1860     acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1861     try {
1862       BlockingRpcChannel channel = acl.coprocessorService(new byte[0]);
1863       AccessControlService.BlockingInterface protocol =
1864         AccessControlService.newBlockingStub(channel);
1865       try {
1866         // but ask for TablePermissions for TEST_TABLE
1867         protocol.checkPermissions(null, checkRequest);
1868         fail("this should have thrown CoprocessorException");
1869       } catch (ServiceException ex) {
1870         // expected
1871       }
1872     } finally {
1873       acl.close();
1874     }
1875   }
1876 
1877   @Test
1878   public void testStopRegionServer() throws Exception {
1879     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
1880       @Override
1881       public Object run() throws Exception {
1882         ACCESS_CONTROLLER.preStopRegionServer(ObserverContext.createAndPrepare(RSCP_ENV, null));
1883         return null;
1884       }
1885     };
1886 
1887     verifyAllowed(action, SUPERUSER, USER_ADMIN);
1888     verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE);
1889   }
1890 
1891   @Test
1892   public void testOpenRegion() throws Exception {
1893     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
1894       @Override
1895       public Object run() throws Exception {
1896         ACCESS_CONTROLLER.preOpen(ObserverContext.createAndPrepare(RCP_ENV, null));
1897         return null;
1898       }
1899     };
1900 
1901     verifyAllowed(action, SUPERUSER, USER_ADMIN);
1902     verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER);
1903   }
1904 
1905   @Test
1906   public void testCloseRegion() throws Exception {
1907     PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
1908       @Override
1909       public Object run() throws Exception {
1910         ACCESS_CONTROLLER.preClose(ObserverContext.createAndPrepare(RCP_ENV, null), false);
1911         return null;
1912       }
1913     };
1914 
1915     verifyAllowed(action, SUPERUSER, USER_ADMIN);
1916     verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER);
1917   }
1918 
1919   @Test
1920   public void testSnapshot() throws Exception {
1921     PrivilegedExceptionAction snapshotAction = new PrivilegedExceptionAction() {
1922       @Override
1923       public Object run() throws Exception {
1924         ACCESS_CONTROLLER.preSnapshot(ObserverContext.createAndPrepare(CP_ENV, null),
1925           null, null);
1926         return null;
1927       }
1928     };
1929 
1930     PrivilegedExceptionAction deleteAction = new PrivilegedExceptionAction() {
1931       @Override
1932       public Object run() throws Exception {
1933         ACCESS_CONTROLLER.preDeleteSnapshot(ObserverContext.createAndPrepare(CP_ENV, null),
1934           null);
1935         return null;
1936       }
1937     };
1938 
1939     PrivilegedExceptionAction restoreAction = new PrivilegedExceptionAction() {
1940       @Override
1941       public Object run() throws Exception {
1942         ACCESS_CONTROLLER.preRestoreSnapshot(ObserverContext.createAndPrepare(CP_ENV, null),
1943           null, null);
1944         return null;
1945       }
1946     };
1947 
1948     PrivilegedExceptionAction cloneAction = new PrivilegedExceptionAction() {
1949       @Override
1950       public Object run() throws Exception {
1951         ACCESS_CONTROLLER.preCloneSnapshot(ObserverContext.createAndPrepare(CP_ENV, null),
1952           null, null);
1953         return null;
1954       }
1955     };
1956 
1957     verifyAllowed(snapshotAction, SUPERUSER, USER_ADMIN);
1958     verifyDenied(snapshotAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER);
1959 
1960     verifyAllowed(cloneAction, SUPERUSER, USER_ADMIN);
1961     verifyDenied(deleteAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER);
1962 
1963     verifyAllowed(restoreAction, SUPERUSER, USER_ADMIN);
1964     verifyDenied(restoreAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER);
1965 
1966     verifyAllowed(deleteAction, SUPERUSER, USER_ADMIN);
1967     verifyDenied(cloneAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER);
1968   }
1969 
1970   @Test
1971   public void testGlobalAuthorizationForNewRegisteredRS() throws Exception {
1972     LOG.debug("Test for global authorization for a new registered RegionServer.");
1973     MiniHBaseCluster hbaseCluster = TEST_UTIL.getHBaseCluster();
1974 
1975     // Since each RegionServer running on different user, add global
1976     // permissions for the new user.
1977     HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
1978     try {
1979       BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE.getTableName().getName());
1980       AccessControlService.BlockingInterface protocol =
1981         AccessControlService.newBlockingStub(service);
1982       String currentUser = User.getCurrent().getShortName();
1983       // User name for the new RegionServer we plan to add.
1984       String activeUserForNewRs = currentUser + ".hfs."
1985           + hbaseCluster.getLiveRegionServerThreads().size();
1986       ProtobufUtil.grant(protocol, activeUserForNewRs,
1987         Permission.Action.ADMIN, Permission.Action.CREATE,
1988         Permission.Action.READ, Permission.Action.WRITE);
1989     } finally {
1990       acl.close();
1991     }
1992     final HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
1993     HTableDescriptor htd = new HTableDescriptor(TEST_TABLE2);
1994     htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
1995     admin.createTable(htd);
1996 
1997     // Starting a new RegionServer.
1998     JVMClusterUtil.RegionServerThread newRsThread = hbaseCluster
1999         .startRegionServer();
2000     final HRegionServer newRs = newRsThread.getRegionServer();
2001 
2002     // Move region to the new RegionServer.
2003     final HTable table = new HTable(TEST_UTIL.getConfiguration(), TEST_TABLE2);
2004     try {
2005       NavigableMap<HRegionInfo, ServerName> regions = table
2006           .getRegionLocations();
2007       final Map.Entry<HRegionInfo, ServerName> firstRegion = regions.entrySet()
2008           .iterator().next();
2009 
2010       PrivilegedExceptionAction moveAction = new PrivilegedExceptionAction() {
2011         @Override
2012         public Object run() throws Exception {
2013           admin.move(firstRegion.getKey().getEncodedNameAsBytes(),
2014               Bytes.toBytes(newRs.getServerName().getServerName()));
2015           return null;
2016         }
2017       };
2018       SUPERUSER.runAs(moveAction);
2019 
2020       final int RETRIES_LIMIT = 10;
2021       int retries = 0;
2022       while (newRs.getOnlineRegions(TEST_TABLE2).size() < 1 && retries < RETRIES_LIMIT) {
2023         LOG.debug("Waiting for region to be opened. Already retried " + retries
2024             + " times.");
2025         try {
2026           Thread.sleep(1000);
2027         } catch (InterruptedException e) {
2028         }
2029         retries++;
2030         if (retries == RETRIES_LIMIT - 1) {
2031           fail("Retry exhaust for waiting region to be opened.");
2032         }
2033       }
2034       // Verify write permission for user "admin2" who has the global
2035       // permissions.
2036       PrivilegedExceptionAction putAction = new PrivilegedExceptionAction() {
2037         @Override
2038         public Object run() throws Exception {
2039           Put put = new Put(Bytes.toBytes("test"));
2040           put.add(TEST_FAMILY, Bytes.toBytes("qual"), Bytes.toBytes("value"));
2041           table.put(put);
2042           return null;
2043         }
2044       };
2045       USER_ADMIN.runAs(putAction);
2046     } finally {
2047       table.close();
2048     }
2049   }
2050 
2051   @Test
2052   public void testTableDescriptorsEnumeration() throws Exception {
2053     User TABLE_ADMIN = User.createUserForTesting(conf, "UserA", new String[0]);
2054 
2055     // Grant TABLE ADMIN privs
2056     HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
2057     try {
2058       BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE.getTableName().getName());
2059       AccessControlService.BlockingInterface protocol =
2060         AccessControlService.newBlockingStub(service);
2061       ProtobufUtil.grant(protocol, TABLE_ADMIN.getShortName(), TEST_TABLE.getTableName(),
2062         null, null, Permission.Action.ADMIN);
2063     } finally {
2064       acl.close();
2065     }
2066 
2067     PrivilegedExceptionAction listTablesAction = new PrivilegedExceptionAction() {
2068       @Override
2069       public Object run() throws Exception {
2070         HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
2071         try {
2072           admin.listTables();
2073         } finally {
2074           admin.close();
2075         }
2076         return null;
2077       }
2078     };
2079 
2080     PrivilegedExceptionAction getTableDescAction = new PrivilegedExceptionAction() {
2081       @Override
2082       public Object run() throws Exception {
2083         HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
2084         try {
2085           admin.getTableDescriptor(TEST_TABLE.getTableName());
2086         } finally {
2087           admin.close();
2088         }
2089         return null;
2090       }
2091     };
2092 
2093     verifyAllowed(listTablesAction, SUPERUSER, USER_ADMIN);
2094     verifyDenied(listTablesAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, TABLE_ADMIN);
2095 
2096     verifyAllowed(getTableDescAction, SUPERUSER, USER_ADMIN, USER_CREATE, TABLE_ADMIN);
2097     verifyDenied(getTableDescAction, USER_RW, USER_RO, USER_NONE);
2098   }
2099 
2100   @Test
2101   public void testTableDeletion() throws Exception {
2102     User TABLE_ADMIN = User.createUserForTesting(conf, "TestUser", new String[0]);
2103 
2104     // Grant TABLE ADMIN privs
2105     HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
2106     try {
2107       BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE.getTableName().getName());
2108       AccessControlService.BlockingInterface protocol =
2109         AccessControlService.newBlockingStub(service);
2110       ProtobufUtil.grant(protocol, TABLE_ADMIN.getShortName(), TEST_TABLE.getTableName(),
2111         null, null, Permission.Action.ADMIN);
2112     } finally {
2113       acl.close();
2114     }
2115 
2116     PrivilegedExceptionAction deleteTableAction = new PrivilegedExceptionAction() {
2117       @Override
2118       public Object run() throws Exception {
2119         HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
2120         try {
2121           admin.disableTable(TEST_TABLE.getTableName());
2122           admin.deleteTable(TEST_TABLE.getTableName());
2123         } finally {
2124           admin.close();
2125         }
2126         return null;
2127       }
2128     };
2129 
2130     verifyDenied(deleteTableAction, USER_RW, USER_RO, USER_NONE);
2131     verifyAllowed(deleteTableAction, TABLE_ADMIN);
2132   }
2133 
2134 }