lasdf1234 commented on code in PR #14482:
URL: https://github.com/apache/gravitino/pull/14482#discussion_r4461566356


##########
server-common/src/main/java/org/apache/gravitino/server/web/filter/BaseMetadataAuthorizationMethodInterceptor.java:
##########
@@ -237,6 +238,45 @@ protected boolean 
shouldSkipExpressionEvaluation(AuthorizationTarget target) {
     return false;
   }
 
+  /**
+   * Returns whether {@link AuthorizationExpression#secondaryExpression()} 
should replace the
+   * primary expression for this request.
+   *
+   * <p>Protocols that understand an {@link ExpressionCondition} (for example 
{@link
+   * ExpressionCondition#OVERWRITE}) override this hook. The default never 
selects the secondary
+   * expression.
+   *
+   * @param condition secondary expression condition from the method annotation
+   * @param method invoked protocol method
+   * @param parameters invoked method parameters
+   * @param args invoked method arguments
+   * @return {@code true} to evaluate the secondary expression
+   */
+  protected boolean shouldUseSecondaryExpression(
+      ExpressionCondition condition, Method method, Parameter[] parameters, 
Object[] args) {
+    return false;
+  }
+
+  /**
+   * Returns the entity type to authorize against when the secondary 
expression is selected.
+   *
+   * <p>Defaults to the primary {@link AuthorizationTarget#entityType()}. 
Protocols may return a
+   * different type when the secondary expression addresses a deeper object 
that a request-specific
+   * handler added to {@code nameIdentifierMap} (for example TABLE for an 
overwrite of a named
+   * table).
+   *
+   * @param annotation authorization annotation on the invoked method
+   * @param primaryTarget authorization target resolved from the primary 
annotation fields
+   * @param nameIdentifierMap identifiers after request-specific handlers have 
run
+   * @return entity type used for secondary expression evaluation
+   */
+  protected Entity.EntityType entityTypeForSecondaryExpression(

Review Comment:
   Thanks — done in 2630649f8.
   
   `shouldUseSecondaryExpression()` / `entityTypeForSecondaryExpression()` are 
gone. `RegisterTableAuthzHandler` now evaluates the modify-table expression on 
overwrite, returns `authorizationCompleted()` for that path, and exposes the 
effective target via `entityType(EntityType defaultType)`, matching the Lance 
overwrite handler pattern. `ExpressionCondition.OVERWRITE` was also removed.



##########
iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/dispatcher/RegisterTableSourceResolver.java:
##########
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+package org.apache.gravitino.iceberg.service.dispatcher;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
+import org.apache.iceberg.TableMetadata;
+import org.apache.iceberg.catalog.Namespace;
+import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.exceptions.NoSuchTableException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Resolves live catalog tables that share a register-table metadata file.
+ *
+ * <p>This class only discovers identities. Privilege checks belong in the 
Iceberg metadata
+ * authorization interceptor handlers.
+ */
+public final class RegisterTableSourceResolver {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(RegisterTableSourceResolver.class);
+
+  private RegisterTableSourceResolver() {}
+
+  /**
+   * Finds tables other than {@code target} that share the given metadata 
file, UUID, or table
+   * location.
+   *
+   * @param catalogWrapper catalog to scan
+   * @param target register destination table
+   * @param metadataLocation metadata file from the register request
+   * @param metadata parsed metadata at that location
+   * @return matching live table identities
+   */
+  public static List<TableIdentifier> findSourceTables(
+      IcebergCatalogWrapper catalogWrapper,
+      TableIdentifier target,
+      String metadataLocation,
+      TableMetadata metadata) {
+    List<TableIdentifier> matches = new ArrayList<>();
+    String uuid = metadata.uuid();
+    String tableLocation = normalizePath(metadata.location());
+    String normalizedMeta = normalizePath(metadataLocation);
+    collectMatchingTables(
+        catalogWrapper, Namespace.empty(), target, uuid, tableLocation, 
normalizedMeta, matches);
+    return matches;
+  }
+
+  private static void collectMatchingTables(
+      IcebergCatalogWrapper catalogWrapper,
+      Namespace parent,
+      TableIdentifier target,
+      String uuid,
+      String tableLocation,
+      String metadataLocation,
+      List<TableIdentifier> matches) {
+    for (Namespace child : catalogWrapper.listNamespace(parent).namespaces()) {
+      collectMatchingTables(
+          catalogWrapper, child, target, uuid, tableLocation, 
metadataLocation, matches);
+    }
+    for (TableIdentifier identifier : 
catalogWrapper.listTable(parent).identifiers()) {

Review Comment:
   Thanks — fixed in 4cf351bb7.
   
   `collectMatchingTables()` now skips `listTable` at `Namespace.empty()` 
(tables always live under a named namespace), so Hive/JDBC catalogs that reject 
root table listing no longer fail during source resolution. The unit test was 
updated to assert root listing is not invoked.



##########
iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/dispatcher/RegisterTableSourceResolver.java:
##########
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+package org.apache.gravitino.iceberg.service.dispatcher;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
+import org.apache.iceberg.TableMetadata;
+import org.apache.iceberg.catalog.Namespace;
+import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.exceptions.NoSuchTableException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Resolves live catalog tables that share a register-table metadata file.
+ *
+ * <p>This class only discovers identities. Privilege checks belong in the 
Iceberg metadata
+ * authorization interceptor handlers.
+ */
+public final class RegisterTableSourceResolver {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(RegisterTableSourceResolver.class);
+
+  private RegisterTableSourceResolver() {}
+
+  /**
+   * Finds tables other than {@code target} that share the given metadata 
file, UUID, or table
+   * location.
+   *
+   * @param catalogWrapper catalog to scan
+   * @param target register destination table
+   * @param metadataLocation metadata file from the register request
+   * @param metadata parsed metadata at that location
+   * @return matching live table identities
+   */
+  public static List<TableIdentifier> findSourceTables(
+      IcebergCatalogWrapper catalogWrapper,
+      TableIdentifier target,
+      String metadataLocation,
+      TableMetadata metadata) {
+    List<TableIdentifier> matches = new ArrayList<>();
+    String uuid = metadata.uuid();
+    String tableLocation = normalizePath(metadata.location());
+    String normalizedMeta = normalizePath(metadataLocation);
+    collectMatchingTables(
+        catalogWrapper, Namespace.empty(), target, uuid, tableLocation, 
normalizedMeta, matches);
+    return matches;
+  }
+
+  private static void collectMatchingTables(
+      IcebergCatalogWrapper catalogWrapper,
+      Namespace parent,
+      TableIdentifier target,
+      String uuid,
+      String tableLocation,
+      String metadataLocation,
+      List<TableIdentifier> matches) {
+    for (Namespace child : catalogWrapper.listNamespace(parent).namespaces()) {
+      collectMatchingTables(
+          catalogWrapper, child, target, uuid, tableLocation, 
metadataLocation, matches);
+    }
+    for (TableIdentifier identifier : 
catalogWrapper.listTable(parent).identifiers()) {
+      if (identifier.equals(target)) {
+        continue;
+      }
+      if (matchesSource(catalogWrapper, identifier, uuid, tableLocation, 
metadataLocation)) {
+        matches.add(identifier);
+      }
+    }
+  }
+
+  private static boolean matchesSource(
+      IcebergCatalogWrapper catalogWrapper,
+      TableIdentifier identifier,
+      String uuid,
+      String tableLocation,
+      String metadataLocation) {
+    Optional<String> currentMeta = 
catalogWrapper.getTableMetadataLocation(identifier);
+    if (currentMeta.isPresent() && 
normalizePath(currentMeta.get()).equals(metadataLocation)) {
+      return true;
+    }
+    try {
+      TableMetadata existing = catalogWrapper.loadTableMetadata(identifier);
+      if (uuid != null && uuid.equals(existing.uuid())) {
+        return true;
+      }
+      return tableLocation != null
+          && !tableLocation.isEmpty()
+          && tableLocation.equals(normalizePath(existing.location()));
+    } catch (NoSuchTableException e) {
+      return false;
+    } catch (RuntimeException e) {

Review Comment:
   Thanks — fixed in 2630649f8.
   
   `matchesSource()` now only treats a confirmed `NoSuchTableException` as 
“unrelated”. Any other failure while loading current metadata propagates, so an 
unreadable live source can no longer be mistaken for orphaned metadata and skip 
source-table authorization. Added a unit test for that path.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to