You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kyuubi.apache.org by ul...@apache.org on 2022/08/18 09:58:14 UTC

[incubator-kyuubi] branch master updated: [KYUUBI #3226] Privileges should be checked only once in `RuleAuthorization`

This is an automated email from the ASF dual-hosted git repository.

ulyssesyou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-kyuubi.git


The following commit(s) were added to refs/heads/master by this push:
     new 98df59107 [KYUUBI #3226] Privileges should be checked only once in `RuleAuthorization`
98df59107 is described below

commit 98df59107d00c60d612444606fb15229e88086d0
Author: yikf <yi...@gmail.com>
AuthorDate: Thu Aug 18 17:58:03 2022 +0800

    [KYUUBI #3226] Privileges should be checked only once in `RuleAuthorization`
    
    ### _Why are the changes needed?_
    
    fix https://github.com/apache/incubator-kyuubi/issues/3226
    
    ### _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
    
    - [ ] [Run test](https://kyuubi.apache.org/docs/latest/develop_tools/testing.html#running-tests) locally before make a pull request
    
    Closes #3262 from yikf/auth-once.
    
    Closes #3226
    
    59c1321e [yikf] fix
    dad11aa8 [yikf] [KYUUBI #3226] check privileges once only
    
    Authored-by: yikf <yi...@gmail.com>
    Signed-off-by: ulysses-you <ul...@apache.org>
---
 .../spark/authz/ranger/RuleAuthorization.scala     | 13 ++++-
 .../authz/ranger/RangerSparkExtensionSuite.scala   | 64 ++++++++++++++++++++++
 2 files changed, 74 insertions(+), 3 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 c22fca632..4a0b95ccf 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
@@ -22,20 +22,27 @@ import scala.collection.mutable.ArrayBuffer
 import org.apache.spark.sql.SparkSession
 import org.apache.spark.sql.catalyst.plans.logical.LogicalPlan
 import org.apache.spark.sql.catalyst.rules.Rule
+import org.apache.spark.sql.catalyst.trees.TreeNodeTag
 
 import org.apache.kyuubi.plugin.spark.authz.{ObjectType, _}
 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.util.AuthZUtils._
 
 class RuleAuthorization(spark: SparkSession) extends Rule[LogicalPlan] {
-  override def apply(plan: LogicalPlan): LogicalPlan = {
-    RuleAuthorization.checkPrivileges(spark, plan)
-    plan
+  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.
   }
 }
 
 object RuleAuthorization {
 
+  val KYUUBI_AUTHZ_TAG = TreeNodeTag[Boolean]("__KYUUBI_AUTHZ_TAG")
+
   def checkPrivileges(spark: SparkSession, plan: LogicalPlan): Unit = {
     val auditHandler = new SparkRangerAuditHandler
     val ugi = getAuthzUgi(spark.sparkContext)
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 ec48c6587..ae519b64c 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
@@ -27,12 +27,14 @@ import org.apache.hadoop.security.UserGroupInformation
 import org.apache.spark.sql.{Row, SparkSessionExtensions}
 import org.apache.spark.sql.catalyst.catalog.HiveTableRelation
 import org.apache.spark.sql.catalyst.plans.logical.Statistics
+import org.apache.spark.sql.execution.columnar.InMemoryRelation
 import org.apache.spark.sql.execution.datasources.LogicalRelation
 import org.scalatest.BeforeAndAfterAll
 // scalastyle:off
 import org.scalatest.funsuite.AnyFunSuite
 
 import org.apache.kyuubi.plugin.spark.authz.SparkSessionProvider
+import org.apache.kyuubi.plugin.spark.authz.ranger.RuleAuthorization.KYUUBI_AUTHZ_TAG
 import org.apache.kyuubi.plugin.spark.authz.util.AuthZUtils.getFieldVal
 
 abstract class RangerSparkExtensionSuite extends AnyFunSuite
@@ -59,6 +61,68 @@ abstract class RangerSparkExtensionSuite extends AnyFunSuite
     s"Permission denied: user [$user] does not have [$privilege] privilege on [$resource]"
   }
 
+  test("[KYUUBI #3226] RuleAuthorization: Should check privileges once only.") {
+    val logicalPlan = doAs("admin", sql("SHOW TABLES").queryExecution.logical)
+    val rule = new RuleAuthorization(spark)
+
+    (1 until 10).foreach { i =>
+      if (i == 1) {
+        assert(logicalPlan.getTagValue(KYUUBI_AUTHZ_TAG).isEmpty)
+      } else {
+        assert(logicalPlan.getTagValue(KYUUBI_AUTHZ_TAG).getOrElse(false))
+      }
+      rule.apply(logicalPlan)
+    }
+
+    assert(logicalPlan.getTagValue(KYUUBI_AUTHZ_TAG).getOrElse(false))
+  }
+
+  test("[KYUUBI #3226]: Another session should also check even if the plan is cached.") {
+    val testTable = "mytable"
+    val create =
+      s"""
+         | CREATE TABLE IF NOT EXISTS $testTable
+         | (id string)
+         | using parquet
+         |""".stripMargin
+    val select = s"SELECT * FROM $testTable"
+    try {
+      // create tmp table
+      doAs(
+        "admin", {
+          sql(create)
+
+          // session1: first query, should auth once.[LogicalRelation]
+          val df = sql(select)
+          val plan1 = df.queryExecution.optimizedPlan
+          assert(plan1.getTagValue(KYUUBI_AUTHZ_TAG).getOrElse(false))
+
+          // cache
+          df.cache()
+
+          // session1: second query, should auth once.[InMemoryRelation]
+          // (don't need to check in again, but it's okay to check in once)
+          val plan2 = sql(select).queryExecution.optimizedPlan
+          assert(plan1 != plan2 && plan2.getTagValue(KYUUBI_AUTHZ_TAG).getOrElse(false))
+
+          // session2: should auth once.
+          val otherSessionDf = spark.newSession().sql(select)
+
+          // KYUUBI_AUTH_TAG is None
+          assert(otherSessionDf.queryExecution.logical.getTagValue(KYUUBI_AUTHZ_TAG).isEmpty)
+          val plan3 = otherSessionDf.queryExecution.optimizedPlan
+          // make sure it use cache.
+          assert(plan3.isInstanceOf[InMemoryRelation])
+          // auth once only.
+          assert(plan3.getTagValue(KYUUBI_AUTHZ_TAG).getOrElse(false))
+        })
+
+    } finally {
+      // finally, drop tmp table
+      doAs("admin", sql(s"DROP TABLE IF EXISTS $testTable"))
+    }
+  }
+
   test("auth: databases") {
     val testDb = "mydb"
     val create = s"CREATE DATABASE IF NOT EXISTS $testDb"