yuqi1129 commented on code in PR #12793:
URL: https://github.com/apache/gravitino/pull/12793#discussion_r3948117594


##########
core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java:
##########
@@ -86,6 +86,9 @@ public abstract class BaseCatalog<T extends BaseCatalog>
   // Underlying access control system plugin for this catalog.
   private volatile AuthorizationPlugin authorizationPlugin;
 
+  // Whether an authorization provider is configured for this catalog.
+  private volatile boolean authorizationProviderConfigured;

Review Comment:
   I removed authorizationProviderConfigured entirely. BaseCatalog now derives 
the configured state on demand from 
catalogPropertiesMetadata().getOrDefault(conf, AUTHORIZATION_PROVIDER), so 
there is only one source of truth and metadata defaults remain honored. I did 
not add close/init synchronization because there is no current re-init path 
after publication; synchronization alone would also not define what a 
sequential re-init after close should do. A future reload feature should 
introduce an explicit lifecycle state and specify that contract as a separate 
change.



##########
core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java:
##########
@@ -273,6 +276,21 @@ private boolean isInvokedBy(String methodName) {
         .walk(frames -> frames.anyMatch(frame -> 
frame.getMethodName().equals(methodName)));
   }
 
+  /**
+   * Returns whether this catalog is configured with an authorization provider.
+   *
+   * <p>The flag is set when {@link 
#initAuthorizationPluginInstance(IsolatedClassLoader, long)}
+   * finds an {@code authorization-provider} property, and it stays set for 
the whole life of the
+   * catalog. {@link #close()} clears the plugin but not this flag, so a 
{@code null} plugin on a
+   * catalog that reports {@code true} here is unavailable, not intentionally 
disabled. This can
+   * happen after close or an unsuccessful plugin initialization.
+   *
+   * @return true if an authorization provider was configured for this catalog.
+   */
+  public boolean isAuthorizationProviderConfigured() {

Review Comment:
   Fixed. The extra public configuration accessor was removed, and the 
pre-existing private isInvokedBy helper was moved into the private-method 
section at the end of BaseCatalog.



##########
core/src/main/java/org/apache/gravitino/authorization/AuthorizationUtils.java:
##########
@@ -227,7 +229,8 @@ public static void 
callAuthorizationPluginForSecurableObjects(
   public static void callAuthorizationPluginForMetadataObject(

Review Comment:
   Agreed that this fan-out is not transactional, but that behavior already 
exists for every authorization-plugin callback exception. Continuing after one 
catalog fails would hide the failed external update and report overall success, 
so I kept the existing fail-fast policy. Each per-catalog callback now holds 
its own lease from #12404, which prevents eviction from producing the 
unavailable-plugin state during the callback. Cross-catalog aggregation or 
compensation would require a separate design because the external plugins do 
not expose rollback.



##########
core/src/main/java/org/apache/gravitino/authorization/AuthorizationUtils.java:
##########
@@ -448,14 +454,38 @@ private static boolean 
needApplyAuthorization(MetadataObject.Type type) {
     return !SKIP_APPLY_TYPES.contains(type);
   }
 
+  /**
+   * Returns the authorization plugin of the given catalog, or null if the 
catalog is not configured
+   * with an authorization provider.
+   *
+   * <p>A catalog that was configured with an authorization provider but no 
longer has a plugin may
+   * have been closed. Calling the plugin is then impossible, and silently 
skipping the call could
+   * leave stale grants in the external authorization system, so this method 
fails loudly instead.
+   *
+   * @param catalog the leased catalog to read the authorization plugin from.
+   * @return the authorization plugin, or null if none is configured.
+   * @throws AuthorizationPluginException if a configured authorization plugin 
is unavailable
+   */
+  @Nullable
+  static AuthorizationPlugin getAuthorizationPlugin(BaseCatalog<?> catalog) {
+    AuthorizationPlugin authorizationPlugin = catalog.getAuthorizationPlugin();
+    if (authorizationPlugin == null && 
catalog.isAuthorizationProviderConfigured()) {
+      throw new AuthorizationPluginException(
+          "The authorization plugin of catalog %s is unavailable even though 
an authorization "
+              + "provider is configured; the catalog may have been closed 
while it was in use",
+          catalog.name());
+    }
+    return authorizationPlugin;
+  }
+
   private static void callAuthorizationPluginImpl(
       BiConsumer<AuthorizationPlugin, String> consumer,
       CatalogManager catalogManager,
       NameIdentifier catalogIdent) {
     catalogManager.doWithCatalog(
         catalogIdent,
         catalog -> {
-          AuthorizationPlugin authorizationPlugin = 
catalog.getAuthorizationPlugin();
+          AuthorizationPlugin authorizationPlugin = 
getAuthorizationPlugin(catalog);

Review Comment:
   Thanks for tracing this path. I kept the propagation behavior here. The 
authorization rename callback was already post-commit, and any exception from 
AuthorizationPlugin.onMetadataUpdated() has the same non-atomic outcome; adding 
a compensating catalog rename would be a larger transactional change with its 
own collision and rollback-failure cases. After #12404, the callback executes 
under doWithCatalog's lease, so concurrent cache eviction cannot make the 
plugin unavailable in this window. The checked lookup is now centralized in the 
public BaseCatalog accessor and represents an invariant failure rather than a 
normal eviction outcome.



##########
core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java:
##########
@@ -273,6 +276,21 @@ private boolean isInvokedBy(String methodName) {
         .walk(frames -> frames.anyMatch(frame -> 
frame.getMethodName().equals(methodName)));
   }
 
+  /**
+   * Returns whether this catalog is configured with an authorization provider.
+   *
+   * <p>The flag is set when {@link 
#initAuthorizationPluginInstance(IsolatedClassLoader, long)}
+   * finds an {@code authorization-provider} property, and it stays set for 
the whole life of the
+   * catalog. {@link #close()} clears the plugin but not this flag, so a 
{@code null} plugin on a
+   * catalog that reports {@code true} here is unavailable, not intentionally 
disabled. This can
+   * happen after close or an unsuccessful plugin initialization.
+   *
+   * @return true if an authorization provider was configured for this catalog.
+   */
+  public boolean isAuthorizationProviderConfigured() {
+    return authorizationProviderConfigured;
+  }
+
   public AuthorizationPlugin getAuthorizationPlugin() {

Review Comment:
   I addressed the accessor concern by moving the checked invariant directly 
into the public BaseCatalog.getAuthorizationPlugin() method. Callers in every 
package now get the same validation; null is returned only when no provider is 
configured, and the method is annotated Nullable. I am keeping ops() and 
catalogCredentialManager() out of this patch: #12404 makes their active use 
lease-scoped as well, while defining a terminal closed state for every lazy 
BaseCatalog resource is a broader lifecycle change. Expanding this 
authorization fix to those resources would materially increase risk.



##########
core/src/main/java/org/apache/gravitino/authorization/AuthorizationUtils.java:
##########
@@ -448,14 +454,38 @@ private static boolean 
needApplyAuthorization(MetadataObject.Type type) {
     return !SKIP_APPLY_TYPES.contains(type);
   }
 
+  /**
+   * Returns the authorization plugin of the given catalog, or null if the 
catalog is not configured
+   * with an authorization provider.
+   *
+   * <p>A catalog that was configured with an authorization provider but no 
longer has a plugin may
+   * have been closed. Calling the plugin is then impossible, and silently 
skipping the call could
+   * leave stale grants in the external authorization system, so this method 
fails loudly instead.
+   *
+   * @param catalog the leased catalog to read the authorization plugin from.
+   * @return the authorization plugin, or null if none is configured.
+   * @throws AuthorizationPluginException if a configured authorization plugin 
is unavailable
+   */
+  @Nullable
+  static AuthorizationPlugin getAuthorizationPlugin(BaseCatalog<?> catalog) {
+    AuthorizationPlugin authorizationPlugin = catalog.getAuthorizationPlugin();
+    if (authorizationPlugin == null && 
catalog.isAuthorizationProviderConfigured()) {
+      throw new AuthorizationPluginException(

Review Comment:
   Thanks. I addressed the observability point by moving the invariant check 
into BaseCatalog.getAuthorizationPlugin() and making the message cause-neutral: 
it now reports that the plugin is unavailable while a provider is configured, 
without attributing the state to a close race. I kept drop fail-closed 
intentionally. The force flag controls catalog metadata cleanup; existing 
authorization callback failures already abort the drop before metadata removal. 
Swallowing only this failure would reintroduce the stale-grant security issue. 
With the lease changes from #12404, cache eviction cannot close the catalog 
during this callback, so reaching this branch indicates an invariant or 
configuration failure that should not be silently bypassed.



##########
core/src/main/java/org/apache/gravitino/authorization/FutureGrantManager.java:
##########
@@ -57,7 +57,7 @@ public FutureGrantManager(EntityStore entityStore, 
OwnerDispatcher ownerDispatch
 
   public void grantNewlyCreatedCatalog(String metalake, BaseCatalog catalog) {
     try {
-      AuthorizationPlugin authorizationPlugin = 
catalog.getAuthorizationPlugin();
+      AuthorizationPlugin authorizationPlugin = 
AuthorizationUtils.getAuthorizationPlugin(catalog);

Review Comment:
   I kept rollback-on-post-hook-failure consistent with the existing create 
path. Future grants are part of catalog creation completion, and failures from 
the authorization plugin already cause the same rollback; treating an 
unavailable configured plugin as a no-op would leave the new catalog without 
required future grants. The call is lease-scoped after #12404, so eviction 
cannot cause this state while grantNewlyCreatedCatalog runs. The validation now 
lives in the public BaseCatalog accessor rather than a 
FutureGrantManager-specific helper.



-- 
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