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.junit.jupiter.api.Assertions.assertFalse; 021import static org.junit.jupiter.api.Assertions.assertTrue; 022 023import java.util.ArrayList; 024import java.util.List; 025import java.util.concurrent.atomic.AtomicBoolean; 026import org.apache.hadoop.conf.Configuration; 027import org.apache.hadoop.hbase.Abortable; 028import org.apache.hadoop.hbase.HBaseTestingUtil; 029import org.apache.hadoop.hbase.TableName; 030import org.apache.hadoop.hbase.Waiter.Predicate; 031import org.apache.hadoop.hbase.security.User; 032import org.apache.hadoop.hbase.testclassification.MediumTests; 033import org.apache.hadoop.hbase.testclassification.SecurityTests; 034import org.apache.hadoop.hbase.zookeeper.ZKWatcher; 035import org.junit.jupiter.api.AfterAll; 036import org.junit.jupiter.api.BeforeAll; 037import org.junit.jupiter.api.Tag; 038import org.junit.jupiter.api.Test; 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041 042import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap; 043import org.apache.hbase.thirdparty.com.google.common.collect.ListMultimap; 044 045/** 046 * Test the reading and writing of access permissions to and from zookeeper. 047 */ 048@Tag(SecurityTests.TAG) 049@Tag(MediumTests.TAG) 050public class TestZKPermissionWatcher { 051 052 private static final Logger LOG = LoggerFactory.getLogger(TestZKPermissionWatcher.class); 053 private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 054 private static AuthManager AUTH_A; 055 private static AuthManager AUTH_B; 056 private static ZKPermissionWatcher WATCHER_A; 057 private static ZKPermissionWatcher WATCHER_B; 058 private final static Abortable ABORTABLE = new Abortable() { 059 private final AtomicBoolean abort = new AtomicBoolean(false); 060 061 @Override 062 public void abort(String why, Throwable e) { 063 LOG.info(why, e); 064 abort.set(true); 065 } 066 067 @Override 068 public boolean isAborted() { 069 return abort.get(); 070 } 071 }; 072 073 private static TableName TEST_TABLE = TableName.valueOf("perms_test"); 074 075 @BeforeAll 076 public static void beforeClass() throws Exception { 077 // setup configuration 078 Configuration conf = UTIL.getConfiguration(); 079 SecureTestUtil.enableSecurity(conf); 080 081 // start minicluster 082 UTIL.startMiniCluster(); 083 AUTH_A = new AuthManager(conf); 084 AUTH_B = new AuthManager(conf); 085 WATCHER_A = new ZKPermissionWatcher( 086 new ZKWatcher(conf, "TestZKPermissionsWatcher_1", ABORTABLE), AUTH_A, conf); 087 WATCHER_B = new ZKPermissionWatcher( 088 new ZKWatcher(conf, "TestZKPermissionsWatcher_2", ABORTABLE), AUTH_B, conf); 089 WATCHER_A.start(); 090 WATCHER_B.start(); 091 } 092 093 @AfterAll 094 public static void afterClass() throws Exception { 095 WATCHER_A.close(); 096 WATCHER_B.close(); 097 UTIL.shutdownMiniCluster(); 098 } 099 100 @Test 101 public void testPermissionsWatcher() throws Exception { 102 Configuration conf = UTIL.getConfiguration(); 103 User george = User.createUserForTesting(conf, "george", new String[] {}); 104 User hubert = User.createUserForTesting(conf, "hubert", new String[] {}); 105 ListMultimap<String, UserPermission> permissions = ArrayListMultimap.create(); 106 107 assertFalse(AUTH_A.authorizeUserTable(george, TEST_TABLE, Permission.Action.READ)); 108 assertFalse(AUTH_A.authorizeUserTable(george, TEST_TABLE, Permission.Action.WRITE)); 109 assertFalse(AUTH_A.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.READ)); 110 assertFalse(AUTH_A.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.WRITE)); 111 112 assertFalse(AUTH_B.authorizeUserTable(george, TEST_TABLE, Permission.Action.READ)); 113 assertFalse(AUTH_B.authorizeUserTable(george, TEST_TABLE, Permission.Action.WRITE)); 114 assertFalse(AUTH_B.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.READ)); 115 assertFalse(AUTH_B.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.WRITE)); 116 117 // update ACL: george RW 118 writeToZookeeper(WATCHER_A, 119 updatePermissions(permissions, george, Permission.Action.READ, Permission.Action.WRITE)); 120 waitForModification(AUTH_B, 1000); 121 122 // check it 123 assertTrue(AUTH_A.authorizeUserTable(george, TEST_TABLE, Permission.Action.READ)); 124 assertTrue(AUTH_A.authorizeUserTable(george, TEST_TABLE, Permission.Action.WRITE)); 125 assertTrue(AUTH_B.authorizeUserTable(george, TEST_TABLE, Permission.Action.READ)); 126 assertTrue(AUTH_B.authorizeUserTable(george, TEST_TABLE, Permission.Action.WRITE)); 127 assertFalse(AUTH_A.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.READ)); 128 assertFalse(AUTH_A.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.WRITE)); 129 assertFalse(AUTH_B.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.READ)); 130 assertFalse(AUTH_B.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.WRITE)); 131 132 // update ACL: hubert R 133 writeToZookeeper(WATCHER_B, updatePermissions(permissions, hubert, Permission.Action.READ)); 134 waitForModification(AUTH_A, 1000); 135 136 // check it 137 assertTrue(AUTH_A.authorizeUserTable(george, TEST_TABLE, Permission.Action.READ)); 138 assertTrue(AUTH_A.authorizeUserTable(george, TEST_TABLE, Permission.Action.WRITE)); 139 assertTrue(AUTH_B.authorizeUserTable(george, TEST_TABLE, Permission.Action.READ)); 140 assertTrue(AUTH_B.authorizeUserTable(george, TEST_TABLE, Permission.Action.WRITE)); 141 assertTrue(AUTH_A.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.READ)); 142 assertFalse(AUTH_A.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.WRITE)); 143 assertTrue(AUTH_B.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.READ)); 144 assertFalse(AUTH_B.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.WRITE)); 145 } 146 147 @Test 148 public void testRaceConditionOnPermissionUpdate() throws Exception { 149 Configuration conf = UTIL.getConfiguration(); 150 User tom = User.createUserForTesting(conf, "tom", new String[] {}); 151 User jerry = User.createUserForTesting(conf, "jerry", new String[] {}); 152 ListMultimap<String, UserPermission> permissions = ArrayListMultimap.create(); 153 154 // update ACL: george RW 155 writeToZookeeper(WATCHER_A, 156 updatePermissions(permissions, tom, Permission.Action.READ, Permission.Action.WRITE)); 157 waitForModification(AUTH_A, 1000); 158 159 // check it 160 assertTrue(AUTH_A.authorizeUserTable(tom, TEST_TABLE, Permission.Action.READ)); 161 assertTrue(AUTH_A.authorizeUserTable(tom, TEST_TABLE, Permission.Action.WRITE)); 162 163 // update ACL: hubert A 164 writeToZookeeper(WATCHER_A, updatePermissions(permissions, jerry, Permission.Action.ADMIN)); 165 // intended not to waitForModification(AUTH_A, 1000); 166 // check george permission should not be updated/removed while updating permission for hubert 167 for (int i = 0; i < 5000; i++) { 168 assertTrue(AUTH_A.authorizeUserTable(tom, TEST_TABLE, Permission.Action.READ)); 169 assertTrue(AUTH_A.authorizeUserTable(tom, TEST_TABLE, Permission.Action.WRITE)); 170 } 171 } 172 173 private ListMultimap<String, UserPermission> updatePermissions( 174 ListMultimap<String, UserPermission> permissions, User user, Permission.Action... actions) { 175 List<UserPermission> acl = new ArrayList<>(1); 176 acl.add(new UserPermission(user.getShortName(), 177 Permission.newBuilder(TEST_TABLE).withActions(actions).build())); 178 permissions.putAll(user.getShortName(), acl); 179 return permissions; 180 } 181 182 private void writeToZookeeper(ZKPermissionWatcher watcher, 183 ListMultimap<String, UserPermission> permissions) { 184 byte[] serialized = 185 PermissionStorage.writePermissionsAsBytes(permissions, UTIL.getConfiguration()); 186 watcher.writeToZookeeper(TEST_TABLE.getName(), serialized); 187 } 188 189 private void waitForModification(AuthManager authManager, long sleep) throws Exception { 190 final long mtime = authManager.getMTime(); 191 // Wait for the update to propagate 192 UTIL.waitFor(10000, 100, new Predicate<Exception>() { 193 @Override 194 public boolean evaluate() throws Exception { 195 return authManager.getMTime() > mtime; 196 } 197 }); 198 Thread.sleep(sleep); 199 } 200}