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