blob: e5bd2e99e5216d9337ab72aa29030947a9b90d38 [file]
# 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.
"""Authorization helpers shared by scan and read entry points."""
from pypaimon.read.push_down_utils import predicate_field_names
from pypaimon.read.query_auth_split import query_auth_columns, resolve_auth_result
from pypaimon.table.special_fields import SpecialFields
def latest_auth_fields(table):
"""The table's current columns, re-read so an alter under a long-lived handle fails closed.
The catalog answers when it produced the table: a REST data token need not reach the schema
directory, which the scanners avoid for the same reason.
"""
environment = getattr(table, "catalog_environment", None)
loader = getattr(environment, "catalog_loader", None)
if loader is not None and environment.identifier is not None:
return loader.load().get_table(environment.identifier).fields
latest = table.schema_manager.latest()
return latest.fields if latest is not None else table.fields
def validate_auth_rules(table, auth_result, latest_fields=None):
"""Refuse the read when the rules cannot be bound to the columns they name.
The reader calls this too, so a split that never met a validating scan is held to it.
"""
if not auth_result.has_restrictions:
return table.fields
if latest_fields is None:
latest_fields = latest_auth_fields(table)
auth_result.validate_against_schema(latest_fields)
auth_result.validate_readable_without_rename(latest_fields, table.fields)
return latest_fields
def _authorization_fields(table):
fields = table.fields
if table.options.row_tracking_enabled():
fields = SpecialFields.row_type_with_row_tracking(fields)
return {field.name for field in fields}
def authorization_columns(table, read_type, predicate=None, partition_predicate=None):
return query_auth_columns(read_type, predicate, partition_predicate, _authorization_fields(table))
def authorize(table, query_auth_fn, read_type, predicate=None, partition_predicate=None):
if query_auth_fn is None:
return None
auth_result = resolve_auth_result(query_auth_fn, read_type, predicate, partition_predicate,
_authorization_fields(table))
if auth_result is not None:
validate_auth_rules(table, auth_result)
if partition_predicate is not None and auth_result.column_masking:
if predicate_field_names(partition_predicate) & set(auth_result.column_masking):
raise ValueError("Explicit partition filters cannot reference masked columns")
return auth_result