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.apache.hadoop.hbase.AuthUtil.toGroupEntry; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertTrue; 023 024import org.apache.hadoop.conf.Configuration; 025import org.apache.hadoop.hbase.Coprocessor; 026import org.apache.hadoop.hbase.CoprocessorEnvironment; 027import org.apache.hadoop.hbase.HBaseClassTestRule; 028import org.apache.hadoop.hbase.HBaseTestingUtility; 029import org.apache.hadoop.hbase.HColumnDescriptor; 030import org.apache.hadoop.hbase.HTableDescriptor; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.TableNotFoundException; 033import org.apache.hadoop.hbase.client.Connection; 034import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 035import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 036import org.apache.hadoop.hbase.coprocessor.ObserverContextImpl; 037import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 038import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment; 039import org.apache.hadoop.hbase.master.MasterCoprocessorHost; 040import org.apache.hadoop.hbase.regionserver.HRegion; 041import org.apache.hadoop.hbase.regionserver.HRegionServer; 042import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost; 043import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost; 044import org.apache.hadoop.hbase.security.User; 045import org.apache.hadoop.hbase.testclassification.MediumTests; 046import org.apache.hadoop.hbase.testclassification.SecurityTests; 047import org.apache.hadoop.hbase.util.Bytes; 048import org.apache.hadoop.hbase.util.JVMClusterUtil; 049import org.junit.AfterClass; 050import org.junit.BeforeClass; 051import org.junit.ClassRule; 052import org.junit.Rule; 053import org.junit.Test; 054import org.junit.experimental.categories.Category; 055import org.junit.rules.TestName; 056import org.slf4j.Logger; 057import org.slf4j.LoggerFactory; 058 059/** 060 * Performs checks for reference counting w.r.t. TableAuthManager which is used by 061 * AccessController. 062 * 063 * NOTE: Only one test in here. In AMv2, there is problem deleting because 064 * we are missing auth. For now disabled. See the cleanup method. 065 */ 066@Category({SecurityTests.class, MediumTests.class}) 067public class TestAccessController3 extends SecureTestUtil { 068 069 @ClassRule 070 public static final HBaseClassTestRule CLASS_RULE = 071 HBaseClassTestRule.forClass(TestAccessController3.class); 072 073 private static final Logger LOG = LoggerFactory.getLogger(TestAccessController.class); 074 private static TableName TEST_TABLE = TableName.valueOf("testtable1"); 075 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 076 private static Configuration conf; 077 078 /** The systemUserConnection created here is tied to the system user. In case, you are planning 079 * to create AccessTestAction, DON'T use this systemUserConnection as the 'doAs' user 080 * gets eclipsed by the system user. */ 081 private static Connection systemUserConnection; 082 083 084 // user with all permissions 085 private static User SUPERUSER; 086 // user granted with all global permission 087 private static User USER_ADMIN; 088 // user with rw permissions on column family. 089 private static User USER_RW; 090 // user with read-only permissions 091 private static User USER_RO; 092 // user is table owner. will have all permissions on table 093 private static User USER_OWNER; 094 // user with create table permissions alone 095 private static User USER_CREATE; 096 // user with no permissions 097 private static User USER_NONE; 098 // user with admin rights on the column family 099 private static User USER_ADMIN_CF; 100 101 private static final String GROUP_ADMIN = "group_admin"; 102 private static final String GROUP_CREATE = "group_create"; 103 private static final String GROUP_READ = "group_read"; 104 private static final String GROUP_WRITE = "group_write"; 105 106 private static User USER_GROUP_ADMIN; 107 private static User USER_GROUP_CREATE; 108 private static User USER_GROUP_READ; 109 private static User USER_GROUP_WRITE; 110 111 // TODO: convert this test to cover the full matrix in 112 // https://hbase.apache.org/book/appendix_acl_matrix.html 113 // creating all Scope x Permission combinations 114 115 private static byte[] TEST_FAMILY = Bytes.toBytes("f1"); 116 117 private static MasterCoprocessorEnvironment CP_ENV; 118 private static AccessController ACCESS_CONTROLLER; 119 private static RegionServerCoprocessorEnvironment RSCP_ENV; 120 private static RegionCoprocessorEnvironment RCP_ENV; 121 122 private static boolean callSuperTwice = true; 123 124 @Rule 125 public TestName name = new TestName(); 126 127 // class with faulty stop() method, controlled by flag 128 public static class FaultyAccessController extends AccessController { 129 public FaultyAccessController() { 130 } 131 132 @Override 133 public void stop(CoprocessorEnvironment env) { 134 super.stop(env); 135 if (callSuperTwice) { 136 super.stop(env); 137 } 138 } 139 } 140 141 @BeforeClass 142 public static void setupBeforeClass() throws Exception { 143 // setup configuration 144 conf = TEST_UTIL.getConfiguration(); 145 // Enable security 146 enableSecurity(conf); 147 String accessControllerClassName = FaultyAccessController.class.getName(); 148 // In this particular test case, we can't use SecureBulkLoadEndpoint because its doAs will fail 149 // to move a file for a random user 150 conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, accessControllerClassName); 151 // Verify enableSecurity sets up what we require 152 verifyConfiguration(conf); 153 154 // Enable EXEC permission checking 155 conf.setBoolean(AccessControlConstants.EXEC_PERMISSION_CHECKS_KEY, true); 156 157 TEST_UTIL.startMiniCluster(); 158 MasterCoprocessorHost cpHost = 159 TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterCoprocessorHost(); 160 cpHost.load(FaultyAccessController.class, Coprocessor.PRIORITY_HIGHEST, conf); 161 ACCESS_CONTROLLER = (AccessController) cpHost.findCoprocessor(accessControllerClassName); 162 CP_ENV = cpHost.createEnvironment(ACCESS_CONTROLLER, Coprocessor.PRIORITY_HIGHEST, 1, conf); 163 RegionServerCoprocessorHost rsHost; 164 do { 165 rsHost = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0) 166 .getRegionServerCoprocessorHost(); 167 } while (rsHost == null); 168 RSCP_ENV = rsHost.createEnvironment(ACCESS_CONTROLLER, Coprocessor.PRIORITY_HIGHEST, 1, conf); 169 170 // Wait for the ACL table to become available 171 TEST_UTIL.waitUntilAllRegionsAssigned(AccessControlLists.ACL_TABLE_NAME); 172 173 // create a set of test users 174 SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" }); 175 USER_ADMIN = User.createUserForTesting(conf, "admin2", new String[0]); 176 USER_RW = User.createUserForTesting(conf, "rwuser", new String[0]); 177 USER_RO = User.createUserForTesting(conf, "rouser", new String[0]); 178 USER_OWNER = User.createUserForTesting(conf, "owner", new String[0]); 179 USER_CREATE = User.createUserForTesting(conf, "tbl_create", new String[0]); 180 USER_NONE = User.createUserForTesting(conf, "nouser", new String[0]); 181 USER_ADMIN_CF = User.createUserForTesting(conf, "col_family_admin", new String[0]); 182 183 USER_GROUP_ADMIN = 184 User.createUserForTesting(conf, "user_group_admin", new String[] { GROUP_ADMIN }); 185 USER_GROUP_CREATE = 186 User.createUserForTesting(conf, "user_group_create", new String[] { GROUP_CREATE }); 187 USER_GROUP_READ = 188 User.createUserForTesting(conf, "user_group_read", new String[] { GROUP_READ }); 189 USER_GROUP_WRITE = 190 User.createUserForTesting(conf, "user_group_write", new String[] { GROUP_WRITE }); 191 192 systemUserConnection = TEST_UTIL.getConnection(); 193 setUpTableAndUserPermissions(); 194 } 195 196 @AfterClass 197 public static void tearDownAfterClass() throws Exception { 198 HRegionServer rs = null; 199 for (JVMClusterUtil.RegionServerThread thread: 200 TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads()) { 201 rs = thread.getRegionServer(); 202 } 203 cleanUp(); 204 TEST_UTIL.shutdownMiniCluster(); 205 assertTrue("region server should have aborted due to FaultyAccessController", rs.isAborted()); 206 } 207 208 private static void setUpTableAndUserPermissions() throws Exception { 209 HTableDescriptor htd = new HTableDescriptor(TEST_TABLE); 210 HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY); 211 hcd.setMaxVersions(100); 212 htd.addFamily(hcd); 213 htd.setOwner(USER_OWNER); 214 createTable(TEST_UTIL, htd, new byte[][] { Bytes.toBytes("s") }); 215 216 HRegion region = TEST_UTIL.getHBaseCluster().getRegions(TEST_TABLE).get(0); 217 RegionCoprocessorHost rcpHost = region.getCoprocessorHost(); 218 RCP_ENV = rcpHost.createEnvironment(ACCESS_CONTROLLER, Coprocessor.PRIORITY_HIGHEST, 1, conf); 219 220 // Set up initial grants 221 222 grantGlobal(TEST_UTIL, USER_ADMIN.getShortName(), 223 Permission.Action.ADMIN, 224 Permission.Action.CREATE, 225 Permission.Action.READ, 226 Permission.Action.WRITE); 227 228 grantOnTable(TEST_UTIL, USER_RW.getShortName(), 229 TEST_TABLE, TEST_FAMILY, null, 230 Permission.Action.READ, 231 Permission.Action.WRITE); 232 233 // USER_CREATE is USER_RW plus CREATE permissions 234 grantOnTable(TEST_UTIL, USER_CREATE.getShortName(), 235 TEST_TABLE, null, null, 236 Permission.Action.CREATE, 237 Permission.Action.READ, 238 Permission.Action.WRITE); 239 240 grantOnTable(TEST_UTIL, USER_RO.getShortName(), 241 TEST_TABLE, TEST_FAMILY, null, 242 Permission.Action.READ); 243 244 grantOnTable(TEST_UTIL, USER_ADMIN_CF.getShortName(), 245 TEST_TABLE, TEST_FAMILY, 246 null, Permission.Action.ADMIN, Permission.Action.CREATE); 247 248 grantGlobal(TEST_UTIL, toGroupEntry(GROUP_ADMIN), Permission.Action.ADMIN); 249 grantGlobal(TEST_UTIL, toGroupEntry(GROUP_CREATE), Permission.Action.CREATE); 250 grantGlobal(TEST_UTIL, toGroupEntry(GROUP_READ), Permission.Action.READ); 251 grantGlobal(TEST_UTIL, toGroupEntry(GROUP_WRITE), Permission.Action.WRITE); 252 253 assertEquals(5, AccessControlLists.getTablePermissions(conf, TEST_TABLE).size()); 254 try { 255 assertEquals(5, AccessControlClient.getUserPermissions(systemUserConnection, 256 TEST_TABLE.toString()).size()); 257 } catch (Throwable e) { 258 LOG.error("error during call of AccessControlClient.getUserPermissions. ", e); 259 } 260 } 261 262 private static void cleanUp() throws Exception { 263 // Clean the _acl_ table 264 // TODO: Skipping delete because of access issues w/ AMv2. 265 // AMv1 seems to crash servers on exit too for same lack of 266 // auth perms but it gets hung up. 267 try { 268 deleteTable(TEST_UTIL, TEST_TABLE); 269 } catch (TableNotFoundException ex) { 270 // Test deleted the table, no problem 271 LOG.info("Test deleted table " + TEST_TABLE); 272 } 273 // Verify all table/namespace permissions are erased 274 assertEquals(0, AccessControlLists.getTablePermissions(conf, TEST_TABLE).size()); 275 assertEquals( 276 0, 277 AccessControlLists.getNamespacePermissions(conf, 278 TEST_TABLE.getNamespaceAsString()).size()); 279 } 280 281 @Test 282 public void testTableCreate() throws Exception { 283 AccessTestAction createTable = new AccessTestAction() { 284 @Override 285 public Object run() throws Exception { 286 HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName())); 287 htd.addFamily(new HColumnDescriptor(TEST_FAMILY)); 288 ACCESS_CONTROLLER.preCreateTable(ObserverContextImpl.createAndPrepare(CP_ENV), htd, null); 289 return null; 290 } 291 }; 292 293 // verify that superuser can create tables 294 verifyAllowed(createTable, SUPERUSER, USER_ADMIN, USER_GROUP_CREATE); 295 296 // all others should be denied 297 verifyDenied(createTable, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_ADMIN, 298 USER_GROUP_READ, USER_GROUP_WRITE); 299 } 300 301}