You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sentry.apache.org by sd...@apache.org on 2015/10/28 08:33:46 UTC
incubator-sentry git commit: SENTRY-847: [column level privilege] if
grant column level privilege to user,
show columns in table shouldn't require extra table level privilege (Guoquan
Shen, Reviewed by: Colin Ma)
Repository: incubator-sentry
Updated Branches:
refs/heads/hive_plugin_v2 fe5e4a9a6 -> 912057309
SENTRY-847: [column level privilege] if grant column level privilege to user, show columns in table shouldn't require extra table level privilege (Guoquan Shen, Reviewed by: Colin Ma)
Project: http://git-wip-us.apache.org/repos/asf/incubator-sentry/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-sentry/commit/91205730
Tree: http://git-wip-us.apache.org/repos/asf/incubator-sentry/tree/91205730
Diff: http://git-wip-us.apache.org/repos/asf/incubator-sentry/diff/91205730
Branch: refs/heads/hive_plugin_v2
Commit: 91205730928e517598a3a49fd985d28aca669b1f
Parents: fe5e4a9
Author: Guoquan Shen <gu...@intel.com>
Authored: Fri Aug 28 18:06:25 2015 +0800
Committer: Sun Dapeng <sd...@apache.org>
Committed: Wed Oct 28 14:55:21 2015 +0800
----------------------------------------------------------------------
.../hive/ql/exec/SentryFilterDDLTask.java | 138 +++++++++++++++++++
.../binding/hive/HiveAuthzBindingHook.java | 70 +++++++++-
.../binding/hive/authz/HiveAuthzPrivileges.java | 3 +-
.../hive/authz/HiveAuthzPrivilegesMap.java | 10 +-
.../file/SimpleFileProviderBackend.java | 2 +-
.../e2e/dbprovider/TestColumnEndToEnd.java | 82 +++++++++++
6 files changed, 301 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/91205730/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/ql/exec/SentryFilterDDLTask.java
----------------------------------------------------------------------
diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/ql/exec/SentryFilterDDLTask.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/ql/exec/SentryFilterDDLTask.java
new file mode 100644
index 0000000..d47ca3b
--- /dev/null
+++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/ql/exec/SentryFilterDDLTask.java
@@ -0,0 +1,138 @@
+/*
+ * 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.hadoop.hive.ql.exec;
+
+import static org.apache.hadoop.util.StringUtils.stringifyException;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hive.metastore.api.FieldSchema;
+import org.apache.hadoop.hive.ql.DriverContext;
+import org.apache.hadoop.hive.ql.ErrorMsg;
+import org.apache.hadoop.hive.ql.metadata.Hive;
+import org.apache.hadoop.hive.ql.metadata.HiveException;
+import org.apache.hadoop.hive.ql.metadata.Table;
+import org.apache.hadoop.hive.ql.metadata.formatting.MetaDataFormatUtils;
+import org.apache.hadoop.hive.ql.plan.HiveOperation;
+import org.apache.hadoop.hive.ql.plan.ShowColumnsDesc;
+import org.apache.hadoop.hive.ql.session.SessionState;
+import org.apache.hadoop.io.IOUtils;
+import org.apache.sentry.binding.hive.HiveAuthzBindingHook;
+import org.apache.sentry.binding.hive.authz.HiveAuthzBinding;
+import org.apache.sentry.core.common.Subject;
+
+import com.google.common.base.Preconditions;
+
+public class SentryFilterDDLTask extends DDLTask {
+ private static final long serialVersionUID = 1L;
+ private static final Log LOG = LogFactory.getLog(SentryFilterDDLTask.class);
+
+ private HiveAuthzBinding hiveAuthzBinding;
+ private Subject subject;
+ private HiveOperation stmtOperation;
+
+ public SentryFilterDDLTask(HiveAuthzBinding hiveAuthzBinding, Subject subject,
+ HiveOperation stmtOperation) {
+ Preconditions.checkNotNull(hiveAuthzBinding);
+ Preconditions.checkNotNull(subject);
+ Preconditions.checkNotNull(stmtOperation);
+
+ this.hiveAuthzBinding = hiveAuthzBinding;
+ this.subject = subject;
+ this.stmtOperation = stmtOperation;
+ }
+
+ public HiveAuthzBinding getHiveAuthzBinding() {
+ return hiveAuthzBinding;
+ }
+
+ public Subject getSubject() {
+ return subject;
+ }
+
+ public HiveOperation getStmtOperation() {
+ return stmtOperation;
+ }
+
+ @Override
+ public int execute(DriverContext driverContext) {
+ // Currently the SentryFilterDDLTask only supports filter the "show columns in table " command.
+ ShowColumnsDesc showCols = work.getShowColumnsDesc();
+ try {
+ if (showCols != null) {
+ return showFilterColumns(showCols);
+ }
+ } catch (Throwable e) {
+ failed(e);
+ return 1;
+ }
+
+ return super.execute(driverContext);
+ }
+
+ private void failed(Throwable e) {
+ while (e.getCause() != null && e.getClass() == RuntimeException.class) {
+ e = e.getCause();
+ }
+ setException(e);
+ LOG.error(stringifyException(e));
+ }
+
+ /**
+ * Filter the command "show columns in table"
+ *
+ */
+ private int showFilterColumns(ShowColumnsDesc showCols) throws HiveException {
+ Table table = Hive.get(conf).getTable(showCols.getTableName());
+
+ // write the results in the file
+ DataOutputStream outStream = null;
+ try {
+ Path resFile = new Path(showCols.getResFile());
+ FileSystem fs = resFile.getFileSystem(conf);
+ outStream = fs.create(resFile);
+
+ List<FieldSchema> cols = table.getCols();
+ cols.addAll(table.getPartCols());
+ // In case the query is served by HiveServer2, don't pad it with spaces,
+ // as HiveServer2 output is consumed by JDBC/ODBC clients.
+ boolean isOutputPadded = !SessionState.get().isHiveServerQuery();
+ outStream.writeBytes(MetaDataFormatUtils.getAllColumnsInformation(
+ fiterColumns(cols, table), false, isOutputPadded, null));
+ outStream.close();
+ outStream = null;
+ } catch (IOException e) {
+ throw new HiveException(e, ErrorMsg.GENERIC_ERROR);
+ } finally {
+ IOUtils.closeStream(outStream);
+ }
+ return 0;
+ }
+
+ private List<FieldSchema> fiterColumns(List<FieldSchema> cols, Table table) throws HiveException {
+ // filter some columns that the subject has privilege on
+ return HiveAuthzBindingHook.filterShowColumns(getHiveAuthzBinding(),
+ cols, getStmtOperation(), getSubject().getName(), table.getTableName(), table.getDbName());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/91205730/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java
----------------------------------------------------------------------
diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java
index ddfb222..62410c7 100644
--- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java
+++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java
@@ -31,6 +31,9 @@ import java.util.Set;
import org.apache.hadoop.hive.common.JavaUtils;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
+import org.apache.hadoop.hive.metastore.api.FieldSchema;
+import org.apache.hadoop.hive.ql.exec.DDLTask;
+import org.apache.hadoop.hive.ql.exec.SentryFilterDDLTask;
import org.apache.hadoop.hive.ql.exec.SentryGrantRevokeTask;
import org.apache.hadoop.hive.ql.exec.Task;
import org.apache.hadoop.hive.ql.hooks.Entity;
@@ -45,6 +48,7 @@ import org.apache.hadoop.hive.ql.parse.BaseSemanticAnalyzer;
import org.apache.hadoop.hive.ql.parse.HiveParser;
import org.apache.hadoop.hive.ql.parse.HiveSemanticAnalyzerHookContext;
import org.apache.hadoop.hive.ql.parse.SemanticException;
+import org.apache.hadoop.hive.ql.plan.DDLWork;
import org.apache.hadoop.hive.ql.plan.HiveOperation;
import org.apache.hadoop.hive.ql.session.SessionState;
import org.apache.sentry.binding.hive.authz.HiveAuthzBinding;
@@ -335,6 +339,22 @@ public class HiveAuthzBindingHook extends AbstractSemanticAnalyzerHook {
// We don't handle authorizing this statement
return;
}
+
+ /**
+ * Replace DDLTask using the SentryFilterDDLTask for protection,
+ * such as "show column" only allow show some column that user can access to.
+ * SENTRY-847
+ */
+ for (int i = 0; i < rootTasks.size(); i++) {
+ Task<? extends Serializable> task = rootTasks.get(i);
+ if (task instanceof DDLTask) {
+ SentryFilterDDLTask filterTask =
+ new SentryFilterDDLTask(hiveAuthzBinding, subject, stmtOperation);
+ filterTask.setWork((DDLWork)task.getWork());
+ rootTasks.set(i, filterTask);
+ }
+ }
+
authorizeWithHiveBindings(context, stmtAuthObject, stmtOperation);
} catch (AuthorizationException e) {
executeOnFailureHooks(context, stmtOperation, e);
@@ -506,7 +526,19 @@ public class HiveAuthzBindingHook extends AbstractSemanticAnalyzerHook {
inputHierarchy.add(connectHierarchy);
outputHierarchy.add(connectHierarchy);
break;
-
+ case COLUMN:
+ for (ReadEntity readEntity: inputs) {
+ if (readEntity.getAccessedColumns() != null && !readEntity.getAccessedColumns().isEmpty()) {
+ addColumnHierarchy(inputHierarchy, readEntity);
+ } else {
+ List<DBModelAuthorizable> entityHierarchy = new ArrayList<DBModelAuthorizable>();
+ entityHierarchy.add(hiveAuthzBinding.getAuthServer());
+ entityHierarchy.addAll(getAuthzHierarchyFromEntity(readEntity));
+ entityHierarchy.add(Column.ALL);
+ inputHierarchy.add(entityHierarchy);
+ }
+ }
+ break;
default:
throw new AuthorizationException("Unknown operation scope type " +
stmtAuthObject.getOperationScope().toString());
@@ -692,6 +724,42 @@ public class HiveAuthzBindingHook extends AbstractSemanticAnalyzerHook {
return filteredResult;
}
+ public static List<FieldSchema> filterShowColumns(
+ HiveAuthzBinding hiveAuthzBinding, List<FieldSchema> cols,
+ HiveOperation operation, String userName, String tableName, String dbName)
+ throws SemanticException {
+ List<FieldSchema> filteredResult = new ArrayList<FieldSchema>();
+ Subject subject = new Subject(userName);
+ HiveAuthzPrivileges ColumnMetaDataPrivilege =
+ HiveAuthzPrivilegesMap.getHiveAuthzPrivileges(HiveOperation.SHOWCOLUMNS);
+
+ Database database = new Database(dbName);
+ Table table = new Table(tableName);
+ for (FieldSchema col : cols) {
+ // if user has privileges on column, add to filtered list, else discard
+ List<List<DBModelAuthorizable>> inputHierarchy = new ArrayList<List<DBModelAuthorizable>>();
+ List<List<DBModelAuthorizable>> outputHierarchy = new ArrayList<List<DBModelAuthorizable>>();
+ List<DBModelAuthorizable> externalAuthorizableHierarchy = new ArrayList<DBModelAuthorizable>();
+ externalAuthorizableHierarchy.add(hiveAuthzBinding.getAuthServer());
+ externalAuthorizableHierarchy.add(database);
+ externalAuthorizableHierarchy.add(table);
+ externalAuthorizableHierarchy.add(new Column(col.getName()));
+ inputHierarchy.add(externalAuthorizableHierarchy);
+
+ try {
+ hiveAuthzBinding.authorize(operation, ColumnMetaDataPrivilege, subject,
+ inputHierarchy, outputHierarchy);
+ filteredResult.add(col);
+ } catch (AuthorizationException e) {
+ // squash the exception, user doesn't have privileges, so the column is
+ // not added to
+ // filtered list.
+ ;
+ }
+ }
+ return filteredResult;
+ }
+
public static List<String> filterShowDatabases(
HiveAuthzBinding hiveAuthzBinding, List<String> queryResult,
HiveOperation operation, String userName) throws SemanticException {
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/91205730/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivileges.java
----------------------------------------------------------------------
diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivileges.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivileges.java
index 8cd82ef..f164b30 100644
--- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivileges.java
+++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivileges.java
@@ -52,7 +52,8 @@ public class HiveAuthzPrivileges {
DATABASE,
TABLE,
FUNCTION,
- CONNECT
+ CONNECT,
+ COLUMN
}
public static enum HiveExtendedOperation {
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/91205730/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java
----------------------------------------------------------------------
diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java
index 0291b6c..e721555 100644
--- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java
+++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java
@@ -140,6 +140,12 @@ public class HiveAuthzPrivilegesMap {
setOperationType(HiveOperationType.INFO).
build();
+ HiveAuthzPrivileges ColumnMetaDataPrivilege = new HiveAuthzPrivileges.AuthzPrivilegeBuilder().
+ addInputObjectPriviledge(AuthorizableType.Column, EnumSet.of(DBModelAction.SELECT, DBModelAction.INSERT)).
+ setOperationScope(HiveOperationScope.COLUMN).
+ setOperationType(HiveOperationType.INFO).
+ build();
+
HiveAuthzPrivileges dbImportPrivilege = new HiveAuthzPrivileges.AuthzPrivilegeBuilder().
addOutputObjectPriviledge(AuthorizableType.Db, EnumSet.of(DBModelAction.CREATE)).
addInputObjectPriviledge(AuthorizableType.URI, EnumSet.of(DBModelAction.ALL)).
@@ -255,9 +261,11 @@ public class HiveAuthzPrivilegesMap {
hiveAuthzStmtPrivMap.put(HiveOperation.CREATEFUNCTION, functionPrivilege);
hiveAuthzStmtPrivMap.put(HiveOperation.DROPFUNCTION, functionPrivilege);
+ // SHOWCOLUMNS
+ hiveAuthzStmtPrivMap.put(HiveOperation.SHOWCOLUMNS, ColumnMetaDataPrivilege);
+
// SHOWDATABASES
// SHOWTABLES
- hiveAuthzStmtPrivMap.put(HiveOperation.SHOWCOLUMNS, tableMetaDataPrivilege);
hiveAuthzStmtPrivMap.put(HiveOperation.SHOW_TABLESTATUS, tableMetaDataPrivilege);
hiveAuthzStmtPrivMap.put(HiveOperation.SHOW_TBLPROPERTIES, tableMetaDataPrivilege);
hiveAuthzStmtPrivMap.put(HiveOperation.SHOW_CREATETABLE, tableMetaDataPrivilege);
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/91205730/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/SimpleFileProviderBackend.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/SimpleFileProviderBackend.java b/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/SimpleFileProviderBackend.java
index 526a0e0..1b83c0d 100644
--- a/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/SimpleFileProviderBackend.java
+++ b/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/SimpleFileProviderBackend.java
@@ -183,7 +183,7 @@ public class SimpleFileProviderBackend implements ProviderBackend {
@Override
public void close() {
- groupRolePrivilegeTable.clear();
+ // SENTRY-847 will use HiveAuthBinding again, so groupRolePrivilegeTable shouldn't clear itself
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/91205730/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestColumnEndToEnd.java
----------------------------------------------------------------------
diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestColumnEndToEnd.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestColumnEndToEnd.java
index 159b9d9..718a736 100644
--- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestColumnEndToEnd.java
+++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestColumnEndToEnd.java
@@ -17,17 +17,22 @@
package org.apache.sentry.tests.e2e.dbprovider;
+import static junit.framework.Assert.fail;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileOutputStream;
import java.sql.Connection;
+import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
import org.apache.sentry.provider.db.SentryAccessDeniedException;
import org.apache.sentry.provider.file.PolicyFile;
import org.apache.sentry.tests.e2e.hive.AbstractTestWithStaticConfiguration;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -272,4 +277,81 @@ public class TestColumnEndToEnd extends AbstractTestWithStaticConfiguration {
statement.close();
connection.close();
}
+
+ @Test
+ public void testShowColumns() throws Exception {
+ // grant select on test_tb(s) to USER1_1
+ Connection connection = context.createConnection(ADMIN1);
+ Statement statement = context.createStatement(connection);
+ statement.execute("CREATE database " + DB1);
+ statement.execute("use " + DB1);
+ statement.execute("CREATE TABLE test_tb (s string, i string)");
+ statement.execute("CREATE ROLE user_role1");
+ statement.execute("GRANT SELECT (s) ON TABLE test_tb TO ROLE user_role1");
+ statement.execute("GRANT ROLE user_role1 TO GROUP " + USERGROUP1);
+ statement.close();
+ connection.close();
+
+ // USER1_1 executes "show columns in test_tb" and gets the s column information
+ connection = context.createConnection(USER1_1);
+ statement = context.createStatement(connection);
+ statement.execute("use " + DB1);
+ ResultSet res = statement.executeQuery("show columns in test_tb");
+
+ List<String> expectedResult = new ArrayList<String>();
+ List<String> returnedResult = new ArrayList<String>();
+ expectedResult.add("s");
+ while (res.next()) {
+ returnedResult.add(res.getString(1).trim());
+ }
+ validateReturnedResult(expectedResult, returnedResult);
+ returnedResult.clear();
+ expectedResult.clear();
+ res.close();
+
+ statement.close();
+ connection.close();
+
+ // grant select on test_tb(s, i) to USER2_1
+ connection = context.createConnection(ADMIN1);
+ statement = context.createStatement(connection);
+ statement.execute("use " + DB1);
+ statement.execute("CREATE ROLE user_role2");
+ statement.execute("GRANT SELECT(s, i) ON TABLE test_tb TO ROLE user_role2");
+ statement.execute("GRANT ROLE user_role2 TO GROUP " + USERGROUP2);
+ statement.close();
+ connection.close();
+
+ // USER2_1 executes "show columns in test_tb" and gets the s,i columns information
+ connection = context.createConnection(USER2_1);
+ statement = context.createStatement(connection);
+ statement.execute("use " + DB1);
+ res = statement.executeQuery("show columns in test_tb");
+
+ expectedResult.add("s");
+ expectedResult.add("i");
+ while (res.next()) {
+ returnedResult.add(res.getString(1).trim());
+ }
+ validateReturnedResult(expectedResult, returnedResult);
+ returnedResult.clear();
+ expectedResult.clear();
+ res.close();
+
+ statement.close();
+ connection.close();
+
+ // USER3_1 executes "show columns in test_tb" and the exception will be thrown
+ connection = context.createConnection(USER3_1);
+ statement = context.createStatement(connection);
+ try {
+ // USER3_1 has no privilege on any column, so "show columns in test_tb" will throw an exception
+ statement.execute("show columns in db_1.test_tb");
+ fail("No valid privileges exception should have been thrown");
+ } catch (Exception e) {
+ }
+
+ statement.close();
+ connection.close();
+ }
}