Skip to content

Authz supports Spark 4.0 and 4.1#7552

Closed
pan3793 wants to merge 18 commits into
apache:masterfrom
pan3793:authz-spark-4.1-v2
Closed

Authz supports Spark 4.0 and 4.1#7552
pan3793 wants to merge 18 commits into
apache:masterfrom
pan3793:authz-spark-4.1-v2

Conversation

@pan3793

@pan3793 pan3793 commented Jul 9, 2026

Copy link
Copy Markdown
Member

Why are the changes needed?

kyuubi-spark-authz does not support Spark 4.0/4.1. Several Spark 4.0+ changes break authorization, row-filtering, and data masking. This PR fixes all known issues and enables CI for Spark 4.0/4.1.

What changes are proposed?

Authz fixes:

  • AlterColumns spec for SPARK-43995 (AlterColumnAlterColumns rename in Spark 4.0)
  • Unwrap CommandResult to authorize CALL procedures (Spark 4.0 eager execution)
  • Match ShowNamespacesCommand for SHOW DATABASES row-filtering (Spark 4.1 nodeName change)
  • Clear leaked KYUUBI_AUTHZ_TAG from cached catalog nodes (Spark 4.0 LogicalRelation reuse)

Test adaptations:

  • Disable ANSI mode in data masking tests (SPARK-44444 enables ANSI by default; AnsiStringPromotionTypeCoercion casts masked strings to bigint → CAST_INVALID_INPUT)
  • HoodieCatalogTableTableExtractor fallback for Hudi 1.2.0 DeleteHoodieTableCommand.dftquery rename
  • Paimon suite: isSupportedVersion = isScalaV212 || isSparkV40OrGreater, alter non-PK column, handle LocalDateTime

Dependency upgrades:

  • Delta 4.3.1 with Spark-version-specific artifacts (delta-spark_4.0_2.13 / delta-spark_4.1_2.13)
  • Hudi 1.1.1 (Spark 3.x, Java 8) / 1.2.0 (Spark 4.x)
  • Paimon 1.4.2 for Spark 4.0; keep 0.8.2 for Spark 3.x (1.4.x has known Spark 3.3 issues)
  • Enable Iceberg, Delta, Hudi tests for Spark 4.0/4.1, Paimon for Spark 4.0

How was this patch tested?

All existing authz tests pass across Spark 3.3/3.4/3.5 (Scala 2.12) and Spark 4.0/4.1 (Scala 2.13).

Was this patch authored or co-authored using generative AI tooling?

Assisted-by: codemaker with glm-5.2

pan3793 added 11 commits July 8, 2026 10:35
SPARK-43995 (Spark 4.0) renames the v2 `ALTER TABLE ... ALTER COLUMN`
command class from `AlterColumn` to `AlterColumns`, wrapping a seq of
`AlterColumnSpec` changes. The table is still carried in `child` as a
`ResolvedTable`, so it reuses the AddColumns spec.

Without this spec, `PrivilegesBuilder` falls back to `QUERY` for
`AlterColumns`, so ALTER COLUMN on Spark 4.0 is not authorized and the
opType assertion fails in V2JdbcTableCatalogPrivilegesBuilderSuite and
IcebergCatalogPrivilegesBuilderSuite.
Spark 4.0 eagerly executes CALL procedures during analysis and rewrites
the `Call` logical node into a `CommandResult` whose
`commandLogicalPlan` field carries the original command. Additionally,
`Call` no longer extends `Command` on Spark 4.0.

Both changes cause the optimizer-stage `RuleAuthorization` to see only
`CommandResult` (resolved to `QUERY`) and never authorize the CALL,
leaving Iceberg procedures like `rewrite_data_files`,
`rollback_to_snapshot`, and `set_current_snapshot` unchecked.

Fix by:
- Adding `unwrapCommandResult` in `serde` package to reflectively read
  `commandLogicalPlan` when the plan is `CommandResult` (no-op on
  Spark 3.x where the class is absent).
- Wiring it into `operationType` and the entry of `PrivilegesBuilder.build`.
- Adding a `case cmd if isKnownTableCommand(cmd)` dispatch fallback in
  `build` for non-`Command` known commands (Spark 4.0 `Call`).
Spark 4.1 resolves `SHOW DATABASES` to `ShowNamespacesCommand` (nodeName
changed from `ShowNamespaces` to `ShowNamespacesCommand`), so both
`RuleReplaceShowObjectCommands` and `FilterDataSourceV2Strategy` miss
the match and the row-filtering placeholder is never applied. As a result,
unauthorized databases are visible to all users on Spark 4.1.

Match both `ShowNamespaces` (Spark 3.x/4.0) and `ShowNamespacesCommand`
(Spark 4.1) in the resolution rule and the planner strategy.
…Spark 4.0

`RuleEliminatePermanentViewMarker` calls `markAllNodesAuthChecked` which
uses `setTagValue` (mutable `TreeNodeTag`) to tag ALL plan nodes. On
Spark 4.0+, `LogicalRelation` instances are reused across queries from
the catalog cache, so the `KYUUBI_AUTHZ_TAG` leaked from a previous
permanent-view query's optimizer into the next query's analyzed plan.
`buildQuery` then saw the leaked tag on the `LogicalRelation` and
skipped privilege extraction, bypassing authz for any query on a table
that appeared in a prior permanent-view query.

Fix by injecting `RuleClearAuthzTag` as the first resolution rule. It
clears `KYUUBI_AUTHZ_TAG` from all nodes before the optimizer runs,
stripping leaked tags. This is safe because the tag is only set by
optimizer rules (`RuleAuthorization`, `RuleEliminatePermanentViewMarker`),
never by resolution rules; clearing is idempotent; and on Spark 3.5
(no leak) it is a harmless no-op.
- DataMasking: Spark 4.0 strict string->numeric cast (CAST_INVALID_INPUT)
  breaks coalesce/join/union over MASK_HASH'd (string) columns derived
  from int sources. String-type the fallback literals and join keys.
  On v2 catalogs (Iceberg/JDBC v2) + Spark 4.0, the v2 type coercion
  still resolves coalesce/union back to the original numeric dataType
  despite the masked StringType output; skip those 2 tests with a TODO
  until the coercion is addressed upstream.

- LoadDataCommand: Spark 4.0 rejects LOAD DATA for datasource tables;
  use STORED AS TEXTFILE to keep the table Hive-native so the
  CommandResult unwrap (previous commit) can authorize LoadDataCommand.

- table stats: Spark 4.0 converts default-format Hive tables to
  LogicalRelation at optimizedPlan; STORED AS TEXTFILE preserves
  HiveTableRelation so the tableStats assertion holds.

- Table Command location privilege: Spark 4.0 no longer propagates CTAS
  output columns into the create privilege; update the expected message
  to table-level (no columns) for Spark 4.0+.
Hudi 1.2.0 renamed DeleteHoodieTableCommand.dft to query, breaking the
existing HudiDataSourceV2RelationTableExtractor which reads the dft
field via reflection. Add HoodieCatalogTableTableExtractor that reads
the stable catalogTable field (HoodieCatalogTable -> CatalogTable) and
add it as a fallback tableDesc in the DeleteHoodieTableCommand spec.

Also use Spark-version-specific bundle artifacts
(hudi-spark4.0-bundle / hudi-spark4.1-bundle) and remove HudiTest from
the exclude tags for spark-4.0 and spark-4.1 profiles.
Upgrade Paimon from 0.8.2 to 1.4.2 for Spark 3.4, 3.5, and 4.0. Spark
3.3 keeps 0.8.2 as 1.4.x requires Java 11. Spark 4.1 is not yet
supported by Paimon, so it keeps paimon-common with PaimonTest excluded.

Paimon 1.4.2 publishes Scala-version-specific artifacts
(paimon-spark-X.Y_2.12 / paimon-spark-X.Y_2.13), so the artifact name
now includes the Scala binary version suffix.

Adapt the PaimonCatalogRangerSparkExtensionSuite:
- isSupportedVersion: isScalaV212 || isSparkV40OrGreater (1.4.2 supports
  Scala 2.13 for Spark 4.0+)
- Changing Column Type: alter a non-PK column (1.4.2 rejects altering
  primary key columns)
- Batch Time Travel: use get(0) instead of getTimestamp(0) to handle
  LocalDateTime returned by 1.4.2 for commit_time
@github-actions github-actions Bot added kind:infra license, community building, project builds, asf infra related, etc. module:spark kind:build module:extensions module:authz labels Jul 9, 2026
@pan3793
pan3793 marked this pull request as ready for review July 10, 2026 07:02
@pan3793
pan3793 requested review from Copilot and ulysses-you July 10, 2026 07:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the kyuubi-spark-authz Spark authorization extension to restore compatibility with Spark 4.0/4.1, addressing Spark-side logical plan changes that impact command authorization, row filtering, and tag-based authz tracking, while also adapting/expanding test and dependency coverage for Spark 4.x profiles.

Changes:

  • Add Spark 4.x compatibility in authz plan handling (e.g., AlterColumns, CommandResult unwrapping, ShowNamespacesCommand, clearing leaked authz tags).
  • Adapt authorization test suites for Spark 4.x behavioral changes (Hive-native table creation for LOAD DATA, ANSI-mode masking workaround, Paimon/Hudi compatibility adjustments).
  • Update Maven profiles/dependencies for Spark 4.0/4.1 (Delta/Hudi/Paimon versions and artifacts) and include authz in code coverage aggregation.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pom.xml Updates Spark 4.0/4.1 profile dependency coordinates (Delta/Hudi/Paimon) and test tag exclusions.
extensions/spark/kyuubi-spark-authz/src/test/scala/org/apache/kyuubi/plugin/spark/authz/SparkSessionProvider.scala Makes extraSparkConf overridable as a method to support layered config overrides.
extensions/spark/kyuubi-spark-authz/src/test/scala/org/apache/kyuubi/plugin/spark/authz/ranger/RangerSparkExtensionSuite.scala Adapts Hive catalog tests for Spark 4.x LOAD DATA + error message differences.
extensions/spark/kyuubi-spark-authz/src/test/scala/org/apache/kyuubi/plugin/spark/authz/ranger/PaimonCatalogRangerSparkExtensionSuite.scala Adjusts Paimon suite support gating and Spark 4.x result/type handling.
extensions/spark/kyuubi-spark-authz/src/test/scala/org/apache/kyuubi/plugin/spark/authz/ranger/datamasking/DataMaskingTestBase.scala Disables ANSI mode in masking tests to avoid Spark 4.0 coercion/runtime failures.
extensions/spark/kyuubi-spark-authz/src/test/scala/org/apache/kyuubi/plugin/spark/authz/ranger/datamasking/DataMaskingForJDBCV2Suite.scala Layers JDBC v2 catalog config on top of the shared masking SparkConf.
extensions/spark/kyuubi-spark-authz/src/test/scala/org/apache/kyuubi/plugin/spark/authz/ranger/datamasking/DataMaskingForIcebergSuite.scala Layers Iceberg catalog config on top of the shared masking SparkConf.
extensions/spark/kyuubi-spark-authz/src/test/scala/org/apache/kyuubi/plugin/spark/authz/gen/TableCommands.scala Adds Spark 4.0 AlterColumns command spec for generated command coverage.
extensions/spark/kyuubi-spark-authz/src/test/scala/org/apache/kyuubi/plugin/spark/authz/gen/HudiCommands.scala Adds fallback table descriptor to handle Hudi field renames across versions.
extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/serde/tableExtractors.scala Introduces HoodieCatalogTableTableExtractor for stable Hudi catalog table extraction.
extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/serde/package.scala Adds CommandResult unwrapping for Spark 4.0 eager CALL execution behavior.
extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/rule/rowfilter/RuleReplaceShowObjectCommands.scala Matches Spark 4.1 ShowNamespacesCommand for namespace row-filtering placeholder insertion.
extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/rule/rowfilter/FilterDataSourceV2Strategy.scala Matches Spark 4.1 ShowNamespacesCommand for filtered namespace exec planning.
extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/rule/Authorization.scala Adds a resolution rule to clear leaked authz tags from cached catalog nodes.
extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/ranger/RangerSparkExtension.scala Injects RuleClearAuthzTag before authz-related resolution rules.
extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/PrivilegesBuilder.scala Unwraps CommandResult and expands v2 command dispatch for Spark 4.0.
extensions/spark/kyuubi-spark-authz/src/main/resources/table_command_spec.json Adds Spark 4.0 AlterColumns spec and Hudi fallback table extraction field mapping.
extensions/spark/kyuubi-spark-authz/src/main/resources/META-INF/services/org.apache.kyuubi.plugin.spark.authz.serde.TableExtractor Registers the new Hudi catalog-table extractor implementation.
dev/kyuubi-codecov/pom.xml Adds kyuubi-spark-authz module to code coverage dependency aggregation.
Comments suppressed due to low confidence (1)

extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/PrivilegesBuilder.scala:308

  • RuleReplaceShowObjectCommands/FilterDataSourceV2Strategy now wrap both ShowNamespaces and ShowNamespacesCommand, but PrivilegesBuilder.build still only recognizes ShowNamespaces. On Spark 4.1 this will likely classify SHOW DATABASES row-filtering plans as a generic QUERY, which can bypass/alter the intended authorization path for namespace listing.
      case ObjectFilterPlaceHolder(child) if child.nodeName == "ShowTables" =>
        OperationType.SHOWTABLES
      case ObjectFilterPlaceHolder(child) if child.nodeName == "ShowNamespaces" =>
        OperationType.SHOWDATABASES

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pom.xml
Comment on lines +2103 to +2107
<hudi.version>1.2.0</hudi.version>
<!-- TODO: update once Paimon support Spark 4.1. -->
<paimon.version>1.4.2</paimon.version>
<paimon.artifact>paimon-spark-4.0_${scala.binary.version}</paimon.artifact>
<maven.plugin.scalatest.exclude.tags>org.scalatest.tags.Slow,org.apache.kyuubi.tags.PaimonTest</maven.plugin.scalatest.exclude.tags>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here keeps Paimon disabled intentionally because Paimon does not provide Spark 4.1 compatible jars

Comment on lines 556 to 560
val changingColumnTypeSql =
s"""
|ALTER TABLE $catalogV2.$namespace1.$table1
|ALTER COLUMN id TYPE DOUBLE
|ALTER COLUMN name TYPE STRING
|""".stripMargin

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0530e9d — the test now uses a dedicated table schema (id int, age int) and alters the non-PK age column from INT to BIGINT, which is a real type change that meaningfully exercises the ALTER COLUMN privilege path.

@pan3793 pan3793 changed the title Authz spark 4.1 v2 Authz supports Spark 4.0 and 4.1 Jul 10, 2026
pan3793 added 3 commits July 10, 2026 15:35
The test used ALTER COLUMN name TYPE STRING, which is a no-op since
name is already string. Use a dedicated table schema (id int, age int)
and alter the non-PK age column from INT to BIGINT to meaningfully
exercise the ALTER COLUMN privilege path.

Also prefer doAs(admin) { ... } over doAs(admin, { ... }).
// inputs (e.g., literals), replacing them with their results and bypassing permission checks.
v1.injectCheckRule(RuleFunctionAuthorization)
// Clear leaked authz tags from cached catalog nodes before any authz resolution rule runs.
v1.injectResolutionRule(_ => RuleClearAuthzTag)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resolution rules will apply plan with fixedPoint iterations, so the RuleClearAuthzTag rule will always cleap up the last round injected tag by all authz rules. Is it expected ? According to the comment, do you want inject rule in PostHocResolution ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right, moved to injectPostHocResolutionRule

BTW, without RuleClearAuthzTag, two permanent-view tests fail on Spark 4.0:

  1. [KYUUBI #5472] Permanent View should pass column when child plan no output
  2. [KYUUBI #5503][AUTHZ] Check plan auth checked should not set tag to all child nodes

@pan3793 pan3793 self-assigned this Jul 10, 2026
@pan3793 pan3793 added this to the v1.12.0 milestone Jul 10, 2026
@pan3793 pan3793 closed this in e532738 Jul 10, 2026
@pan3793

pan3793 commented Jul 10, 2026

Copy link
Copy Markdown
Member Author

merged to master

@pan3793
pan3793 deleted the authz-spark-4.1-v2 branch July 14, 2026 07:35
pan3793 added a commit to pan3793/kyuubi that referenced this pull request Jul 15, 2026
Master apache#7552 already disables ANSI mode in DataMaskingTestBase.extraSparkConf,
so the duplicate in SparkSessionProvider is unnecessary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind:build kind:infra license, community building, project builds, asf infra related, etc. module:authz module:extensions module:spark

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants