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.HBaseTestingUtility;
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 HBaseTestingUtility UTIL = new HBaseTestingUtility();
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 =
079      TableName.valueOf("perms_test");
080
081  @BeforeClass
082  public static void beforeClass() throws Exception {
083    // setup configuration
084    Configuration conf = UTIL.getConfiguration();
085    SecureTestUtil.enableSecurity(conf);
086
087    // start minicluster
088    UTIL.startMiniCluster();
089    AUTH_A = new AuthManager(conf);
090    AUTH_B = new AuthManager(conf);
091    WATCHER_A = new ZKPermissionWatcher(
092        new ZKWatcher(conf, "TestZKPermissionsWatcher_1", ABORTABLE), AUTH_A, conf);
093    WATCHER_B = new ZKPermissionWatcher(
094        new ZKWatcher(conf, "TestZKPermissionsWatcher_2", ABORTABLE), AUTH_B, conf);
095    WATCHER_A.start();
096    WATCHER_B.start();
097  }
098
099  @AfterClass
100  public static void afterClass() throws Exception {
101    WATCHER_A.close();
102    WATCHER_B.close();
103    UTIL.shutdownMiniCluster();
104  }
105
106  @Test
107  public void testPermissionsWatcher() throws Exception {
108    Configuration conf = UTIL.getConfiguration();
109    User george = User.createUserForTesting(conf, "george", new String[] { });
110    User hubert = User.createUserForTesting(conf, "hubert", new String[] { });
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    List<UserPermission> acl = new ArrayList<>(1);
124    acl.add(new UserPermission(george.getShortName(), Permission.newBuilder(TEST_TABLE)
125        .withActions(Permission.Action.READ, Permission.Action.WRITE).build()));
126    ListMultimap<String, UserPermission> multimap = ArrayListMultimap.create();
127    multimap.putAll(george.getShortName(), acl);
128    byte[] serialized = PermissionStorage.writePermissionsAsBytes(multimap, conf);
129    WATCHER_A.writeToZookeeper(TEST_TABLE.getName(), serialized);
130    final long mtimeB = AUTH_B.getMTime();
131    // Wait for the update to propagate
132    UTIL.waitFor(10000, 100, new Predicate<Exception>() {
133      @Override
134      public boolean evaluate() throws Exception {
135        return AUTH_B.getMTime() > mtimeB;
136      }
137    });
138    Thread.sleep(1000);
139
140    // check it
141    assertTrue(AUTH_A.authorizeUserTable(george, TEST_TABLE, Permission.Action.READ));
142    assertTrue(AUTH_A.authorizeUserTable(george, TEST_TABLE, Permission.Action.WRITE));
143    assertTrue(AUTH_B.authorizeUserTable(george, TEST_TABLE, Permission.Action.READ));
144    assertTrue(AUTH_B.authorizeUserTable(george, TEST_TABLE, Permission.Action.WRITE));
145    assertFalse(AUTH_A.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.READ));
146    assertFalse(AUTH_A.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.WRITE));
147    assertFalse(AUTH_B.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.READ));
148    assertFalse(AUTH_B.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.WRITE));
149
150    // update ACL: hubert R
151    List<UserPermission> acl2 = new ArrayList<>(1);
152    acl2.add(new UserPermission(hubert.getShortName(),
153        Permission.newBuilder(TEST_TABLE).withActions(TablePermission.Action.READ).build()));
154    final long mtimeA = AUTH_A.getMTime();
155    multimap.putAll(hubert.getShortName(), acl2);
156    byte[] serialized2 = PermissionStorage.writePermissionsAsBytes(multimap, conf);
157    WATCHER_B.writeToZookeeper(TEST_TABLE.getName(), serialized2);
158    // Wait for the update to propagate
159    UTIL.waitFor(10000, 100, new Predicate<Exception>() {
160      @Override
161      public boolean evaluate() throws Exception {
162        return AUTH_A.getMTime() > mtimeA;
163      }
164    });
165    Thread.sleep(1000);
166
167    // check it
168    assertTrue(AUTH_A.authorizeUserTable(george, TEST_TABLE, Permission.Action.READ));
169    assertTrue(AUTH_A.authorizeUserTable(george, TEST_TABLE, Permission.Action.WRITE));
170    assertTrue(AUTH_B.authorizeUserTable(george, TEST_TABLE, Permission.Action.READ));
171    assertTrue(AUTH_B.authorizeUserTable(george, TEST_TABLE, Permission.Action.WRITE));
172    assertTrue(AUTH_A.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.READ));
173    assertFalse(AUTH_A.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.WRITE));
174    assertTrue(AUTH_B.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.READ));
175    assertFalse(AUTH_B.authorizeUserTable(hubert, TEST_TABLE, Permission.Action.WRITE));
176  }
177}