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