/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.security.access; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.Abortable; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.LargeTests; import org.apache.hadoop.hbase.client.HBaseAdmin; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; /** * Test the reading and writing of access permissions on {@code _acl_} table. */ @Category(LargeTests.class) public class TestTablePermissions { private static final Log LOG = LogFactory.getLog(TestTablePermissions.class); private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); private static ZooKeeperWatcher ZKW; private final static Abortable ABORTABLE = new Abortable() { private final AtomicBoolean abort = new AtomicBoolean(false); @Override public void abort(String why, Throwable e) { LOG.info(why, e); abort.set(true); } @Override public boolean isAborted() { return abort.get(); } }; private static byte[] TEST_TABLE = Bytes.toBytes("perms_test"); private static byte[] TEST_TABLE2 = Bytes.toBytes("perms_test2"); private static byte[] TEST_FAMILY = Bytes.toBytes("f1"); private static byte[] TEST_QUALIFIER = Bytes.toBytes("col1"); @BeforeClass public static void beforeClass() throws Exception { // setup configuration Configuration conf = UTIL.getConfiguration(); SecureTestUtil.enableSecurity(conf); UTIL.startMiniCluster(); // Wait for the ACL table to become available UTIL.waitTableAvailable(AccessControlLists.ACL_TABLE_NAME, 5000); ZKW = new ZooKeeperWatcher(UTIL.getConfiguration(), "TestTablePermissions", ABORTABLE); UTIL.createTable(TEST_TABLE, TEST_FAMILY); UTIL.createTable(TEST_TABLE2, TEST_FAMILY); } @AfterClass public static void afterClass() throws Exception { UTIL.shutdownMiniCluster(); } @Test public void testBasicWrite() throws Exception { Configuration conf = UTIL.getConfiguration(); // add some permissions AccessControlLists.addUserPermission(conf, new UserPermission(Bytes.toBytes("george"), TEST_TABLE, null, (byte[])null, UserPermission.Action.READ, UserPermission.Action.WRITE)); AccessControlLists.addUserPermission(conf, new UserPermission(Bytes.toBytes("hubert"), TEST_TABLE, null, (byte[])null, UserPermission.Action.READ)); AccessControlLists.addUserPermission(conf, new UserPermission(Bytes.toBytes("humphrey"), TEST_TABLE, TEST_FAMILY, TEST_QUALIFIER, UserPermission.Action.READ)); // retrieve the same ListMultimap<String,TablePermission> perms = AccessControlLists.getTablePermissions(conf, TEST_TABLE); List<TablePermission> userPerms = perms.get("george"); assertNotNull("Should have permissions for george", userPerms); assertEquals("Should have 1 permission for george", 1, userPerms.size()); TablePermission permission = userPerms.get(0); assertTrue("Permission should be for " + TEST_TABLE, Bytes.equals(TEST_TABLE, permission.getTable())); assertNull("Column family should be empty", permission.getFamily()); // check actions assertNotNull(permission.getActions()); assertEquals(2, permission.getActions().length); List<TablePermission.Action> actions = Arrays.asList(permission.getActions()); assertTrue(actions.contains(TablePermission.Action.READ)); assertTrue(actions.contains(TablePermission.Action.WRITE)); userPerms = perms.get("hubert"); assertNotNull("Should have permissions for hubert", userPerms); assertEquals("Should have 1 permission for hubert", 1, userPerms.size()); permission = userPerms.get(0); assertTrue("Permission should be for " + TEST_TABLE, Bytes.equals(TEST_TABLE, permission.getTable())); assertNull("Column family should be empty", permission.getFamily()); // check actions assertNotNull(permission.getActions()); assertEquals(1, permission.getActions().length); actions = Arrays.asList(permission.getActions()); assertTrue(actions.contains(TablePermission.Action.READ)); assertFalse(actions.contains(TablePermission.Action.WRITE)); userPerms = perms.get("humphrey"); assertNotNull("Should have permissions for humphrey", userPerms); assertEquals("Should have 1 permission for humphrey", 1, userPerms.size()); permission = userPerms.get(0); assertTrue("Permission should be for " + TEST_TABLE, Bytes.equals(TEST_TABLE, permission.getTable())); assertTrue("Permission should be for family " + TEST_FAMILY, Bytes.equals(TEST_FAMILY, permission.getFamily())); assertTrue("Permission should be for qualifier " + TEST_QUALIFIER, Bytes.equals(TEST_QUALIFIER, permission.getQualifier())); // check actions assertNotNull(permission.getActions()); assertEquals(1, permission.getActions().length); actions = Arrays.asList(permission.getActions()); assertTrue(actions.contains(TablePermission.Action.READ)); assertFalse(actions.contains(TablePermission.Action.WRITE)); // table 2 permissions AccessControlLists.addUserPermission(conf, new UserPermission(Bytes.toBytes("hubert"), TEST_TABLE2, null, (byte[])null, TablePermission.Action.READ, TablePermission.Action.WRITE)); // check full load Map<byte[],ListMultimap<String,TablePermission>> allPerms = AccessControlLists.loadAll(conf); assertEquals("Full permission map should have entries for both test tables", 2, allPerms.size()); userPerms = allPerms.get(TEST_TABLE).get("hubert"); assertNotNull(userPerms); assertEquals(1, userPerms.size()); permission = userPerms.get(0); assertTrue(Bytes.equals(TEST_TABLE, permission.getTable())); assertEquals(1, permission.getActions().length); assertEquals(TablePermission.Action.READ, permission.getActions()[0]); userPerms = allPerms.get(TEST_TABLE2).get("hubert"); assertNotNull(userPerms); assertEquals(1, userPerms.size()); permission = userPerms.get(0); assertTrue(Bytes.equals(TEST_TABLE2, permission.getTable())); assertEquals(2, permission.getActions().length); actions = Arrays.asList(permission.getActions()); assertTrue(actions.contains(TablePermission.Action.READ)); assertTrue(actions.contains(TablePermission.Action.WRITE)); } @Test public void testPersistence() throws Exception { Configuration conf = UTIL.getConfiguration(); AccessControlLists.addUserPermission(conf, new UserPermission(Bytes.toBytes("albert"), TEST_TABLE, null, (byte[])null, TablePermission.Action.READ)); AccessControlLists.addUserPermission(conf, new UserPermission(Bytes.toBytes("betty"), TEST_TABLE, null, (byte[])null, TablePermission.Action.READ, TablePermission.Action.WRITE)); AccessControlLists.addUserPermission(conf, new UserPermission(Bytes.toBytes("clark"), TEST_TABLE, TEST_FAMILY, TablePermission.Action.READ)); AccessControlLists.addUserPermission(conf, new UserPermission(Bytes.toBytes("dwight"), TEST_TABLE, TEST_FAMILY, TEST_QUALIFIER, TablePermission.Action.WRITE)); // verify permissions survive changes in table metadata ListMultimap<String,TablePermission> preperms = AccessControlLists.getTablePermissions(conf, TEST_TABLE); HTable table = new HTable(conf, TEST_TABLE); table.put(new Put(Bytes.toBytes("row1")) .add(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes("v1"))); table.put(new Put(Bytes.toBytes("row2")) .add(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes("v2"))); HBaseAdmin admin = UTIL.getHBaseAdmin(); admin.split(TEST_TABLE); // wait for split Thread.sleep(10000); ListMultimap<String,TablePermission> postperms = AccessControlLists.getTablePermissions(conf, TEST_TABLE); checkMultimapEqual(preperms, postperms); } @Test public void testSerialization() throws Exception { Configuration conf = UTIL.getConfiguration(); ListMultimap<String,TablePermission> permissions = ArrayListMultimap.create(); permissions.put("george", new TablePermission(TEST_TABLE, null, TablePermission.Action.READ)); permissions.put("george", new TablePermission(TEST_TABLE, TEST_FAMILY, TablePermission.Action.WRITE)); permissions.put("george", new TablePermission(TEST_TABLE2, null, TablePermission.Action.READ)); permissions.put("hubert", new TablePermission(TEST_TABLE2, null, TablePermission.Action.READ, TablePermission.Action.WRITE)); byte[] permsData = AccessControlLists.writePermissionsAsBytes(permissions, conf); ListMultimap<String,TablePermission> copy = AccessControlLists.readPermissions(permsData, conf); checkMultimapEqual(permissions, copy); } public void checkMultimapEqual(ListMultimap<String,TablePermission> first, ListMultimap<String,TablePermission> second) { assertEquals(first.size(), second.size()); for (String key : first.keySet()) { List<TablePermission> firstPerms = first.get(key); List<TablePermission> secondPerms = second.get(key); assertNotNull(secondPerms); assertEquals(firstPerms.size(), secondPerms.size()); LOG.info("First permissions: "+firstPerms.toString()); LOG.info("Second permissions: "+secondPerms.toString()); for (TablePermission p : firstPerms) { assertTrue("Permission "+p.toString()+" not found", secondPerms.contains(p)); } } } @Test public void testEquals() throws Exception { TablePermission p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ); TablePermission p2 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ); assertTrue(p1.equals(p2)); assertTrue(p2.equals(p1)); p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ, TablePermission.Action.WRITE); p2 = new TablePermission(TEST_TABLE, null, TablePermission.Action.WRITE, TablePermission.Action.READ); assertTrue(p1.equals(p2)); assertTrue(p2.equals(p1)); p1 = new TablePermission(TEST_TABLE, TEST_FAMILY, TablePermission.Action.READ, TablePermission.Action.WRITE); p2 = new TablePermission(TEST_TABLE, TEST_FAMILY, TablePermission.Action.WRITE, TablePermission.Action.READ); assertTrue(p1.equals(p2)); assertTrue(p2.equals(p1)); p1 = new TablePermission(TEST_TABLE, TEST_FAMILY, TEST_QUALIFIER, TablePermission.Action.READ, TablePermission.Action.WRITE); p2 = new TablePermission(TEST_TABLE, TEST_FAMILY, TEST_QUALIFIER, TablePermission.Action.WRITE, TablePermission.Action.READ); assertTrue(p1.equals(p2)); assertTrue(p2.equals(p1)); p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ); p2 = new TablePermission(TEST_TABLE, TEST_FAMILY, TablePermission.Action.READ); assertFalse(p1.equals(p2)); assertFalse(p2.equals(p1)); p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ); p2 = new TablePermission(TEST_TABLE, null, TablePermission.Action.WRITE); assertFalse(p1.equals(p2)); assertFalse(p2.equals(p1)); p2 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ, TablePermission.Action.WRITE); assertFalse(p1.equals(p2)); assertFalse(p2.equals(p1)); p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ); p2 = new TablePermission(TEST_TABLE2, null, TablePermission.Action.READ); assertFalse(p1.equals(p2)); assertFalse(p2.equals(p1)); p2 = new TablePermission(TEST_TABLE, null); assertFalse(p1.equals(p2)); assertFalse(p2.equals(p1)); } @Test public void testGlobalPermission() throws Exception { Configuration conf = UTIL.getConfiguration(); // add some permissions AccessControlLists.addUserPermission(conf, new UserPermission(Bytes.toBytes("user1"), Permission.Action.READ, Permission.Action.WRITE)); AccessControlLists.addUserPermission(conf, new UserPermission(Bytes.toBytes("user2"), Permission.Action.CREATE)); AccessControlLists.addUserPermission(conf, new UserPermission(Bytes.toBytes("user3"), Permission.Action.ADMIN, Permission.Action.READ, Permission.Action.CREATE)); ListMultimap<String,TablePermission> perms = AccessControlLists.getTablePermissions(conf, null); List<TablePermission> user1Perms = perms.get("user1"); assertEquals("Should have 1 permission for user1", 1, user1Perms.size()); assertEquals("user1 should have WRITE permission", new Permission.Action[] { Permission.Action.READ, Permission.Action.WRITE }, user1Perms.get(0).getActions()); List<TablePermission> user2Perms = perms.get("user2"); assertEquals("Should have 1 permission for user2", 1, user2Perms.size()); assertEquals("user2 should have CREATE permission", new Permission.Action[] { Permission.Action.CREATE }, user2Perms.get(0).getActions()); List<TablePermission> user3Perms = perms.get("user3"); assertEquals("Should have 1 permission for user3", 1, user3Perms.size()); assertEquals("user3 should have ADMIN, READ, CREATE permission", new Permission.Action[] { Permission.Action.ADMIN, Permission.Action.READ, Permission.Action.CREATE }, user3Perms.get(0).getActions()); } @Test public void testAuthManager() throws Exception { Configuration conf = UTIL.getConfiguration(); /* test a race condition causing TableAuthManager to sometimes fail global permissions checks * when the global cache is being updated */ TableAuthManager authManager = TableAuthManager.get(ZKW, conf); // currently running user is the system user and should have global admin perms User currentUser = User.getCurrent(); assertTrue(authManager.authorize(currentUser, Permission.Action.ADMIN)); for (int i=1; i<=50; i++) { AccessControlLists.addUserPermission(conf, new UserPermission(Bytes.toBytes("testauth"+i), Permission.Action.ADMIN, Permission.Action.READ, Permission.Action.WRITE)); // make sure the system user still shows as authorized assertTrue("Failed current user auth check on iter "+i, authManager.authorize(currentUser, Permission.Action.ADMIN)); } } }