You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kyuubi.apache.org by bo...@apache.org on 2023/03/17 05:16:24 UTC
[kyuubi] branch master updated: [KYUUBI #4504] [Authz] [Bug] Fix source table privilege requirement when querying permanent view in Spark 3.1 and below
This is an automated email from the ASF dual-hosted git repository.
bowenliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kyuubi.git
The following commit(s) were added to refs/heads/master by this push:
new 7709cd18a [KYUUBI #4504] [Authz] [Bug] Fix source table privilege requirement when querying permanent view in Spark 3.1 and below
7709cd18a is described below
commit 7709cd18a7febb4cbf2b7e0d1170ad11dd01f301
Author: liangbowen <li...@gf.com.cn>
AuthorDate: Fri Mar 17 13:16:13 2023 +0800
[KYUUBI #4504] [Authz] [Bug] Fix source table privilege requirement when querying permanent view in Spark 3.1 and below
### _Why are the changes needed?_
To fix #4504.
- add ut for querying the second column of permenant view with multi-column source table (in purpose for reproducing cases for Spark 3.1 and below)
- make user RuleAuthorization check only once, by marking `KYUUBI_AUTHZ_TAG` tag on plan's children and itself , to prevent penetration checks to source table of perm view
### _How was this patch tested?_
- [ ] Add some test cases that check the changes thoroughly including negative and positive cases if possible
- [ ] Add screenshots for manual tests if appropriate
- [x] [Run test](https://kyuubi.readthedocs.io/en/master/develop_tools/testing.html#running-tests) locally before make a pull request
Closes #4529 from bowenliang123/4504-authz-permview.
Closes #4504
b5f9bedcf [liangbowen] minor update
3efba2d23 [liangbowen] update ut and policy
a7584d9d9 [liangbowen] update
c0c11558e [liangbowen] update
ee208d71d [liangbowen] update
3c7804e76 [liangbowen] fix ut
5ab0d1a1f [liangbowen] check RuleAuthorization run only once
Authored-by: liangbowen <li...@gf.com.cn>
Signed-off-by: liangbowen <li...@gf.com.cn>
---
.../spark/authz/ranger/RuleAuthorization.scala | 29 ++++++----
.../src/test/resources/sparkSql_hive_jenkins.json | 61 ++++++++++++++++++++++
.../authz/ranger/RangerSparkExtensionSuite.scala | 54 +++++++++++++++----
3 files changed, 125 insertions(+), 19 deletions(-)
diff --git a/extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/ranger/RuleAuthorization.scala b/extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/ranger/RuleAuthorization.scala
index 1c73acc49..3d53174f3 100644
--- a/extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/ranger/RuleAuthorization.scala
+++ b/extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/ranger/RuleAuthorization.scala
@@ -27,16 +27,15 @@ import org.apache.spark.sql.catalyst.trees.TreeNodeTag
import org.apache.kyuubi.plugin.spark.authz._
import org.apache.kyuubi.plugin.spark.authz.ObjectType._
-import org.apache.kyuubi.plugin.spark.authz.ranger.RuleAuthorization.KYUUBI_AUTHZ_TAG
+import org.apache.kyuubi.plugin.spark.authz.ranger.RuleAuthorization._
import org.apache.kyuubi.plugin.spark.authz.ranger.SparkRangerAdminPlugin._
-import org.apache.kyuubi.plugin.spark.authz.util.AuthZUtils._;
+import org.apache.kyuubi.plugin.spark.authz.util.AuthZUtils._
class RuleAuthorization(spark: SparkSession) extends Rule[LogicalPlan] {
- override def apply(plan: LogicalPlan): LogicalPlan = plan match {
- case p if !plan.getTagValue(KYUUBI_AUTHZ_TAG).contains(true) =>
- RuleAuthorization.checkPrivileges(spark, p)
- p.setTagValue(KYUUBI_AUTHZ_TAG, true)
- p
- case p => p // do nothing if checked privileges already.
+ override def apply(plan: LogicalPlan): LogicalPlan = {
+ plan match {
+ case plan if isAuthChecked(plan) => plan // do nothing if checked privileges already.
+ case p => checkPrivileges(spark, p)
+ }
}
}
@@ -44,7 +43,7 @@ object RuleAuthorization {
val KYUUBI_AUTHZ_TAG = TreeNodeTag[Boolean]("__KYUUBI_AUTHZ_TAG")
- def checkPrivileges(spark: SparkSession, plan: LogicalPlan): Unit = {
+ private def checkPrivileges(spark: SparkSession, plan: LogicalPlan): LogicalPlan = {
val auditHandler = new SparkRangerAuditHandler
val ugi = getAuthzUgi(spark.sparkContext)
val (inputs, outputs, opType) = PrivilegesBuilder.build(plan, spark)
@@ -94,5 +93,17 @@ object RuleAuthorization {
verify(Seq(req), auditHandler)
}
}
+ markAuthChecked(plan)
+ }
+
+ private def markAuthChecked(plan: LogicalPlan): LogicalPlan = {
+ plan.transformUp { case p =>
+ p.setTagValue(KYUUBI_AUTHZ_TAG, true)
+ p
+ }
+ }
+
+ private def isAuthChecked(plan: LogicalPlan): Boolean = {
+ plan.find(_.getTagValue(KYUUBI_AUTHZ_TAG).contains(true)).nonEmpty
}
}
diff --git a/extensions/spark/kyuubi-spark-authz/src/test/resources/sparkSql_hive_jenkins.json b/extensions/spark/kyuubi-spark-authz/src/test/resources/sparkSql_hive_jenkins.json
index 84b0e30eb..250df2ddc 100644
--- a/extensions/spark/kyuubi-spark-authz/src/test/resources/sparkSql_hive_jenkins.json
+++ b/extensions/spark/kyuubi-spark-authz/src/test/resources/sparkSql_hive_jenkins.json
@@ -1151,6 +1151,67 @@
"guid": "b3f1f1e0-2bd6-4b20-8a32-a531006ae151",
"isEnabled": true,
"version": 1
+ },
+ {
+ "service": "hive_jenkins",
+ "name": "someone_access_perm_view",
+ "policyType": 0,
+ "policyPriority": 0,
+ "description": "",
+ "isAuditEnabled": true,
+ "resources": {
+ "database": {
+ "values": [
+ "default"
+ ],
+ "isExcludes": false,
+ "isRecursive": false
+ },
+ "column": {
+ "values": [
+ "*"
+ ],
+ "isExcludes": false,
+ "isRecursive": false
+ },
+ "table": {
+ "values": [
+ "perm_view"
+ ],
+ "isExcludes": false,
+ "isRecursive": false
+ }
+ },
+ "policyItems": [
+ {
+ "accesses": [
+ {
+ "type": "select",
+ "isAllowed": true
+ }
+ ],
+ "users": [
+ "user_perm_view_only"
+ ],
+ "groups": [],
+ "conditions": [],
+ "delegateAdmin": false
+ }
+ ],
+ "denyPolicyItems": [],
+ "allowExceptions": [],
+ "denyExceptions": [],
+ "dataMaskPolicyItems": [],
+ "rowFilterPolicyItems": [],
+ "options": {},
+ "validitySchedules": [],
+ "policyLabels": [
+ ""
+ ],
+ "id": 123,
+ "guid": "2fb6099d-e421-41df-9d24-f2f47bed618e",
+ "isEnabled": true,
+ "version": 5
}
],
"serviceDef": {
diff --git a/extensions/spark/kyuubi-spark-authz/src/test/scala/org/apache/kyuubi/plugin/spark/authz/ranger/RangerSparkExtensionSuite.scala b/extensions/spark/kyuubi-spark-authz/src/test/scala/org/apache/kyuubi/plugin/spark/authz/ranger/RangerSparkExtensionSuite.scala
index 9cd647cdd..3aa551c42 100644
--- a/extensions/spark/kyuubi-spark-authz/src/test/scala/org/apache/kyuubi/plugin/spark/authz/ranger/RangerSparkExtensionSuite.scala
+++ b/extensions/spark/kyuubi-spark-authz/src/test/scala/org/apache/kyuubi/plugin/spark/authz/ranger/RangerSparkExtensionSuite.scala
@@ -521,27 +521,61 @@ class HiveCatalogRangerSparkExtensionSuite extends RangerSparkExtensionSuite {
}
test("[KYUUBI #3326] check persisted view and skip shadowed table") {
+ val db1 = "default"
val table = "hive_src"
val permView = "perm_view"
- val db1 = "default"
- val db2 = "db2"
withCleanTmpResources(Seq(
(s"$db1.$table", "table"),
- (s"$db2.$permView", "view"),
- (db2, "database"))) {
- doAs("admin", sql(s"CREATE TABLE IF NOT EXISTS $db1.$table (id int)"))
-
- doAs("admin", sql(s"CREATE DATABASE IF NOT EXISTS $db2"))
- doAs("admin", sql(s"CREATE VIEW $db2.$permView AS SELECT * FROM $table"))
+ (s"$db1.$permView", "view"))) {
+ doAs("admin", sql(s"CREATE TABLE IF NOT EXISTS $db1.$table (id int, name string)"))
+ doAs("admin", sql(s"CREATE VIEW $db1.$permView AS SELECT * FROM $db1.$table"))
+ // KYUUBI #3326: with no privileges to the permanent view or the source table
val e1 = intercept[AccessControlException](
- doAs("someone", sql(s"select * from $db2.$permView")).show(0))
+ doAs(
+ "someone", {
+ sql(s"select * from $db1.$permView").collect()
+ }))
+ if (isSparkV31OrGreater) {
+ assert(e1.getMessage.contains(s"does not have [select] privilege on [$db1/$permView/id]"))
+ } else {
+ assert(e1.getMessage.contains(s"does not have [select] privilege on [$db1/$table/id]"))
+ }
+ }
+ }
+
+ test("KYUUBI #4504: query permanent view with privilege to permanent view only") {
+ val db1 = "default"
+ val table = "hive_src"
+ val permView = "perm_view"
+ val userPermViewOnly = "user_perm_view_only"
+
+ withCleanTmpResources(Seq(
+ (s"$db1.$table", "table"),
+ (s"$db1.$permView", "view"))) {
+ doAs("admin", sql(s"CREATE TABLE IF NOT EXISTS $db1.$table (id int, name string)"))
+ doAs("admin", sql(s"CREATE VIEW $db1.$permView AS SELECT * FROM $db1.$table"))
+
+ // query all columns of the permanent view
+ // with access privileges to the permanent view but no privilege to the source table
+ val sql1 = s"SELECT * FROM $db1.$permView"
if (isSparkV31OrGreater) {
- assert(e1.getMessage.contains(s"does not have [select] privilege on [$db2/$permView/id]"))
+ doAs(userPermViewOnly, { sql(sql1).collect() })
} else {
+ val e1 = intercept[AccessControlException](doAs(userPermViewOnly, { sql(sql1).collect() }))
assert(e1.getMessage.contains(s"does not have [select] privilege on [$db1/$table/id]"))
}
+
+ // query the second column of permanent view with multiple columns
+ // with access privileges to the permanent view but no privilege to the source table
+ val sql2 = s"SELECT name FROM $db1.$permView"
+ if (isSparkV31OrGreater) {
+ doAs(userPermViewOnly, { sql(sql2).collect() })
+ } else {
+ val e2 = intercept[AccessControlException](doAs(userPermViewOnly, { sql(sql2).collect() }))
+ assert(e2.getMessage.contains(s"does not have [select] privilege on [$db1/$table/name]"))
+ }
}
}