Repository navigation
Conversation
…t_partitions_with_specs get_partitions_with_specs skipped FilterUtils.checkDbAndTableFilters - the table-level metastore filter hook check every regular partition listing RPC performs upfront in GetPartitionsHandler.authorizeTableForPartitionMetadata(). With metastore.server.filter.enabled=true and a filter hook that hides the table, the regular listing RPCs fail fast with NoSuchObjectException while get_partitions_with_specs still fetched all matching partitions and only applied partition-level filtering afterwards. Perform the same check (on normalized identifiers, mirroring GetPartitionsHandler.beforeExecute()) right after the PreReadTableEvent, so authorization ordering is preserved: the pre-event listeners still run first. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
|
cc @dengzhhu653 |
| firePreEvent(new PreReadTableEvent(table, this)); | ||
| // Perform the same table-level filter hook check the regular partition listing RPCs | ||
| // perform in GetPartitionsHandler.authorizeTableForPartitionMetadata() | ||
| FilterUtils.checkDbAndTableFilters(isServerFilterEnabled, filterHook, normalizeIdentifier(catName), |
There was a problem hiding this comment.
This is not needed I think, as the PreReadTableEvent should have checked the permission, fire another check looks redundant. As the GetPartitionsHandler.authorizeTableForPartitionMetadata(), I think we can remove it, what do you think?
There was a problem hiding this comment.
Thanks for looking at this! I don't think it's redundant — PreReadTableEvent and FilterUtils.checkDbAndTableFilters are two separate mechanisms:
firePreEvent(new PreReadTableEvent(...))triggers pre-event listeners likeAuthorizationPreEventListener, which does privilege-based authorization (throwsAuthorizationExceptionif the caller lacksSELECT).FilterUtils.checkDbAndTableFilterscalls the configuredMetaStoreFilterHook(e.g. Ranger) for visibility filtering — it hides objects the caller shouldn't even know exist, by throwingNoSuchObjectException.
These aren't substitutes for each other, and the existing GetPartitionsHandler.beforeExecute() already calls both back-to-back (firePreEvent then authorizeTableForPartitionMetadata()), so this PR is just matching that established pattern rather than introducing a new one.
Concrete example: say Ranger is configured as the filter hook (metastore.server.filter.enabled=true), and user bob has no SELECT grant on sales.transactions.
- Calling
get_partitionstoday:authorizeTableForPartitionMetadata()filters the table name via the Ranger hook, gets an empty result, and fails fast withNoSuchObjectException— before touching any partition data. - Calling
get_partitions_with_specsbefore this fix: HMS fetches theTableobject, firesPreReadTableEvent(a no-op if no legacyAuthorizationPreEventListeneris configured, which is common in Ranger-only setups), then goes ahead and fetches all matching partitions from the RawStore, and only filters them out afterward at the partition level.bobgets back an empty list instead ofNoSuchObjectException— inconsistent with every other partition-listing RPC, and wasteful if the table has a large number of partitions.
This PR closes that gap by performing the same table-level check get_partitions already does, right after the PreReadTableEvent, so authorization ordering is preserved (pre-event listeners still run first) but the filter hook now also gets a chance to fail fast.
The test added in TestFilterHooks.testFilterForPartition covers exactly this: with a blocking filter hook, it asserts getPartitionsWithSpecs() throws NoSuchObjectException at the server side — which would fail if this check were removed.
There was a problem hiding this comment.
One more reason this consistency matters: Apache Spark is about to start relying on getPartitionsWithSpecs more heavily. See apache/spark#58984, which switches write (InsertIntoHadoopFsRelationCommand), truncate (TruncateTableCommand), repair (RepairTableCommand), and metadata-only query call sites to a new listPartitionLocations API backed by this exact RPC (Shim_v4_0 on Hive 4+).
Those are write/DDL-adjacent code paths, not just read-only listing — so it's important that getPartitionsWithSpecs enforces the same table-level filter-hook check as every other partition-listing RPC. If this RPC behaved differently (e.g. silently returning an empty/partial result instead of failing with NoSuchObjectException when the caller shouldn't even see the table), Spark callers built on top of it could end up with inconsistent or unsafe behavior for users who are supposed to be filtered out. Keeping this check in place is what lets downstream consumers like Spark adopt the API with confidence that it behaves the same as the rest of the partition-listing surface.
There was a problem hiding this comment.
To get a full Ranger support, HMS should enable both the filter hook and pre-event listener, losing either of them can lead to the security gaps. The HiveMetaStoreAuthorizer is an example for both reacting to the pre-event and filtering the result based on authorization checks.
If a table passed on pre-event check, it doesn't need to check the filter hook again, as it's always green in such case.
The filter hooks is more for filtering the result before responding it to client, other than validating the request input.



JIRA: HIVE-30195 (umbrella epic: HIVE-19715 · related: HIVE-30196 — same class of gap, the limit-check enforcement in the same RPC, handled in a separate PR)
Problem
get_partitions_with_specs(introduced by HIVE-20306) skippedFilterUtils.checkDbAndTableFilters— the table-level metastore filter hook check that every regular partition listing RPC performs upfront inGetPartitionsHandler.beforeExecute()→authorizeTableForPartitionMetadata().With
metastore.server.filter.enabled=trueand a filter hook that hides the table from the caller (server-side result filtering, e.g. masking), the regular listing RPCs (get_partitions,get_partitions_by_names,get_partitions_with_auth,get_partitions_by_filter,get_partitions_by_expr,get_partitions_ps,get_partitions_ps_with_auth) fail fast withNoSuchObjectException, whileget_partitions_with_specsstill fetched all matching partitions and only applied partition-level filtering (FilterUtils.filterPartitionsIfEnabled) afterwards — whether rows are exposed depends on the hook'sfilterPartitionsimplementation.Note: deployments that configure
HiveMetaStoreAuthorizeras a pre-event listener are not affected — authorization is enforced by the pre-event; this gap affects the filter-hook (visibility/masking) mechanism specifically.What this change does
Adds the missing check in
HMSHandler.get_partitions_with_specs, right afterfirePreEvent(new PreReadTableEvent(table, this)), mirroringGetPartitionsHandler.beforeExecute()(including identifier normalization):Authorization ordering is preserved: the pre-event listeners still run before the filter hook check, and the partition-level post-fetch filtering is unchanged.
Behavior change (only when
metastore.server.filter.enabled=true; default is unchanged)When the filter hook filters out the database or the table,
get_partitions_with_specsnow fails fast withNoSuchObjectException, matching every regular partition listing RPC. Previously it fetched all matching partitions and relied on the hook's post-fetchfilterPartitionsto trim the response.Tests
TestFilterHooks.testFilterForPartition(server-side branch): added thegetPartitionsWithSpecsassertion next to the existinggetPartitionsByNamesone — with the filter hook blocking the table, the RPC must fail withNoSuchObjectExceptionlike its siblings. Verified red→green: the new assertion fails without the fix (getPartitionsWithSpecs() should fail with blocking mode at server side) and passes with it.Full verification on this branch:
TestFilterHooks4/4,TestHmsServerAuthorization5/5 (authorization failure still precedes the filter check),TestGetPartitionsUsingProjectionAndFilterSpecs25/25,TestRemoteHiveMetaStore#testGetPartitionsWithSpecandTestEmbeddedHiveMetaStore#testGetPartitionsWithSpecpass.