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