001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.security.access; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotNull; 023import static org.junit.Assert.assertNull; 024import static org.junit.Assert.assertTrue; 025 026import java.io.IOException; 027import java.util.Arrays; 028import java.util.List; 029import java.util.Map; 030import java.util.concurrent.atomic.AtomicBoolean; 031import org.apache.hadoop.conf.Configuration; 032import org.apache.hadoop.hbase.Abortable; 033import org.apache.hadoop.hbase.HBaseClassTestRule; 034import org.apache.hadoop.hbase.HBaseTestingUtility; 035import org.apache.hadoop.hbase.TableName; 036import org.apache.hadoop.hbase.client.Admin; 037import org.apache.hadoop.hbase.client.Connection; 038import org.apache.hadoop.hbase.client.ConnectionFactory; 039import org.apache.hadoop.hbase.client.Put; 040import org.apache.hadoop.hbase.client.Table; 041import org.apache.hadoop.hbase.security.User; 042import org.apache.hadoop.hbase.testclassification.LargeTests; 043import org.apache.hadoop.hbase.testclassification.SecurityTests; 044import org.apache.hadoop.hbase.util.Bytes; 045import org.apache.hadoop.hbase.zookeeper.ZKWatcher; 046import org.junit.After; 047import org.junit.AfterClass; 048import org.junit.BeforeClass; 049import org.junit.ClassRule; 050import org.junit.Test; 051import org.junit.experimental.categories.Category; 052import org.slf4j.Logger; 053import org.slf4j.LoggerFactory; 054 055import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap; 056import org.apache.hbase.thirdparty.com.google.common.collect.ListMultimap; 057 058/** 059 * Test the reading and writing of access permissions on {@code _acl_} table. 060 */ 061@Category({SecurityTests.class, LargeTests.class}) 062public class TestTablePermissions { 063 064 @ClassRule 065 public static final HBaseClassTestRule CLASS_RULE = 066 HBaseClassTestRule.forClass(TestTablePermissions.class); 067 068 private static final Logger LOG = LoggerFactory.getLogger(TestTablePermissions.class); 069 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 070 private static ZKWatcher ZKW; 071 private final static Abortable ABORTABLE = new Abortable() { 072 private final AtomicBoolean abort = new AtomicBoolean(false); 073 074 @Override 075 public void abort(String why, Throwable e) { 076 LOG.info(why, e); 077 abort.set(true); 078 } 079 080 @Override 081 public boolean isAborted() { 082 return abort.get(); 083 } 084 }; 085 086 private static String TEST_NAMESPACE = "perms_test_ns"; 087 private static String TEST_NAMESPACE2 = "perms_test_ns2"; 088 private static TableName TEST_TABLE = 089 TableName.valueOf("perms_test"); 090 private static TableName TEST_TABLE2 = 091 TableName.valueOf("perms_test2"); 092 private static byte[] TEST_FAMILY = Bytes.toBytes("f1"); 093 private static byte[] TEST_QUALIFIER = Bytes.toBytes("col1"); 094 095 @BeforeClass 096 public static void beforeClass() throws Exception { 097 // setup configuration 098 Configuration conf = UTIL.getConfiguration(); 099 SecureTestUtil.enableSecurity(conf); 100 101 UTIL.startMiniCluster(); 102 103 // Wait for the ACL table to become available 104 UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME); 105 106 ZKW = new ZKWatcher(UTIL.getConfiguration(), 107 "TestTablePermissions", ABORTABLE); 108 109 UTIL.createTable(TEST_TABLE, TEST_FAMILY); 110 UTIL.createTable(TEST_TABLE2, TEST_FAMILY); 111 } 112 113 @AfterClass 114 public static void afterClass() throws Exception { 115 UTIL.shutdownMiniCluster(); 116 } 117 118 @After 119 public void tearDown() throws Exception { 120 Configuration conf = UTIL.getConfiguration(); 121 try (Connection connection = ConnectionFactory.createConnection(conf); 122 Table table = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) { 123 AccessControlLists.removeTablePermissions(conf, TEST_TABLE, table); 124 AccessControlLists.removeTablePermissions(conf, TEST_TABLE2, table); 125 AccessControlLists.removeTablePermissions(conf, AccessControlLists.ACL_TABLE_NAME, table); 126 } 127 } 128 129 /** 130 * The AccessControlLists.addUserPermission may throw exception before closing the table. 131 */ 132 private void addUserPermission(Configuration conf, UserPermission userPerm, Table t) throws IOException { 133 try { 134 AccessControlLists.addUserPermission(conf, userPerm, t); 135 } finally { 136 t.close(); 137 } 138 } 139 140 @Test 141 public void testBasicWrite() throws Exception { 142 Configuration conf = UTIL.getConfiguration(); 143 try (Connection connection = ConnectionFactory.createConnection(conf)) { 144 // add some permissions 145 addUserPermission(conf, 146 new UserPermission(Bytes.toBytes("george"), TEST_TABLE, null, (byte[])null, 147 UserPermission.Action.READ, UserPermission.Action.WRITE), 148 connection.getTable(AccessControlLists.ACL_TABLE_NAME)); 149 addUserPermission(conf, 150 new UserPermission(Bytes.toBytes("hubert"), TEST_TABLE, null, (byte[])null, 151 UserPermission.Action.READ), 152 connection.getTable(AccessControlLists.ACL_TABLE_NAME)); 153 addUserPermission(conf, 154 new UserPermission(Bytes.toBytes("humphrey"), 155 TEST_TABLE, TEST_FAMILY, TEST_QUALIFIER, 156 UserPermission.Action.READ), 157 connection.getTable(AccessControlLists.ACL_TABLE_NAME)); 158 } 159 // retrieve the same 160 ListMultimap<String,TablePermission> perms = 161 AccessControlLists.getTablePermissions(conf, TEST_TABLE); 162 List<TablePermission> userPerms = perms.get("george"); 163 assertNotNull("Should have permissions for george", userPerms); 164 assertEquals("Should have 1 permission for george", 1, userPerms.size()); 165 TablePermission permission = userPerms.get(0); 166 assertEquals("Permission should be for " + TEST_TABLE, 167 TEST_TABLE, permission.getTableName()); 168 assertNull("Column family should be empty", permission.getFamily()); 169 170 // check actions 171 assertNotNull(permission.getActions()); 172 assertEquals(2, permission.getActions().length); 173 List<TablePermission.Action> actions = Arrays.asList(permission.getActions()); 174 assertTrue(actions.contains(TablePermission.Action.READ)); 175 assertTrue(actions.contains(TablePermission.Action.WRITE)); 176 177 userPerms = perms.get("hubert"); 178 assertNotNull("Should have permissions for hubert", userPerms); 179 assertEquals("Should have 1 permission for hubert", 1, userPerms.size()); 180 permission = userPerms.get(0); 181 assertEquals("Permission should be for " + TEST_TABLE, 182 TEST_TABLE, permission.getTableName()); 183 assertNull("Column family should be empty", permission.getFamily()); 184 185 // check actions 186 assertNotNull(permission.getActions()); 187 assertEquals(1, permission.getActions().length); 188 actions = Arrays.asList(permission.getActions()); 189 assertTrue(actions.contains(TablePermission.Action.READ)); 190 assertFalse(actions.contains(TablePermission.Action.WRITE)); 191 192 userPerms = perms.get("humphrey"); 193 assertNotNull("Should have permissions for humphrey", userPerms); 194 assertEquals("Should have 1 permission for humphrey", 1, userPerms.size()); 195 permission = userPerms.get(0); 196 assertEquals("Permission should be for " + TEST_TABLE, 197 TEST_TABLE, permission.getTableName()); 198 assertTrue("Permission should be for family " + Bytes.toString(TEST_FAMILY), 199 Bytes.equals(TEST_FAMILY, permission.getFamily())); 200 assertTrue("Permission should be for qualifier " + Bytes.toString(TEST_QUALIFIER), 201 Bytes.equals(TEST_QUALIFIER, permission.getQualifier())); 202 203 // check actions 204 assertNotNull(permission.getActions()); 205 assertEquals(1, permission.getActions().length); 206 actions = Arrays.asList(permission.getActions()); 207 assertTrue(actions.contains(TablePermission.Action.READ)); 208 assertFalse(actions.contains(TablePermission.Action.WRITE)); 209 210 // table 2 permissions 211 try (Connection connection = ConnectionFactory.createConnection(conf); 212 Table table = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) { 213 AccessControlLists.addUserPermission(conf, 214 new UserPermission(Bytes.toBytes("hubert"), TEST_TABLE2, null, (byte[])null, 215 TablePermission.Action.READ, TablePermission.Action.WRITE), table); 216 } 217 // check full load 218 Map<byte[], ListMultimap<String,TablePermission>> allPerms = 219 AccessControlLists.loadAll(conf); 220 assertEquals("Full permission map should have entries for both test tables", 221 2, allPerms.size()); 222 223 userPerms = allPerms.get(TEST_TABLE.getName()).get("hubert"); 224 assertNotNull(userPerms); 225 assertEquals(1, userPerms.size()); 226 permission = userPerms.get(0); 227 assertEquals(TEST_TABLE, permission.getTableName()); 228 assertEquals(1, permission.getActions().length); 229 assertEquals(TablePermission.Action.READ, permission.getActions()[0]); 230 231 userPerms = allPerms.get(TEST_TABLE2.getName()).get("hubert"); 232 assertNotNull(userPerms); 233 assertEquals(1, userPerms.size()); 234 permission = userPerms.get(0); 235 assertEquals(TEST_TABLE2, permission.getTableName()); 236 assertEquals(2, permission.getActions().length); 237 actions = Arrays.asList(permission.getActions()); 238 assertTrue(actions.contains(TablePermission.Action.READ)); 239 assertTrue(actions.contains(TablePermission.Action.WRITE)); 240 } 241 242 @Test 243 public void testPersistence() throws Exception { 244 Configuration conf = UTIL.getConfiguration(); 245 try (Connection connection = ConnectionFactory.createConnection(conf)) { 246 addUserPermission(conf, 247 new UserPermission(Bytes.toBytes("albert"), TEST_TABLE, null, 248 (byte[])null, TablePermission.Action.READ), connection.getTable(AccessControlLists.ACL_TABLE_NAME)); 249 addUserPermission(conf, 250 new UserPermission(Bytes.toBytes("betty"), TEST_TABLE, null, 251 (byte[])null, TablePermission.Action.READ, 252 TablePermission.Action.WRITE), connection.getTable(AccessControlLists.ACL_TABLE_NAME)); 253 addUserPermission(conf, 254 new UserPermission(Bytes.toBytes("clark"), 255 TEST_TABLE, TEST_FAMILY, 256 TablePermission.Action.READ), connection.getTable(AccessControlLists.ACL_TABLE_NAME)); 257 addUserPermission(conf, 258 new UserPermission(Bytes.toBytes("dwight"), 259 TEST_TABLE, TEST_FAMILY, TEST_QUALIFIER, 260 TablePermission.Action.WRITE), connection.getTable(AccessControlLists.ACL_TABLE_NAME)); 261 } 262 // verify permissions survive changes in table metadata 263 ListMultimap<String,TablePermission> preperms = 264 AccessControlLists.getTablePermissions(conf, TEST_TABLE); 265 266 Table table = UTIL.getConnection().getTable(TEST_TABLE); 267 table.put(new Put(Bytes.toBytes("row1")) 268 .addColumn(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes("v1"))); 269 table.put(new Put(Bytes.toBytes("row2")) 270 .addColumn(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes("v2"))); 271 Admin admin = UTIL.getAdmin(); 272 try { 273 admin.split(TEST_TABLE); 274 } 275 catch (IOException e) { 276 //although split fail, this may not affect following check 277 //In old Split API without AM2, if region's best split key is not found, 278 //there are not exception thrown. But in current API, exception 279 //will be thrown. 280 LOG.debug("region is not splittable, because " + e); 281 } 282 283 // wait for split 284 Thread.sleep(10000); 285 286 ListMultimap<String,TablePermission> postperms = 287 AccessControlLists.getTablePermissions(conf, TEST_TABLE); 288 289 checkMultimapEqual(preperms, postperms); 290 } 291 292 @Test 293 public void testSerialization() throws Exception { 294 Configuration conf = UTIL.getConfiguration(); 295 ListMultimap<String,TablePermission> permissions = createPermissions(); 296 byte[] permsData = AccessControlLists.writePermissionsAsBytes(permissions, conf); 297 298 ListMultimap<String, TablePermission> copy = 299 AccessControlLists.readPermissions(permsData, conf); 300 301 checkMultimapEqual(permissions, copy); 302 } 303 304 private ListMultimap<String,TablePermission> createPermissions() { 305 ListMultimap<String,TablePermission> permissions = ArrayListMultimap.create(); 306 permissions.put("george", new TablePermission(TEST_TABLE, null, 307 TablePermission.Action.READ)); 308 permissions.put("george", new TablePermission(TEST_TABLE, TEST_FAMILY, 309 TablePermission.Action.WRITE)); 310 permissions.put("george", new TablePermission(TEST_TABLE2, null, 311 TablePermission.Action.READ)); 312 permissions.put("hubert", new TablePermission(TEST_TABLE2, null, 313 TablePermission.Action.READ, TablePermission.Action.WRITE)); 314 permissions.put("bruce",new TablePermission(TEST_NAMESPACE, 315 TablePermission.Action.READ)); 316 return permissions; 317 } 318 319 public void checkMultimapEqual(ListMultimap<String,TablePermission> first, 320 ListMultimap<String,TablePermission> second) { 321 assertEquals(first.size(), second.size()); 322 for (String key : first.keySet()) { 323 List<TablePermission> firstPerms = first.get(key); 324 List<TablePermission> secondPerms = second.get(key); 325 assertNotNull(secondPerms); 326 assertEquals(firstPerms.size(), secondPerms.size()); 327 LOG.info("First permissions: "+firstPerms.toString()); 328 LOG.info("Second permissions: "+secondPerms.toString()); 329 for (TablePermission p : firstPerms) { 330 assertTrue("Permission "+p.toString()+" not found", secondPerms.contains(p)); 331 } 332 } 333 } 334 335 @Test 336 public void testEquals() throws Exception { 337 TablePermission p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ); 338 TablePermission p2 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ); 339 assertTrue(p1.equals(p2)); 340 assertTrue(p2.equals(p1)); 341 342 p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ, TablePermission.Action.WRITE); 343 p2 = new TablePermission(TEST_TABLE, null, TablePermission.Action.WRITE, TablePermission.Action.READ); 344 assertTrue(p1.equals(p2)); 345 assertTrue(p2.equals(p1)); 346 347 p1 = new TablePermission(TEST_TABLE, TEST_FAMILY, TablePermission.Action.READ, TablePermission.Action.WRITE); 348 p2 = new TablePermission(TEST_TABLE, TEST_FAMILY, TablePermission.Action.WRITE, TablePermission.Action.READ); 349 assertTrue(p1.equals(p2)); 350 assertTrue(p2.equals(p1)); 351 352 p1 = new TablePermission(TEST_TABLE, TEST_FAMILY, TEST_QUALIFIER, TablePermission.Action.READ, TablePermission.Action.WRITE); 353 p2 = new TablePermission(TEST_TABLE, TEST_FAMILY, TEST_QUALIFIER, TablePermission.Action.WRITE, TablePermission.Action.READ); 354 assertTrue(p1.equals(p2)); 355 assertTrue(p2.equals(p1)); 356 357 p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ); 358 p2 = new TablePermission(TEST_TABLE, TEST_FAMILY, TablePermission.Action.READ); 359 assertFalse(p1.equals(p2)); 360 assertFalse(p2.equals(p1)); 361 362 p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ); 363 p2 = new TablePermission(TEST_TABLE, null, TablePermission.Action.WRITE); 364 assertFalse(p1.equals(p2)); 365 assertFalse(p2.equals(p1)); 366 p2 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ, TablePermission.Action.WRITE); 367 assertFalse(p1.equals(p2)); 368 assertFalse(p2.equals(p1)); 369 370 p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ); 371 p2 = new TablePermission(TEST_TABLE2, null, TablePermission.Action.READ); 372 assertFalse(p1.equals(p2)); 373 assertFalse(p2.equals(p1)); 374 375 p2 = new TablePermission(TEST_TABLE, null); 376 assertFalse(p1.equals(p2)); 377 assertFalse(p2.equals(p1)); 378 379 p1 = new TablePermission(TEST_NAMESPACE, TablePermission.Action.READ); 380 p2 = new TablePermission(TEST_NAMESPACE, TablePermission.Action.READ); 381 assertEquals(p1, p2); 382 383 p1 = new TablePermission(TEST_NAMESPACE, TablePermission.Action.READ); 384 p2 = new TablePermission(TEST_NAMESPACE2, TablePermission.Action.READ); 385 assertFalse(p1.equals(p2)); 386 assertFalse(p2.equals(p1)); 387 } 388 389 @Test 390 public void testGlobalPermission() throws Exception { 391 Configuration conf = UTIL.getConfiguration(); 392 393 // add some permissions 394 try (Connection connection = ConnectionFactory.createConnection(conf)) { 395 addUserPermission(conf, 396 new UserPermission(Bytes.toBytes("user1"), 397 Permission.Action.READ, Permission.Action.WRITE), connection.getTable(AccessControlLists.ACL_TABLE_NAME)); 398 addUserPermission(conf, 399 new UserPermission(Bytes.toBytes("user2"), 400 Permission.Action.CREATE), connection.getTable(AccessControlLists.ACL_TABLE_NAME)); 401 addUserPermission(conf, 402 new UserPermission(Bytes.toBytes("user3"), 403 Permission.Action.ADMIN, Permission.Action.READ, Permission.Action.CREATE), 404 connection.getTable(AccessControlLists.ACL_TABLE_NAME)); 405 } 406 ListMultimap<String,TablePermission> perms = AccessControlLists.getTablePermissions(conf, null); 407 List<TablePermission> user1Perms = perms.get("user1"); 408 assertEquals("Should have 1 permission for user1", 1, user1Perms.size()); 409 assertEquals("user1 should have WRITE permission", 410 new Permission.Action[] { Permission.Action.READ, Permission.Action.WRITE }, 411 user1Perms.get(0).getActions()); 412 413 List<TablePermission> user2Perms = perms.get("user2"); 414 assertEquals("Should have 1 permission for user2", 1, user2Perms.size()); 415 assertEquals("user2 should have CREATE permission", 416 new Permission.Action[] { Permission.Action.CREATE }, 417 user2Perms.get(0).getActions()); 418 419 List<TablePermission> user3Perms = perms.get("user3"); 420 assertEquals("Should have 1 permission for user3", 1, user3Perms.size()); 421 assertEquals("user3 should have ADMIN, READ, CREATE permission", 422 new Permission.Action[] { 423 Permission.Action.READ, Permission.Action.CREATE, Permission.Action.ADMIN 424 }, 425 user3Perms.get(0).getActions()); 426 } 427 428 @Test 429 public void testAuthManager() throws Exception { 430 Configuration conf = UTIL.getConfiguration(); 431 /* test a race condition causing TableAuthManager to sometimes fail global permissions checks 432 * when the global cache is being updated 433 */ 434 TableAuthManager authManager = TableAuthManager.getOrCreate(ZKW, conf); 435 // currently running user is the system user and should have global admin perms 436 User currentUser = User.getCurrent(); 437 assertTrue(authManager.authorize(currentUser, Permission.Action.ADMIN)); 438 try (Connection connection = ConnectionFactory.createConnection(conf)) { 439 for (int i=1; i<=50; i++) { 440 addUserPermission(conf, new UserPermission(Bytes.toBytes("testauth"+i), 441 Permission.Action.ADMIN, Permission.Action.READ, Permission.Action.WRITE), 442 connection.getTable(AccessControlLists.ACL_TABLE_NAME)); 443 // make sure the system user still shows as authorized 444 assertTrue("Failed current user auth check on iter "+i, 445 authManager.authorize(currentUser, Permission.Action.ADMIN)); 446 } 447 } 448 } 449}