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.rsgroup;
019
020import static org.apache.hadoop.hbase.AuthUtil.toGroupEntry;
021import static org.junit.Assert.assertEquals;
022import static org.junit.Assert.fail;
023
024import java.io.IOException;
025import java.util.Optional;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.hbase.HBaseClassTestRule;
028import org.apache.hadoop.hbase.HBaseTestingUtil;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.TableNotFoundException;
031import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
032import org.apache.hadoop.hbase.client.Connection;
033import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
034import org.apache.hadoop.hbase.ipc.RpcServer;
035import org.apache.hadoop.hbase.master.HMaster;
036import org.apache.hadoop.hbase.security.User;
037import org.apache.hadoop.hbase.security.UserProvider;
038import org.apache.hadoop.hbase.security.access.AccessChecker;
039import org.apache.hadoop.hbase.security.access.AccessControlClient;
040import org.apache.hadoop.hbase.security.access.Permission;
041import org.apache.hadoop.hbase.security.access.PermissionStorage;
042import org.apache.hadoop.hbase.security.access.SecureTestUtil;
043import org.apache.hadoop.hbase.testclassification.MediumTests;
044import org.apache.hadoop.hbase.testclassification.SecurityTests;
045import org.apache.hadoop.hbase.util.Bytes;
046import org.junit.AfterClass;
047import org.junit.BeforeClass;
048import org.junit.ClassRule;
049import org.junit.Test;
050import org.junit.experimental.categories.Category;
051import org.slf4j.Logger;
052import org.slf4j.LoggerFactory;
053
054/**
055 * Performs authorization checks for rsgroup operations, according to different levels of authorized
056 * users.
057 */
058@Category({ SecurityTests.class, MediumTests.class })
059public class TestRSGroupsWithACL extends SecureTestUtil {
060
061  @ClassRule
062  public static final HBaseClassTestRule CLASS_RULE =
063    HBaseClassTestRule.forClass(TestRSGroupsWithACL.class);
064
065  private static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsWithACL.class);
066  private static TableName TEST_TABLE = TableName.valueOf("testtable1");
067  private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
068  private static Configuration conf;
069
070  private static Connection systemUserConnection;
071  // user with all permissions
072  private static User SUPERUSER;
073  // user granted with all global permission
074  private static User USER_ADMIN;
075  // user with rw permissions on column family.
076  private static User USER_RW;
077  // user with read-only permissions
078  private static User USER_RO;
079  // user is table owner. will have all permissions on table
080  private static User USER_OWNER;
081  // user with create table permissions alone
082  private static User USER_CREATE;
083  // user with no permissions
084  private static User USER_NONE;
085
086  private static final String GROUP_ADMIN = "group_admin";
087  private static final String GROUP_CREATE = "group_create";
088  private static final String GROUP_READ = "group_read";
089  private static final String GROUP_WRITE = "group_write";
090
091  private static User USER_GROUP_ADMIN;
092  private static User USER_GROUP_CREATE;
093  private static User USER_GROUP_READ;
094  private static User USER_GROUP_WRITE;
095
096  private static byte[] TEST_FAMILY = Bytes.toBytes("f1");
097  private static HMaster master;
098  private static AccessChecker accessChecker;
099  private static UserProvider userProvider;
100
101  @BeforeClass
102  public static void setupBeforeClass() throws Exception {
103    // setup configuration
104    conf = TEST_UTIL.getConfiguration();
105    // Enable security
106    enableSecurity(conf);
107    // Verify enableSecurity sets up what we require
108    verifyConfiguration(conf);
109    // Enable rsgroup
110    RSGroupUtil.enableRSGroup(conf);
111
112    TEST_UTIL.startMiniCluster();
113    // Wait for the ACL table to become available
114    TEST_UTIL.waitUntilAllRegionsAssigned(PermissionStorage.ACL_TABLE_NAME);
115    TEST_UTIL.waitUntilAllRegionsAssigned(RSGroupInfoManagerImpl.RSGROUP_TABLE_NAME);
116    TEST_UTIL.waitUntilNoRegionsInTransition();
117
118    // create a set of test users
119    SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
120    USER_ADMIN = User.createUserForTesting(conf, "admin2", new String[0]);
121    USER_RW = User.createUserForTesting(conf, "rwuser", new String[0]);
122    USER_RO = User.createUserForTesting(conf, "rouser", new String[0]);
123    USER_OWNER = User.createUserForTesting(conf, "owner", new String[0]);
124    USER_CREATE = User.createUserForTesting(conf, "tbl_create", new String[0]);
125    USER_NONE = User.createUserForTesting(conf, "nouser", new String[0]);
126
127    USER_GROUP_ADMIN =
128      User.createUserForTesting(conf, "user_group_admin", new String[] { GROUP_ADMIN });
129    USER_GROUP_CREATE =
130      User.createUserForTesting(conf, "user_group_create", new String[] { GROUP_CREATE });
131    USER_GROUP_READ =
132      User.createUserForTesting(conf, "user_group_read", new String[] { GROUP_READ });
133    USER_GROUP_WRITE =
134      User.createUserForTesting(conf, "user_group_write", new String[] { GROUP_WRITE });
135
136    // Grant table creation permission to USER_OWNER
137    grantGlobal(TEST_UTIL, USER_OWNER.getShortName(), Permission.Action.CREATE);
138
139    systemUserConnection = TEST_UTIL.getConnection();
140    setUpTableAndUserPermissions();
141    master = TEST_UTIL.getHBaseCluster().getMaster();
142    accessChecker = master.getAccessChecker();
143    userProvider = UserProvider.instantiate(TEST_UTIL.getConfiguration());
144  }
145
146  private void checkPermission(String request) throws IOException {
147    accessChecker.requirePermission(getActiveUser(), request, null, Permission.Action.ADMIN);
148  }
149
150  private User getActiveUser() throws IOException {
151    // for non-rpc handling, fallback to system user
152    Optional<User> optionalUser = RpcServer.getRequestUser();
153    if (optionalUser.isPresent()) {
154      return optionalUser.get();
155    }
156    return userProvider.getCurrent();
157  }
158
159  private static void setUpTableAndUserPermissions() throws Exception {
160    TableDescriptorBuilder tableBuilder = TableDescriptorBuilder.newBuilder(TEST_TABLE);
161    ColumnFamilyDescriptorBuilder cfd = ColumnFamilyDescriptorBuilder.newBuilder(TEST_FAMILY);
162    cfd.setMaxVersions(100);
163    tableBuilder.setColumnFamily(cfd.build());
164    createTable(TEST_UTIL, USER_OWNER, tableBuilder.build(), new byte[][] { Bytes.toBytes("s") });
165
166    // Set up initial grants
167    grantGlobal(TEST_UTIL, USER_ADMIN.getShortName(), Permission.Action.ADMIN,
168      Permission.Action.CREATE, Permission.Action.READ, Permission.Action.WRITE);
169
170    grantOnTable(TEST_UTIL, USER_RW.getShortName(), TEST_TABLE, TEST_FAMILY, null,
171      Permission.Action.READ, Permission.Action.WRITE);
172
173    // USER_CREATE is USER_RW plus CREATE permissions
174    grantOnTable(TEST_UTIL, USER_CREATE.getShortName(), TEST_TABLE, null, null,
175      Permission.Action.CREATE, Permission.Action.READ, Permission.Action.WRITE);
176
177    grantOnTable(TEST_UTIL, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null,
178      Permission.Action.READ);
179
180    grantGlobal(TEST_UTIL, toGroupEntry(GROUP_ADMIN), Permission.Action.ADMIN);
181    grantGlobal(TEST_UTIL, toGroupEntry(GROUP_CREATE), Permission.Action.CREATE);
182    grantGlobal(TEST_UTIL, toGroupEntry(GROUP_READ), Permission.Action.READ);
183    grantGlobal(TEST_UTIL, toGroupEntry(GROUP_WRITE), Permission.Action.WRITE);
184
185    assertEquals(4, PermissionStorage.getTablePermissions(conf, TEST_TABLE).size());
186    try {
187      assertEquals(4,
188        AccessControlClient.getUserPermissions(systemUserConnection, TEST_TABLE.toString()).size());
189    } catch (AssertionError e) {
190      fail(e.getMessage());
191    } catch (Throwable e) {
192      LOG.error("error during call of AccessControlClient.getUserPermissions. ", e);
193    }
194  }
195
196  private static void cleanUp() throws Exception {
197    // Clean the _acl_ table
198    try {
199      deleteTable(TEST_UTIL, TEST_TABLE);
200    } catch (TableNotFoundException ex) {
201      // Test deleted the table, no problem
202      LOG.info("Test deleted table " + TEST_TABLE);
203    }
204    // Verify all table/namespace permissions are erased
205    assertEquals(0, PermissionStorage.getTablePermissions(conf, TEST_TABLE).size());
206    assertEquals(0,
207      PermissionStorage.getNamespacePermissions(conf, TEST_TABLE.getNamespaceAsString()).size());
208  }
209
210  @AfterClass
211  public static void tearDownAfterClass() throws Exception {
212    cleanUp();
213    TEST_UTIL.shutdownMiniCluster();
214  }
215
216  @Test
217  public void testGetRSGroupInfo() throws Exception {
218    AccessTestAction action = () -> {
219      checkPermission("getRSGroupInfo");
220      return null;
221    };
222
223    validateAdminPermissions(action);
224  }
225
226  @Test
227  public void testGetRSGroupInfoOfTable() throws Exception {
228    AccessTestAction action = () -> {
229      checkPermission("getRSGroupInfoOfTable");
230      return null;
231    };
232
233    validateAdminPermissions(action);
234  }
235
236  @Test
237  public void testMoveServers() throws Exception {
238    AccessTestAction action = () -> {
239      checkPermission("moveServers");
240      return null;
241    };
242
243    validateAdminPermissions(action);
244  }
245
246  @Test
247  public void testMoveTables() throws Exception {
248    AccessTestAction action = () -> {
249      checkPermission("moveTables");
250      return null;
251    };
252
253    validateAdminPermissions(action);
254  }
255
256  @Test
257  public void testAddRSGroup() throws Exception {
258    AccessTestAction action = () -> {
259      checkPermission("addRSGroup");
260      return null;
261    };
262
263    validateAdminPermissions(action);
264  }
265
266  @Test
267  public void testRemoveRSGroup() throws Exception {
268    AccessTestAction action = () -> {
269      checkPermission("removeRSGroup");
270      return null;
271    };
272
273    validateAdminPermissions(action);
274  }
275
276  @Test
277  public void testBalanceRSGroup() throws Exception {
278    AccessTestAction action = () -> {
279      checkPermission("balanceRSGroup");
280      return null;
281    };
282
283    validateAdminPermissions(action);
284  }
285
286  @Test
287  public void testListRSGroup() throws Exception {
288    AccessTestAction action = () -> {
289      checkPermission("listRSGroup");
290      return null;
291    };
292
293    validateAdminPermissions(action);
294  }
295
296  @Test
297  public void testGetRSGroupInfoOfServer() throws Exception {
298    AccessTestAction action = () -> {
299      checkPermission("getRSGroupInfoOfServer");
300      return null;
301    };
302
303    validateAdminPermissions(action);
304  }
305
306  @Test
307  public void testMoveServersAndTables() throws Exception {
308    AccessTestAction action = () -> {
309      checkPermission("moveServersAndTables");
310      return null;
311    };
312
313    validateAdminPermissions(action);
314  }
315
316  @Test
317  public void testRemoveServers() throws Exception {
318    AccessTestAction action = () -> {
319      checkPermission("removeServers");
320      return null;
321    };
322
323    validateAdminPermissions(action);
324  }
325
326  @Test
327  public void testRenameRSGroup() throws Exception {
328    AccessTestAction action = () -> {
329      checkPermission("renameRSGroup");
330      return null;
331    };
332
333    validateAdminPermissions(action);
334  }
335
336  @Test
337  public void testUpdateRSGroupConfig() throws Exception {
338    AccessTestAction action = () -> {
339      checkPermission("updateRSGroupConfig");
340      return null;
341    };
342
343    validateAdminPermissions(action);
344  }
345
346  private void validateAdminPermissions(AccessTestAction action) throws Exception {
347    verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN);
348    verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ,
349      USER_GROUP_WRITE, USER_GROUP_CREATE);
350  }
351}