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; 023import org.apache.hadoop.conf.Configuration; 024import org.apache.hadoop.hbase.Coprocessor; 025import org.apache.hadoop.hbase.CoprocessorEnvironment; 026import org.apache.hadoop.hbase.HBaseClassTestRule; 027import org.apache.hadoop.hbase.HBaseTestingUtility; 028import org.apache.hadoop.hbase.HColumnDescriptor; 029import org.apache.hadoop.hbase.HTableDescriptor; 030import org.apache.hadoop.hbase.TableName; 031import org.apache.hadoop.hbase.TableNotFoundException; 032import org.apache.hadoop.hbase.client.Connection; 033import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 034import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 035import org.apache.hadoop.hbase.coprocessor.ObserverContextImpl; 036import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 037import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment; 038import org.apache.hadoop.hbase.master.MasterCoprocessorHost; 039import org.apache.hadoop.hbase.regionserver.HRegion; 040import org.apache.hadoop.hbase.regionserver.HRegionServer; 041import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost; 042import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost; 043import org.apache.hadoop.hbase.security.User; 044import org.apache.hadoop.hbase.testclassification.MediumTests; 045import org.apache.hadoop.hbase.testclassification.SecurityTests; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.apache.hadoop.hbase.util.JVMClusterUtil; 048import org.junit.AfterClass; 049import org.junit.BeforeClass; 050import org.junit.ClassRule; 051import org.junit.Rule; 052import org.junit.Test; 053import org.junit.experimental.categories.Category; 054import org.junit.rules.TestName; 055import org.slf4j.Logger; 056import org.slf4j.LoggerFactory; 057 058/** 059 * Performs checks for reference counting w.r.t. AuthManager which is used by 060 * AccessController. 061 * 062 * NOTE: Only one test in here. In AMv2, there is problem deleting because 063 * we are missing auth. For now disabled. See the cleanup method. 064 */ 065@Category({SecurityTests.class, MediumTests.class}) 066public class TestAccessController3 extends SecureTestUtil { 067 068 @ClassRule 069 public static final HBaseClassTestRule CLASS_RULE = 070 HBaseClassTestRule.forClass(TestAccessController3.class); 071 072 private static final Logger LOG = LoggerFactory.getLogger(TestAccessController.class); 073 private static TableName TEST_TABLE = TableName.valueOf("testtable1"); 074 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 075 private static Configuration conf; 076 077 /** The systemUserConnection created here is tied to the system user. In case, you are planning 078 * to create AccessTestAction, DON'T use this systemUserConnection as the 'doAs' user 079 * gets eclipsed by the system user. */ 080 private static Connection systemUserConnection; 081 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) 165 .getRegionServerCoprocessorHost(); 166 } while (rsHost == null); 167 RSCP_ENV = rsHost.createEnvironment(ACCESS_CONTROLLER, Coprocessor.PRIORITY_HIGHEST, 1, conf); 168 169 // Wait for the ACL table to become available 170 TEST_UTIL.waitUntilAllRegionsAssigned(PermissionStorage.ACL_TABLE_NAME); 171 172 // create a set of test users 173 SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" }); 174 USER_ADMIN = User.createUserForTesting(conf, "admin2", new String[0]); 175 USER_RW = User.createUserForTesting(conf, "rwuser", new String[0]); 176 USER_RO = User.createUserForTesting(conf, "rouser", new String[0]); 177 USER_OWNER = User.createUserForTesting(conf, "owner", new String[0]); 178 USER_CREATE = User.createUserForTesting(conf, "tbl_create", new String[0]); 179 USER_NONE = User.createUserForTesting(conf, "nouser", new String[0]); 180 USER_ADMIN_CF = User.createUserForTesting(conf, "col_family_admin", new String[0]); 181 182 USER_GROUP_ADMIN = 183 User.createUserForTesting(conf, "user_group_admin", new String[] { GROUP_ADMIN }); 184 USER_GROUP_CREATE = 185 User.createUserForTesting(conf, "user_group_create", new String[] { GROUP_CREATE }); 186 USER_GROUP_READ = 187 User.createUserForTesting(conf, "user_group_read", new String[] { GROUP_READ }); 188 USER_GROUP_WRITE = 189 User.createUserForTesting(conf, "user_group_write", new String[] { GROUP_WRITE }); 190 191 systemUserConnection = TEST_UTIL.getConnection(); 192 setUpTableAndUserPermissions(); 193 } 194 195 @AfterClass 196 public static void tearDownAfterClass() throws Exception { 197 assertEquals(1, TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads().size()); 198 HRegionServer rs = TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads().get(0). 199 getRegionServer(); 200 // Strange place for an assert. 201 assertFalse("RegionServer should have ABORTED (FaultyAccessController)", rs.isAborted()); 202 cleanUp(); 203 TEST_UTIL.shutdownMiniCluster(); 204 } 205 206 private static void setUpTableAndUserPermissions() throws Exception { 207 HTableDescriptor htd = new HTableDescriptor(TEST_TABLE); 208 HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY); 209 hcd.setMaxVersions(100); 210 htd.addFamily(hcd); 211 htd.setOwner(USER_OWNER); 212 createTable(TEST_UTIL, htd, new byte[][] { Bytes.toBytes("s") }); 213 214 HRegion region = TEST_UTIL.getHBaseCluster().getRegions(TEST_TABLE).get(0); 215 RegionCoprocessorHost rcpHost = region.getCoprocessorHost(); 216 RCP_ENV = rcpHost.createEnvironment(ACCESS_CONTROLLER, Coprocessor.PRIORITY_HIGHEST, 1, conf); 217 218 // Set up initial grants 219 220 grantGlobal(TEST_UTIL, USER_ADMIN.getShortName(), 221 Permission.Action.ADMIN, 222 Permission.Action.CREATE, 223 Permission.Action.READ, 224 Permission.Action.WRITE); 225 226 grantOnTable(TEST_UTIL, USER_RW.getShortName(), 227 TEST_TABLE, TEST_FAMILY, null, 228 Permission.Action.READ, 229 Permission.Action.WRITE); 230 231 // USER_CREATE is USER_RW plus CREATE permissions 232 grantOnTable(TEST_UTIL, USER_CREATE.getShortName(), 233 TEST_TABLE, null, null, 234 Permission.Action.CREATE, 235 Permission.Action.READ, 236 Permission.Action.WRITE); 237 238 grantOnTable(TEST_UTIL, USER_RO.getShortName(), 239 TEST_TABLE, TEST_FAMILY, null, 240 Permission.Action.READ); 241 242 grantOnTable(TEST_UTIL, USER_ADMIN_CF.getShortName(), 243 TEST_TABLE, TEST_FAMILY, 244 null, Permission.Action.ADMIN, Permission.Action.CREATE); 245 246 grantGlobal(TEST_UTIL, toGroupEntry(GROUP_ADMIN), Permission.Action.ADMIN); 247 grantGlobal(TEST_UTIL, toGroupEntry(GROUP_CREATE), Permission.Action.CREATE); 248 grantGlobal(TEST_UTIL, toGroupEntry(GROUP_READ), Permission.Action.READ); 249 grantGlobal(TEST_UTIL, toGroupEntry(GROUP_WRITE), Permission.Action.WRITE); 250 251 assertEquals(5, PermissionStorage.getTablePermissions(conf, TEST_TABLE).size()); 252 try { 253 assertEquals(5, AccessControlClient.getUserPermissions(systemUserConnection, 254 TEST_TABLE.toString()).size()); 255 } catch (Throwable e) { 256 LOG.error("error during call of AccessControlClient.getUserPermissions. ", e); 257 } 258 } 259 260 private static void cleanUp() throws Exception { 261 // Clean the _acl_ table 262 // TODO: Skipping delete because of access issues w/ AMv2. 263 // AMv1 seems to crash servers on exit too for same lack of 264 // auth perms but it gets hung up. 265 try { 266 deleteTable(TEST_UTIL, TEST_TABLE); 267 } catch (TableNotFoundException ex) { 268 // Test deleted the table, no problem 269 LOG.info("Test deleted table " + TEST_TABLE); 270 } 271 // Verify all table/namespace permissions are erased 272 assertEquals(0, PermissionStorage.getTablePermissions(conf, TEST_TABLE).size()); 273 assertEquals(0, 274 PermissionStorage.getNamespacePermissions(conf, TEST_TABLE.getNamespaceAsString()).size()); 275 } 276 277 @Test 278 public void testTableCreate() throws Exception { 279 AccessTestAction createTable = new AccessTestAction() { 280 @Override 281 public Object run() throws Exception { 282 HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName())); 283 htd.addFamily(new HColumnDescriptor(TEST_FAMILY)); 284 ACCESS_CONTROLLER.preCreateTable(ObserverContextImpl.createAndPrepare(CP_ENV), htd, null); 285 return null; 286 } 287 }; 288 289 // verify that superuser can create tables 290 verifyAllowed(createTable, SUPERUSER, USER_ADMIN, USER_GROUP_CREATE, USER_GROUP_ADMIN); 291 292 // all others should be denied 293 verifyDenied(createTable, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, 294 USER_GROUP_WRITE); 295 } 296 297}