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