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  package org.apache.hadoop.hbase.security.access;
19  
20  import static org.junit.Assert.*;
21  
22  import java.util.Arrays;
23  import java.util.List;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.conf.Configuration;
28  import org.apache.hadoop.hbase.HBaseTestingUtility;
29  import org.apache.hadoop.hbase.HColumnDescriptor;
30  import org.apache.hadoop.hbase.HTableDescriptor;
31  import org.apache.hadoop.hbase.NamespaceDescriptor;
32  import org.apache.hadoop.hbase.TableName;
33  import org.apache.hadoop.hbase.TableNotFoundException;
34  import org.apache.hadoop.hbase.client.HBaseAdmin;
35  import org.apache.hadoop.hbase.client.HTable;
36  import org.apache.hadoop.hbase.client.Put;
37  import org.apache.hadoop.hbase.client.Result;
38  import org.apache.hadoop.hbase.client.ResultScanner;
39  import org.apache.hadoop.hbase.client.Scan;
40  import org.apache.hadoop.hbase.security.User;
41  import org.apache.hadoop.hbase.security.access.Permission.Action;
42  import org.apache.hadoop.hbase.testclassification.LargeTests;
43  import org.apache.hadoop.hbase.util.Bytes;
44  import org.apache.hadoop.hbase.util.TestTableName;
45  import org.apache.hadoop.hbase.zookeeper.ZKUtil;
46  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
47  import org.junit.After;
48  import org.junit.AfterClass;
49  import org.junit.Before;
50  import org.junit.BeforeClass;
51  import org.junit.Rule;
52  import org.junit.Test;
53  import org.junit.experimental.categories.Category;
54  
55  @Category(LargeTests.class)
56  public class TestAccessController2 extends SecureTestUtil {
57    private static final Log LOG = LogFactory.getLog(TestAccessController2.class);
58  
59    private static final byte[] TEST_ROW = Bytes.toBytes("test");
60    private static final byte[] TEST_FAMILY = Bytes.toBytes("f");
61    private static final byte[] TEST_QUALIFIER = Bytes.toBytes("q");
62    private static final byte[] TEST_VALUE = Bytes.toBytes("value");
63  
64    private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
65    private static Configuration conf;
66  
67    private final static byte[] Q1 = Bytes.toBytes("q1");
68    private final static byte[] value1 = Bytes.toBytes("value1");
69  
70    private static byte[] TEST_FAMILY_2 = Bytes.toBytes("f2");
71    private static byte[] TEST_ROW_2 = Bytes.toBytes("r2");
72    private final static byte[] Q2 = Bytes.toBytes("q2");
73    private final static byte[] value2 = Bytes.toBytes("value2");
74  
75    private static byte[] TEST_ROW_3 = Bytes.toBytes("r3");
76  
77    private static final String TESTGROUP_1 = "testgroup_1";
78    private static final String TESTGROUP_2 = "testgroup_2";
79  
80    private static User TESTGROUP1_USER1;
81    private static User TESTGROUP2_USER1;
82  
83    @Rule
84    public TestTableName TEST_TABLE = new TestTableName();
85    private String namespace = "testNamespace";
86    private String tname = namespace + ":testtable1";
87    private byte[] tableName = Bytes.toBytes(tname);
88    private static String TESTGROUP_1_NAME;
89  
90    @BeforeClass
91    public static void setupBeforeClass() throws Exception {
92      conf = TEST_UTIL.getConfiguration();
93      // Enable security
94      enableSecurity(conf);
95      // Verify enableSecurity sets up what we require
96      verifyConfiguration(conf);
97      TEST_UTIL.startMiniCluster();
98      // Wait for the ACL table to become available
99      TEST_UTIL.waitUntilAllRegionsAssigned(AccessControlLists.ACL_TABLE_NAME);
100 
101     TESTGROUP_1_NAME = convertToGroup(TESTGROUP_1);
102     TESTGROUP1_USER1 =
103         User.createUserForTesting(conf, "testgroup1_user1", new String[] { TESTGROUP_1 });
104     TESTGROUP2_USER1 =
105         User.createUserForTesting(conf, "testgroup2_user2", new String[] { TESTGROUP_2 });
106   }
107 
108   @Before
109   public void setUp() throws Exception {
110     TEST_UTIL.getHBaseAdmin().createNamespace(NamespaceDescriptor.create(namespace).build());
111     HTable table = null;
112     try {
113       table =
114           TEST_UTIL.createTable(TableName.valueOf(tableName),
115             new String[] { Bytes.toString(TEST_FAMILY), Bytes.toString(TEST_FAMILY_2) });
116 
117       // Ingesting test data.
118       table.put(Arrays.asList(new Put(TEST_ROW).add(TEST_FAMILY, Q1, value1),
119           new Put(TEST_ROW_2).add(TEST_FAMILY, Q2, value2),
120           new Put(TEST_ROW_3).add(TEST_FAMILY_2, Q1, value1)));
121     } finally {
122       table.close();
123     }
124 
125     assertEquals(1, AccessControlLists.getTablePermissions(conf, TableName.valueOf(tableName))
126         .size());
127     try {
128       assertEquals(1, AccessControlClient.getUserPermissions(conf, tableName.toString()).size());
129     } catch (Throwable e) {
130       LOG.error("Error during call of AccessControlClient.getUserPermissions. ", e);
131     }
132   }
133 
134   @AfterClass
135   public static void tearDownAfterClass() throws Exception {
136     TEST_UTIL.shutdownMiniCluster();
137   }
138 
139   @After
140   public void tearDown() throws Exception {
141     // Clean the _acl_ table
142     try {
143       TEST_UTIL.deleteTable(tableName);
144     } catch (TableNotFoundException ex) {
145       // Test deleted the table, no problem
146       LOG.info("Test deleted table " + tableName);
147     }
148     TEST_UTIL.getHBaseAdmin().deleteNamespace(namespace);
149     // Verify all table/namespace permissions are erased
150     assertEquals(0, AccessControlLists.getTablePermissions(conf, TableName.valueOf(tableName))
151         .size());
152     assertEquals(0, AccessControlLists.getNamespacePermissions(conf, namespace).size());
153   }
154 
155   @Test
156   public void testCreateWithCorrectOwner() throws Exception {
157     // Create a test user
158     User testUser =
159         User.createUserForTesting(TEST_UTIL.getConfiguration(), "TestUser", new String[0]);
160     // Grant the test user the ability to create tables
161     SecureTestUtil.grantGlobal(TEST_UTIL, testUser.getShortName(), Action.CREATE);
162     verifyAllowed(new AccessTestAction() {
163       @Override
164       public Object run() throws Exception {
165         HTableDescriptor desc = new HTableDescriptor(TEST_TABLE.getTableName());
166         desc.addFamily(new HColumnDescriptor(TEST_FAMILY));
167         HBaseAdmin admin = new HBaseAdmin(conf);
168         try {
169           admin.createTable(desc);
170         } finally {
171           admin.close();
172         }
173         return null;
174       }
175     }, testUser);
176     TEST_UTIL.waitTableEnabled(TEST_TABLE.getTableName().getName());
177     // Verify that owner permissions have been granted to the test user on the
178     // table just created
179     List<TablePermission> perms =
180         AccessControlLists.getTablePermissions(conf, TEST_TABLE.getTableName()).get(
181           testUser.getShortName());
182     assertNotNull(perms);
183     assertFalse(perms.isEmpty());
184     // Should be RWXCA
185     assertTrue(perms.get(0).implies(Permission.Action.READ));
186     assertTrue(perms.get(0).implies(Permission.Action.WRITE));
187     assertTrue(perms.get(0).implies(Permission.Action.EXEC));
188     assertTrue(perms.get(0).implies(Permission.Action.CREATE));
189     assertTrue(perms.get(0).implies(Permission.Action.ADMIN));
190   }
191 
192   @Test
193   public void testCreateTableWithGroupPermissions() throws Exception {
194     grantGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.CREATE);
195     try {
196       AccessTestAction createAction = new AccessTestAction() {
197         @Override
198         public Object run() throws Exception {
199           HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
200           HTableDescriptor desc = new HTableDescriptor(TEST_TABLE.getTableName());
201           desc.addFamily(new HColumnDescriptor(TEST_FAMILY));
202           try {
203             admin.createTable(desc);
204           } finally {
205             admin.close();
206           }
207           return null;
208         }
209       };
210       verifyAllowed(createAction, TESTGROUP1_USER1);
211       verifyDenied(createAction, TESTGROUP2_USER1);
212     } finally {
213       revokeGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.CREATE);
214     }
215   }
216 
217   @Test
218   public void testACLTableAccess() throws Exception {
219     final Configuration conf = TEST_UTIL.getConfiguration();
220 
221     // Superuser
222     User superUser = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
223 
224     // Global users
225     User globalRead = User.createUserForTesting(conf, "globalRead", new String[0]);
226     User globalWrite = User.createUserForTesting(conf, "globalWrite", new String[0]);
227     User globalCreate = User.createUserForTesting(conf, "globalCreate", new String[0]);
228     User globalAdmin = User.createUserForTesting(conf, "globalAdmin", new String[0]);
229     SecureTestUtil.grantGlobal(TEST_UTIL, globalRead.getShortName(), Action.READ);
230     SecureTestUtil.grantGlobal(TEST_UTIL, globalWrite.getShortName(), Action.WRITE);
231     SecureTestUtil.grantGlobal(TEST_UTIL, globalCreate.getShortName(), Action.CREATE);
232     SecureTestUtil.grantGlobal(TEST_UTIL, globalAdmin.getShortName(), Action.ADMIN);
233 
234     // Namespace users
235     User nsRead = User.createUserForTesting(conf, "nsRead", new String[0]);
236     User nsWrite = User.createUserForTesting(conf, "nsWrite", new String[0]);
237     User nsCreate = User.createUserForTesting(conf, "nsCreate", new String[0]);
238     User nsAdmin = User.createUserForTesting(conf, "nsAdmin", new String[0]);
239     SecureTestUtil.grantOnNamespace(TEST_UTIL, nsRead.getShortName(),
240       TEST_TABLE.getTableName().getNamespaceAsString(), Action.READ);
241     SecureTestUtil.grantOnNamespace(TEST_UTIL, nsWrite.getShortName(),
242       TEST_TABLE.getTableName().getNamespaceAsString(), Action.WRITE);
243     SecureTestUtil.grantOnNamespace(TEST_UTIL, nsCreate.getShortName(),
244       TEST_TABLE.getTableName().getNamespaceAsString(), Action.CREATE);
245     SecureTestUtil.grantOnNamespace(TEST_UTIL, nsAdmin.getShortName(),
246       TEST_TABLE.getTableName().getNamespaceAsString(), Action.ADMIN);
247 
248     // Table users
249     User tableRead = User.createUserForTesting(conf, "tableRead", new String[0]);
250     User tableWrite = User.createUserForTesting(conf, "tableWrite", new String[0]);
251     User tableCreate = User.createUserForTesting(conf, "tableCreate", new String[0]);
252     User tableAdmin = User.createUserForTesting(conf, "tableAdmin", new String[0]);
253     SecureTestUtil.grantOnTable(TEST_UTIL, tableRead.getShortName(),
254       TEST_TABLE.getTableName(), null, null, Action.READ);
255     SecureTestUtil.grantOnTable(TEST_UTIL, tableWrite.getShortName(),
256       TEST_TABLE.getTableName(), null, null, Action.WRITE);
257     SecureTestUtil.grantOnTable(TEST_UTIL, tableCreate.getShortName(),
258       TEST_TABLE.getTableName(), null, null, Action.CREATE);
259     SecureTestUtil.grantOnTable(TEST_UTIL, tableAdmin.getShortName(),
260       TEST_TABLE.getTableName(), null, null, Action.ADMIN);
261 
262     grantGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.WRITE);
263     try {
264       // Write tests
265 
266       AccessTestAction writeAction = new AccessTestAction() {
267         @Override
268         public Object run() throws Exception {
269           HTable t = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
270           try {
271             t.put(new Put(TEST_ROW).add(AccessControlLists.ACL_LIST_FAMILY, TEST_QUALIFIER,
272               TEST_VALUE));
273             return null;
274           } finally {
275             t.close();
276           }
277         }
278       };
279 
280       // All writes to ACL table denied except for GLOBAL WRITE permission and superuser
281 
282       verifyDenied(writeAction, globalAdmin, globalCreate, globalRead, TESTGROUP2_USER1);
283       verifyDenied(writeAction, nsAdmin, nsCreate, nsRead, nsWrite);
284       verifyDenied(writeAction, tableAdmin, tableCreate, tableRead, tableWrite);
285       verifyAllowed(writeAction, superUser, globalWrite, TESTGROUP1_USER1);
286     } finally {
287       revokeGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.WRITE);
288     }
289 
290     grantGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.READ);
291     try {
292       // Read tests
293 
294       AccessTestAction scanAction = new AccessTestAction() {
295         @Override
296         public Object run() throws Exception {
297           HTable t = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
298           try {
299             ResultScanner s = t.getScanner(new Scan());
300             try {
301               for (Result r = s.next(); r != null; r = s.next()) {
302                 // do nothing
303               }
304             } finally {
305               s.close();
306             }
307             return null;
308           } finally {
309             t.close();
310           }
311         }
312       };
313 
314       // All reads from ACL table denied except for GLOBAL READ and superuser
315 
316       verifyDenied(scanAction, globalAdmin, globalCreate, globalWrite, TESTGROUP2_USER1);
317       verifyDenied(scanAction, nsCreate, nsAdmin, nsRead, nsWrite);
318       verifyDenied(scanAction, tableCreate, tableAdmin, tableRead, tableWrite);
319       verifyAllowed(scanAction, superUser, globalRead, TESTGROUP1_USER1);
320     } finally {
321       revokeGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.READ);
322     }
323   }
324 
325   /*
326    * Test table scan operation at table, column family and column qualifier level.
327    */
328   @Test(timeout = 300000)
329   public void testPostGrantAndRevokeScanAction() throws Exception {
330     TableName name = TableName.valueOf(tableName);
331     AccessTestAction scanTableActionForGroupWithTableLevelAccess = new AccessTestAction() {
332       @Override
333       public Void run() throws Exception {
334         HTable table = new HTable(conf, tableName);
335         Scan s1 = new Scan();
336         ResultScanner scanner1 = table.getScanner(s1);
337         try {
338           Result[] next1 = scanner1.next(5);
339           assertTrue("User having table level access should be able to scan all "
340               + "the data in the table.", next1.length == 3);
341         } finally {
342           scanner1.close();
343           table.close();
344         }
345         return null;
346       }
347     };
348 
349     AccessTestAction scanTableActionForGroupWithFamilyLevelAccess = new AccessTestAction() {
350       @Override
351       public Void run() throws Exception {
352         HTable table = new HTable(conf, tableName);
353         Scan s1 = new Scan();
354         ResultScanner scanner1 = table.getScanner(s1);
355         try {
356           Result[] next1 = scanner1.next(5);
357           assertTrue("User having column family level access should be able to scan all "
358               + "the data belonging to that family.", next1.length == 2);
359         } finally {
360           scanner1.close();
361           table.close();
362         }
363         return null;
364       }
365     };
366 
367     AccessTestAction scanFamilyActionForGroupWithFamilyLevelAccess = new AccessTestAction() {
368       @Override
369       public Void run() throws Exception {
370         HTable table = new HTable(conf, tableName);
371         Scan s1 = new Scan();
372         s1.addFamily(TEST_FAMILY_2);
373         ResultScanner scanner1 = null;
374         try {
375           scanner1 = table.getScanner(s1);
376         } finally {
377           if (scanner1 != null) {
378             scanner1.close();
379           }
380           table.close();
381         }
382         return null;
383       }
384     };
385 
386     AccessTestAction scanTableActionForGroupWithQualifierLevelAccess = new AccessTestAction() {
387       @Override
388       public Void run() throws Exception {
389         HTable table = new HTable(conf, tableName);
390         Scan s1 = new Scan();
391         ResultScanner scanner1 = table.getScanner(s1);
392         try {
393           Result[] next1 = scanner1.next(5);
394           assertTrue("User having column qualifier level access should be able to scan "
395               + "that column family qualifier data.", next1.length == 1);
396         } finally {
397           scanner1.close();
398           table.close();
399         }
400         return null;
401       }
402     };
403 
404     AccessTestAction scanFamilyActionForGroupWithQualifierLevelAccess = new AccessTestAction() {
405       @Override
406       public Void run() throws Exception {
407 
408         HTable table = new HTable(conf, tableName);
409         Scan s1 = new Scan();
410         s1.addFamily(TEST_FAMILY_2);
411         ResultScanner scanner1 = null;
412         try {
413           scanner1 = table.getScanner(s1);
414         } finally {
415           if (scanner1 != null) {
416             scanner1.close();
417           }
418           table.close();
419         }
420         return null;
421       }
422     };
423 
424     AccessTestAction scanQualifierActionForGroupWithQualifierLevelAccess = new AccessTestAction() {
425       @Override
426       public Void run() throws Exception {
427         HTable table = new HTable(conf, tableName);
428         Scan s1 = new Scan();
429         s1.addColumn(TEST_FAMILY, Q2);
430         ResultScanner scanner1 = null;
431         try {
432           scanner1 = table.getScanner(s1);
433         } finally {
434           if (scanner1 != null) {
435             scanner1.close();
436           }
437           table.close();
438         }
439         return null;
440       }
441     };
442 
443     // Verify user from a group which has table level access can read all the data and group which
444     // has no access can't read any data.
445     grantOnTable(TEST_UTIL, '@' + TESTGROUP_1, name, null, null, Permission.Action.READ);
446     verifyAllowed(TESTGROUP1_USER1, scanTableActionForGroupWithTableLevelAccess);
447     verifyDenied(TESTGROUP2_USER1, scanTableActionForGroupWithTableLevelAccess);
448 
449     // Verify user from a group whose table level access has been revoked can't read any data.
450     revokeFromTable(TEST_UTIL, '@' + TESTGROUP_1, name, null, null);
451     verifyDenied(TESTGROUP1_USER1, scanTableActionForGroupWithTableLevelAccess);
452 
453     // Verify user from a group which has column family level access can read all the data
454     // belonging to that family and group which has no access can't read any data.
455     grantOnTable(TEST_UTIL, '@' + TESTGROUP_1, name, TEST_FAMILY, null, Permission.Action.READ);
456     verifyAllowed(TESTGROUP1_USER1, scanTableActionForGroupWithFamilyLevelAccess);
457     verifyDenied(TESTGROUP1_USER1, scanFamilyActionForGroupWithFamilyLevelAccess);
458     verifyDenied(TESTGROUP2_USER1, scanTableActionForGroupWithFamilyLevelAccess);
459     verifyDenied(TESTGROUP2_USER1, scanFamilyActionForGroupWithFamilyLevelAccess);
460 
461     // Verify user from a group whose column family level access has been revoked can't read any
462     // data from that family.
463     revokeFromTable(TEST_UTIL, '@' + TESTGROUP_1, name, TEST_FAMILY, null);
464     verifyDenied(TESTGROUP1_USER1, scanTableActionForGroupWithFamilyLevelAccess);
465 
466     // Verify user from a group which has column qualifier level access can read data that has this
467     // family and qualifier, and group which has no access can't read any data.
468     grantOnTable(TEST_UTIL, '@' + TESTGROUP_1, name, TEST_FAMILY, Q1, Permission.Action.READ);
469     verifyAllowed(TESTGROUP1_USER1, scanTableActionForGroupWithQualifierLevelAccess);
470     verifyDenied(TESTGROUP1_USER1, scanFamilyActionForGroupWithQualifierLevelAccess);
471     verifyDenied(TESTGROUP1_USER1, scanQualifierActionForGroupWithQualifierLevelAccess);
472     verifyDenied(TESTGROUP2_USER1, scanTableActionForGroupWithQualifierLevelAccess);
473     verifyDenied(TESTGROUP2_USER1, scanFamilyActionForGroupWithQualifierLevelAccess);
474     verifyDenied(TESTGROUP2_USER1, scanQualifierActionForGroupWithQualifierLevelAccess);
475 
476     // Verify user from a group whose column qualifier level access has been revoked can't read the
477     // data having this column family and qualifier.
478     revokeFromTable(TEST_UTIL, '@' + TESTGROUP_1, name, TEST_FAMILY, Q1);
479     verifyDenied(TESTGROUP1_USER1, scanTableActionForGroupWithQualifierLevelAccess);
480   }
481 
482   @Test
483   public void testACLZNodeDeletion() throws Exception {
484     String baseAclZNode = "/hbase/acl/";
485     String ns = "testACLZNodeDeletionNamespace";
486     NamespaceDescriptor desc = NamespaceDescriptor.create(ns).build();
487     createNamespace(TEST_UTIL, desc);
488 
489     final TableName table = TableName.valueOf(ns, "testACLZNodeDeletionTable");
490     final byte[] family = Bytes.toBytes("f1");
491     HTableDescriptor htd = new HTableDescriptor(table);
492     htd.addFamily(new HColumnDescriptor(family));
493     TEST_UTIL.getHBaseAdmin().createTable(htd);
494 
495     // Namespace needs this, as they follow the lazy creation of ACL znode.
496     grantOnNamespace(TEST_UTIL, TESTGROUP1_USER1.getShortName(), ns, Action.ADMIN);
497     ZooKeeperWatcher zkw = TEST_UTIL.getMiniHBaseCluster().getMaster().getZooKeeper();
498     assertTrue("The acl znode for table should exist",  ZKUtil.checkExists(zkw, baseAclZNode +
499         table.getNameAsString()) != -1);
500     assertTrue("The acl znode for namespace should exist", ZKUtil.checkExists(zkw, baseAclZNode +
501         convertToNamespace(ns)) != -1);
502 
503     revokeFromNamespace(TEST_UTIL, TESTGROUP1_USER1.getShortName(), ns, Action.ADMIN);
504     TEST_UTIL.deleteTable(table);
505     deleteNamespace(TEST_UTIL, ns);
506 
507     assertTrue("The acl znode for table should have been deleted",
508         ZKUtil.checkExists(zkw, baseAclZNode + table.getNameAsString()) == -1);
509     assertTrue( "The acl znode for namespace should have been deleted",
510         ZKUtil.checkExists(zkw, baseAclZNode + convertToNamespace(ns)) == -1);
511   }
512 }