blob: 49202a4981d36cdd6a45d9c58c13e45f766defc2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.phoenix.end2end;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.security.access.AccessControlClient;
import org.apache.hadoop.hbase.security.access.Permission.Action;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.phoenix.query.QueryServices;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
/**
* Test that verifies a user can read Phoenix tables with a minimal set of permissions.
*/
@Category(NeedsOwnMiniClusterTest.class)
public class SystemTablePermissionsIT {
private static String SUPERUSER;
private static final Set<String> PHOENIX_SYSTEM_TABLES = new HashSet<>(Arrays.asList(
"SYSTEM.CATALOG", "SYSTEM.SEQUENCE", "SYSTEM.STATS", "SYSTEM.FUNCTION",
"SYSTEM.MUTEX"));
private static final Set<String> PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES = new HashSet<>(
Arrays.asList("SYSTEM:CATALOG", "SYSTEM:SEQUENCE", "SYSTEM:STATS", "SYSTEM:FUNCTION",
"SYSTEM:MUTEX"));
private static final String TABLE_NAME =
SystemTablePermissionsIT.class.getSimpleName().toUpperCase();
private static final int NUM_RECORDS = 5;
private HBaseTestingUtility testUtil = null;
private Properties clientProperties = null;
@BeforeClass
public static void setup() throws Exception {
SUPERUSER = System.getProperty("user.name");
}
private static void setCommonConfigProperties(Configuration conf) {
conf.set("hbase.coprocessor.master.classes",
"org.apache.hadoop.hbase.security.access.AccessController");
conf.set("hbase.coprocessor.region.classes",
"org.apache.hadoop.hbase.security.access.AccessController");
conf.set("hbase.coprocessor.regionserver.classes",
"org.apache.hadoop.hbase.security.access.AccessController");
conf.set("hbase.security.exec.permission.checks", "true");
conf.set("hbase.security.authorization", "true");
conf.set("hbase.superuser", SUPERUSER);
}
@After
public void cleanup() throws Exception {
if (null != testUtil) {
testUtil.shutdownMiniCluster();
testUtil = null;
}
}
@Test
public void testSystemTablePermissions() throws Exception {
testUtil = new HBaseTestingUtility();
clientProperties = new Properties();
Configuration conf = testUtil.getConfiguration();
setCommonConfigProperties(conf);
conf.set(QueryServices.IS_NAMESPACE_MAPPING_ENABLED, "false");
clientProperties.setProperty(QueryServices.IS_NAMESPACE_MAPPING_ENABLED, "false");
testUtil.startMiniCluster(1);
final UserGroupInformation superUser = UserGroupInformation.createUserForTesting(
SUPERUSER, new String[0]);
final UserGroupInformation regularUser = UserGroupInformation.createUserForTesting(
"user", new String[0]);
superUser.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
createTable();
readTable();
return null;
}
});
Set<String> tables = getHBaseTables();
assertTrue("HBase tables do not include expected Phoenix tables: " + tables,
tables.containsAll(PHOENIX_SYSTEM_TABLES));
// Grant permission to the system tables for the unprivileged user
superUser.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
try {
grantPermissions(regularUser.getShortUserName(), PHOENIX_SYSTEM_TABLES,
Action.EXEC, Action.READ);
grantPermissions(regularUser.getShortUserName(),
Collections.singleton(TABLE_NAME), Action.READ);
} catch (Throwable e) {
if (e instanceof Exception) {
throw (Exception) e;
} else {
throw new Exception(e);
}
}
return null;
}
});
// Make sure that the unprivileged user can read the table
regularUser.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
// We expect this to not throw an error
readTable();
return null;
}
});
}
@Test
public void testNamespaceMappedSystemTables() throws Exception {
testUtil = new HBaseTestingUtility();
clientProperties = new Properties();
Configuration conf = testUtil.getConfiguration();
setCommonConfigProperties(conf);
testUtil.getConfiguration().set(QueryServices.IS_NAMESPACE_MAPPING_ENABLED, "true");
clientProperties.setProperty(QueryServices.IS_NAMESPACE_MAPPING_ENABLED, "true");
testUtil.startMiniCluster(1);
final UserGroupInformation superUser =
UserGroupInformation.createUserForTesting(SUPERUSER, new String[0]);
final UserGroupInformation regularUser =
UserGroupInformation.createUserForTesting("user", new String[0]);
superUser.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
createTable();
readTable();
return null;
}
});
Set<String> tables = getHBaseTables();
assertTrue("HBase tables do not include expected Phoenix tables: " + tables,
tables.containsAll(PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES));
// Grant permission to the system tables for the unprivileged user
// An unprivileged user should only need to be able to Read and eXecute on them.
superUser.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
try {
grantPermissions(regularUser.getShortUserName(),
PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES, Action.EXEC, Action.READ);
grantPermissions(regularUser.getShortUserName(),
Collections.singleton(TABLE_NAME), Action.READ);
} catch (Throwable e) {
if (e instanceof Exception) {
throw (Exception) e;
} else {
throw new Exception(e);
}
}
return null;
}
});
regularUser.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
// We expect this to not throw an error
readTable();
return null;
}
});
}
private String getJdbcUrl() {
return "jdbc:phoenix:localhost:" + testUtil.getZkCluster().getClientPort() + ":/hbase";
}
private void createTable() throws SQLException {
try (Connection conn = DriverManager.getConnection(getJdbcUrl(), clientProperties);
Statement stmt = conn.createStatement();) {
assertFalse(stmt.execute("DROP TABLE IF EXISTS " + TABLE_NAME));
assertFalse(stmt.execute("CREATE TABLE " + TABLE_NAME
+ "(pk INTEGER not null primary key, data VARCHAR)"));
try (PreparedStatement pstmt = conn.prepareStatement("UPSERT INTO "
+ TABLE_NAME + " values(?, ?)")) {
for (int i = 0; i < NUM_RECORDS; i++) {
pstmt.setInt(1, i);
pstmt.setString(2, Integer.toString(i));
assertEquals(1, pstmt.executeUpdate());
}
}
conn.commit();
}
}
private void readTable() throws SQLException {
try (Connection conn = DriverManager.getConnection(getJdbcUrl(), clientProperties);
Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery("SELECT pk, data FROM " + TABLE_NAME);
assertNotNull(rs);
int i = 0;
while (rs.next()) {
assertEquals(i, rs.getInt(1));
assertEquals(Integer.toString(i), rs.getString(2));
i++;
}
assertEquals(NUM_RECORDS, i);
}
}
private void grantPermissions(String toUser, Set<String> tablesToGrant, Action... actions)
throws Throwable {
for (String table : tablesToGrant) {
AccessControlClient.grant(testUtil.getConnection(), TableName.valueOf(table), toUser,
null, null, actions);
}
}
private Set<String> getHBaseTables() throws IOException {
Set<String> tables = new HashSet<>();
for (TableName tn : testUtil.getHBaseAdmin().listTableNames()) {
tables.add(tn.getNameAsString());
}
return tables;
}
}