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