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