This is an automated email from the ASF dual-hosted git repository.

liuxun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new f94fcdc94c [#7795] fix(authz): Support check deny privilege (#7800)
f94fcdc94c is described below

commit f94fcdc94c75a9acd7b8dd869b44b955df6622c1
Author: yangyang zhong <[email protected]>
AuthorDate: Wed Jul 30 16:44:37 2025 +0800

    [#7795] fix(authz): Support check deny privilege (#7800)
    
    ### What changes were proposed in this pull request?
    
    Support check deny privilege
    
    ### Why are the changes needed?
    
    Fix: #7795
    
    ### Does this PR introduce _any_ user-facing change?
    
    None
    
    ### How was this patch tested?
    
    Existing metadata authentication for IT and UT
---
 .../test/authorization/TableAuthorizationIT.java   |  12 +-
 .../org/apache/gravitino/auth/AuthConstants.java   |   7 ++
 .../authorization/GravitinoAuthorizer.java         |   6 +
 .../authorization/PassThroughAuthorizer.java       |   9 ++
 .../AuthorizationExpressionConverter.java          |  79 +++++++++---
 .../authorization/jcasbin/JcasbinAuthorizer.java   | 134 +++++++++++++--------
 .../authorization/MockGravitinoAuthorizer.java     |   9 ++
 .../server/web/rest/FilesetOperations.java         |   2 +-
 .../filter/TestGravitinoInterceptionService.java   |   9 ++
 .../TestCatalogAuthorizationExpression.java        |  12 ++
 .../TestFilesetAuthorizationExpression.java        |  64 ++++++++++
 .../TestModelAuthorizationExpression.java          |  83 +++++++++++++
 .../TestSchemaAuthorizationExpression.java         |  30 +++++
 .../TestTableAuthorizationExpression.java          |  52 ++++++++
 .../TestTopicAuthorizationExpression.java          |  56 +++++++++
 15 files changed, 499 insertions(+), 65 deletions(-)

diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/TableAuthorizationIT.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/TableAuthorizationIT.java
index 6042c0c33a..6c9f260594 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/TableAuthorizationIT.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/TableAuthorizationIT.java
@@ -186,9 +186,19 @@ public class TableAuthorizationIT extends 
BaseRestApiAuthorizationIT {
     GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
     gravitinoMetalake.grantPrivilegesToRole(
         role,
-        MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"), 
MetadataObject.Type.TABLE),
+        MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA), 
MetadataObject.Type.SCHEMA),
         ImmutableList.of(Privileges.SelectTable.allow()));
     tableCatalogNormalUser.loadTable(NameIdentifier.of(SCHEMA, "table1"));
+    gravitinoMetalake.grantPrivilegesToRole(
+        role,
+        MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"), 
MetadataObject.Type.TABLE),
+        ImmutableList.of(Privileges.SelectTable.deny()));
+    assertThrows(
+        String.format("Can not access metadata {%s.%s.%s}.", CATALOG, SCHEMA, 
"table1"),
+        RuntimeException.class,
+        () -> {
+          tableCatalogNormalUser.loadTable(NameIdentifier.of(SCHEMA, 
"table1"));
+        });
   }
 
   @Test
diff --git a/common/src/main/java/org/apache/gravitino/auth/AuthConstants.java 
b/common/src/main/java/org/apache/gravitino/auth/AuthConstants.java
index 46b4e5964f..8750c4c942 100644
--- a/common/src/main/java/org/apache/gravitino/auth/AuthConstants.java
+++ b/common/src/main/java/org/apache/gravitino/auth/AuthConstants.java
@@ -21,6 +21,7 @@ package org.apache.gravitino.auth;
 
 /** Constants used for authentication. */
 public final class AuthConstants {
+
   private AuthConstants() {}
 
   /** The HTTP header used to pass the authentication token. */
@@ -50,6 +51,12 @@ public final class AuthConstants {
   /** SELF authorization expression. */
   public static final String SELF = "SELF";
 
+  /** deny. */
+  public static final String DENY = "deny";
+
+  /** allow. */
+  public static final String ALLOW = "allow";
+
   /**
    * The default name of the attribute that stores the authenticated principal 
in the request.
    *
diff --git 
a/core/src/main/java/org/apache/gravitino/authorization/GravitinoAuthorizer.java
 
b/core/src/main/java/org/apache/gravitino/authorization/GravitinoAuthorizer.java
index 1887ffea08..08bcaa251d 100644
--- 
a/core/src/main/java/org/apache/gravitino/authorization/GravitinoAuthorizer.java
+++ 
b/core/src/main/java/org/apache/gravitino/authorization/GravitinoAuthorizer.java
@@ -50,6 +50,12 @@ public interface GravitinoAuthorizer extends Closeable {
       MetadataObject metadataObject,
       Privilege.Name privilege);
 
+  boolean deny(
+      Principal principal,
+      String metalake,
+      MetadataObject metadataObject,
+      Privilege.Name privilege);
+
   /**
    * Determine whether the user is the Owner of a certain metadata object.
    *
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/PassThroughAuthorizer.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/PassThroughAuthorizer.java
index f28e402128..3288f92b3b 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/PassThroughAuthorizer.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/PassThroughAuthorizer.java
@@ -43,6 +43,15 @@ public class PassThroughAuthorizer implements 
GravitinoAuthorizer {
     return true;
   }
 
+  @Override
+  public boolean deny(
+      Principal principal,
+      String metalake,
+      MetadataObject metadataObject,
+      Privilege.Name privilege) {
+    return false;
+  }
+
   @Override
   public boolean isOwner(Principal principal, String metalake, MetadataObject 
metadataObject) {
     return true;
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java
index b72d904761..a090edbb77 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java
@@ -49,6 +49,8 @@ public class AuthorizationExpressionConverter {
    */
   public static final String CAN_SET_OWNER = "CAN_SET_OWNER";
 
+  private static final String DENY_PREFIX = "DENY_";
+
   /**
    * The EXPRESSION_CACHE caches the result of converting authorization 
expressions into an OGNL
    * expression.
@@ -82,6 +84,13 @@ public class AuthorizationExpressionConverter {
             String replacement;
             if (AuthConstants.OWNER.equals(privilegeOrExpression)) {
               replacement = 
String.format("authorizer.isOwner(principal,METALAKE_NAME,%s)", type);
+            } else if (privilegeOrExpression.startsWith(DENY_PREFIX)) {
+              String privilege = privilegeOrExpression.substring(5);
+              replacement =
+                  String.format(
+                      "authorizer.deny(principal,METALAKE_NAME,%s,"
+                          + 
"@org.apache.gravitino.authorization.Privilege\\$Name@%s)",
+                      type, privilege);
             } else if (AuthConstants.SELF.equals(privilegeOrExpression)) {
               replacement =
                   String.format(
@@ -94,6 +103,7 @@ public class AuthorizationExpressionConverter {
                           + 
"@org.apache.gravitino.authorization.Privilege\\$Name@%s)",
                       type, privilegeOrExpression);
             }
+
             matcher.appendReplacement(result, replacement);
           }
           matcher.appendTail(result);
@@ -147,48 +157,87 @@ public class AuthorizationExpressionConverter {
   public static String replaceAnyPrivilege(String expression) {
     expression = expression.replaceAll("SERVICE_ADMIN", 
"authorizer.isServiceAdmin()");
     expression = expression.replaceAll("METALAKE_USER", 
"authorizer.isMetalakeUser(METALAKE_NAME)");
-    expression = expression.replaceAll("ANY_USE_CATALOG", "(ANY(USE_CATALOG, 
METALAKE, CATALOG))");
     expression =
-        expression.replaceAll("ANY_USE_SCHEMA", "(ANY(USE_SCHEMA, METALAKE, 
CATALOG, SCHEMA))");
+        expression.replaceAll(
+            "ANY_USE_CATALOG",
+            "((ANY(USE_CATALOG, METALAKE, CATALOG)) && "
+                + "!(ANY(DENY_USE_CATALOG, METALAKE, CATALOG)))");
+    expression =
+        expression.replaceAll(
+            "ANY_USE_SCHEMA",
+            "((ANY(USE_SCHEMA, METALAKE, CATALOG, SCHEMA)) "
+                + "&& !(ANY(DENY_USE_SCHEMA, METALAKE, CATALOG, SCHEMA)))");
+    expression =
+        expression.replaceAll(
+            "ANY_CREATE_SCHEMA",
+            "((ANY(CREATE_SCHEMA, METALAKE, CATALOG)) "
+                + "&& !(ANY(DENY_CREATE_SCHEMA, METALAKE, CATALOG)))");
     expression =
-        expression.replaceAll("ANY_CREATE_SCHEMA", "(ANY(CREATE_SCHEMA, 
METALAKE, CATALOG))");
+        expression.replaceAll(
+            "ANY_SELECT_TABLE",
+            "((ANY(SELECT_TABLE, METALAKE, CATALOG, SCHEMA, TABLE)) "
+                + "&& !(ANY(DENY_SELECT_TABLE, METALAKE, CATALOG, SCHEMA, 
TABLE)) )");
     expression =
         expression.replaceAll(
-            "ANY_SELECT_TABLE", "(ANY(SELECT_TABLE, METALAKE, CATALOG, SCHEMA, 
TABLE))");
+            "ANY_MODIFY_TABLE",
+            "((ANY(MODIFY_TABLE, METALAKE, CATALOG, SCHEMA, TABLE)) "
+                + "&& !(ANY(DENY_MODIFY_TABLE, METALAKE, CATALOG, SCHEMA, 
TABLE)))");
     expression =
         expression.replaceAll(
-            "ANY_MODIFY_TABLE", "(ANY(MODIFY_TABLE, METALAKE, CATALOG, SCHEMA, 
TABLE))");
+            "ANY_CREATE_TABLE",
+            "((ANY(CREATE_TABLE, METALAKE, CATALOG, SCHEMA, TABLE)) "
+                + "&& !(ANY(DENY_CREATE_TABLE, METALAKE, CATALOG, SCHEMA, 
TABLE)))");
     expression =
         expression.replaceAll(
-            "ANY_CREATE_TABLE", "(ANY(CREATE_TABLE, METALAKE, CATALOG, SCHEMA, 
TABLE))");
+            "ANY_CREATE_FILESET",
+            "((ANY(CREATE_FILESET, METALAKE, CATALOG, SCHEMA, TABLE)) "
+                + "&& !(ANY(DENY_CREATE_FILESET, METALAKE, CATALOG, SCHEMA, 
TABLE)))");
     expression =
         expression.replaceAll(
             "SCHEMA_OWNER_WITH_USE_CATALOG",
-            "SCHEMA::OWNER && (ANY(USE_CATALOG, METALAKE, CATALOG))");
+            "SCHEMA::OWNER && "
+                + "((ANY(USE_CATALOG, METALAKE, CATALOG)) && "
+                + "!(ANY(DENY_USE_CATALOG, METALAKE, CATALOG)))");
     expression =
         expression.replaceAll(
-            "ANY_USE_MODEL", "(ANY(USE_MODEL, METALAKE, CATALOG, SCHEMA, 
MODEL))");
+            "ANY_USE_MODEL",
+            "((ANY(USE_MODEL, METALAKE, CATALOG, SCHEMA, MODEL)) && "
+                + "!(ANY(DENY_USE_MODEL, METALAKE, CATALOG, SCHEMA, MODEL)))");
     expression =
         expression.replaceAll(
             "ANY_CREATE_MODEL_VERSION",
-            "(ANY(CREATE_MODEL_VERSION, METALAKE, CATALOG, SCHEMA, MODEL))");
+            "((ANY(CREATE_MODEL_VERSION, METALAKE, CATALOG, SCHEMA, MODEL)) "
+                + "&& !(ANY(DENY_CREATE_MODEL_VERSION, METALAKE, CATALOG, 
SCHEMA, MODEL)))");
     expression =
-        expression.replaceAll("ANY_CREATE_MODEL", "(ANY(CREATE_MODEL, 
METALAKE, CATALOG, SCHEMA))");
+        expression.replaceAll(
+            "ANY_CREATE_MODEL",
+            "((ANY(CREATE_MODEL, METALAKE, CATALOG, SCHEMA)) "
+                + "&& !(ANY(DENY_CREATE_MODEL, METALAKE, CATALOG, SCHEMA)))");
     expression =
         expression.replaceAll(
-            "ANY_CREATE_TOPIC", "(ANY(CREATE_TOPIC, METALAKE, CATALOG, SCHEMA, 
TOPIC))");
+            "ANY_CREATE_TOPIC",
+            "((ANY(CREATE_TOPIC, METALAKE, CATALOG, SCHEMA, TOPIC)) "
+                + "&& !(ANY(DENY_CREATE_TOPIC, METALAKE, CATALOG, SCHEMA, 
TOPIC)))");
     expression =
         expression.replaceAll(
-            "ANY_PRODUCE_TOPIC", "(ANY(PRODUCE_TOPIC, METALAKE, CATALOG, 
SCHEMA, TOPIC))");
+            "ANY_PRODUCE_TOPIC",
+            "((ANY(PRODUCE_TOPIC, METALAKE, CATALOG, SCHEMA, TOPIC))"
+                + "&& !(ANY(DENY_PRODUCE_TOPIC, METALAKE, CATALOG, SCHEMA, 
TOPIC)))");
     expression =
         expression.replaceAll(
-            "ANY_CONSUME_TOPIC", "(ANY(CONSUME_TOPIC, METALAKE, CATALOG, 
SCHEMA, TOPIC))");
+            "ANY_CONSUME_TOPIC",
+            "((ANY(CONSUME_TOPIC, METALAKE, CATALOG, SCHEMA, TOPIC))"
+                + "&& !(ANY(DENY_CONSUME_TOPIC, METALAKE, CATALOG, SCHEMA, 
TOPIC)))");
     expression =
         expression.replaceAll(
-            "ANY_READ_FILESET", "(ANY(READ_FILESET, METALAKE, CATALOG, SCHEMA, 
FILESET))");
+            "ANY_READ_FILESET",
+            "((ANY(READ_FILESET, METALAKE, CATALOG, SCHEMA, FILESET))"
+                + "&& !(ANY(DENY_READ_FILESET, METALAKE, CATALOG, SCHEMA, 
FILESET)))");
     expression =
         expression.replaceAll(
-            "ANY_WRITE_FILESET", "(ANY(WRITE_FILESET, METALAKE, CATALOG, 
SCHEMA, FILESET))");
+            "ANY_WRITE_FILESET",
+            "((ANY(WRITE_FILESET, METALAKE, CATALOG, SCHEMA, FILESET))"
+                + "&& !(ANY(DENY_WRITE_FILESET, METALAKE, CATALOG, SCHEMA, 
FILESET)))");
     expression =
         expression.replaceAll(
             CAN_SET_OWNER,
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java
index d8f260a638..86a36b9897 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java
@@ -30,8 +30,6 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.gravitino.Config;
-import org.apache.gravitino.Configs;
 import org.apache.gravitino.Entity;
 import org.apache.gravitino.EntityStore;
 import org.apache.gravitino.GravitinoEnv;
@@ -61,9 +59,16 @@ public class JcasbinAuthorizer implements 
GravitinoAuthorizer {
   private static final Logger LOG = 
LoggerFactory.getLogger(JcasbinAuthorizer.class);
 
   /** Jcasbin enforcer is used for metadata authorization. */
-  private Enforcer enforcer;
+  private Enforcer allowEnforcer;
 
-  private final Set<String> serviceAdmins = ConcurrentHashMap.newKeySet();
+  /** Jcasbin deny enforcer is used for metadata authorization. */
+  private Enforcer denyEnforcer;
+
+  /** allow internal authorizer */
+  private InternalAuthorizer allowInternalAuthorizer;
+
+  /** deny internal authorizer */
+  private InternalAuthorizer denyInternalAuthorizer;
 
   /**
    * loadedRoles is used to cache roles that have loaded permissions. When the 
permissions of a role
@@ -73,20 +78,22 @@ public class JcasbinAuthorizer implements 
GravitinoAuthorizer {
 
   @Override
   public void initialize() {
-    try (InputStream modelStream =
-        JcasbinAuthorizer.class.getResourceAsStream("/jcasbin_model.conf")) {
+    allowEnforcer = new SyncedEnforcer(getModel("/jcasbin_model.conf"), new 
GravitinoAdapter());
+    allowInternalAuthorizer = new InternalAuthorizer(allowEnforcer);
+    denyEnforcer = new SyncedEnforcer(getModel("/jcasbin_model.conf"), new 
GravitinoAdapter());
+    denyInternalAuthorizer = new InternalAuthorizer(denyEnforcer);
+  }
+
+  private Model getModel(String modelFilePath) {
+    Model model = new Model();
+    try (InputStream modelStream = 
JcasbinAuthorizer.class.getResourceAsStream(modelFilePath)) {
       Preconditions.checkNotNull(modelStream, "Jcasbin model file can not 
found.");
       String modelData = IOUtils.toString(modelStream, StandardCharsets.UTF_8);
-      Model model = new Model();
       model.loadModelFromText(modelData);
-      enforcer = new SyncedEnforcer(model, new GravitinoAdapter());
-      Config config = GravitinoEnv.getInstance().config();
-      if (config != null) {
-        serviceAdmins.addAll(config.get(Configs.SERVICE_ADMINS));
-      }
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
+    return model;
   }
 
   @Override
@@ -95,12 +102,24 @@ public class JcasbinAuthorizer implements 
GravitinoAuthorizer {
       String metalake,
       MetadataObject metadataObject,
       Privilege.Name privilege) {
-    return authorizeInternal(principal, metalake, metadataObject, 
privilege.name());
+    return allowInternalAuthorizer.authorizeInternal(
+        principal, metalake, metadataObject, privilege.name());
+  }
+
+  @Override
+  public boolean deny(
+      Principal principal,
+      String metalake,
+      MetadataObject metadataObject,
+      Privilege.Name privilege) {
+    return denyInternalAuthorizer.authorizeInternal(
+        principal, metalake, metadataObject, privilege.name());
   }
 
   @Override
   public boolean isOwner(Principal principal, String metalake, MetadataObject 
metadataObject) {
-    return authorizeInternal(principal, metalake, metadataObject, 
AuthConstants.OWNER);
+    return allowInternalAuthorizer.authorizeInternal(
+        principal, metalake, metadataObject, AuthConstants.OWNER);
   }
 
   @Override
@@ -146,7 +165,7 @@ public class JcasbinAuthorizer implements 
GravitinoAuthorizer {
         UserEntity userEntity = getUserEntity(currentUserName, metalake);
         Long userId = userEntity.id();
         loadRolePrivilege(metalake, currentUserName, userId);
-        return enforcer.hasRoleForUser(String.valueOf(userId), 
String.valueOf(roleId));
+        return allowEnforcer.hasRoleForUser(String.valueOf(userId), 
String.valueOf(roleId));
       } catch (Exception e) {
         LOG.warn("can not get user id or role id.", e);
         return false;
@@ -220,7 +239,8 @@ public class JcasbinAuthorizer implements 
GravitinoAuthorizer {
   @Override
   public void handleRolePrivilegeChange(Long roleId) {
     loadedRoles.remove(roleId);
-    enforcer.deleteRole(String.valueOf(roleId));
+    allowEnforcer.deleteRole(String.valueOf(roleId));
+    denyEnforcer.deleteRole(String.valueOf(roleId));
   }
 
   @Override
@@ -234,42 +254,51 @@ public class JcasbinAuthorizer implements 
GravitinoAuthorizer {
             String.valueOf(metadataObject.type()),
             String.valueOf(metadataId),
             AuthConstants.OWNER,
-            "allow");
-    enforcer.removePolicy(policy);
+            AuthConstants.ALLOW);
+    allowEnforcer.removePolicy(policy);
   }
 
   @Override
   public void close() throws IOException {}
 
-  private boolean authorizeInternal(
-      Principal principal, String metalake, MetadataObject metadataObject, 
String privilege) {
-    String username = principal.getName();
-    return loadPrivilegeAndAuthorize(username, metalake, metadataObject, 
privilege);
-  }
+  private class InternalAuthorizer {
 
-  private boolean authorizeByJcasbin(
-      Long userId, MetadataObject metadataObject, Long metadataId, String 
privilege) {
-    return enforcer.enforce(
-        String.valueOf(userId),
-        String.valueOf(metadataObject.type()),
-        String.valueOf(metadataId),
-        privilege);
-  }
+    Enforcer enforcer;
 
-  private boolean loadPrivilegeAndAuthorize(
-      String username, String metalake, MetadataObject metadataObject, String 
privilege) {
-    Long metadataId;
-    Long userId;
-    try {
-      UserEntity userEntity = getUserEntity(username, metalake);
-      userId = userEntity.id();
-      metadataId = MetadataIdConverter.getID(metadataObject, metalake);
-    } catch (Exception e) {
-      LOG.debug("Can not get entity id", e);
-      return false;
+    public InternalAuthorizer(Enforcer enforcer) {
+      this.enforcer = enforcer;
+    }
+
+    private boolean authorizeInternal(
+        Principal principal, String metalake, MetadataObject metadataObject, 
String privilege) {
+      String username = principal.getName();
+      return loadPrivilegeAndAuthorize(username, metalake, metadataObject, 
privilege);
+    }
+
+    private boolean loadPrivilegeAndAuthorize(
+        String username, String metalake, MetadataObject metadataObject, 
String privilege) {
+      Long metadataId;
+      Long userId;
+      try {
+        UserEntity userEntity = getUserEntity(username, metalake);
+        userId = userEntity.id();
+        metadataId = MetadataIdConverter.getID(metadataObject, metalake);
+      } catch (Exception e) {
+        LOG.debug("Can not get entity id", e);
+        return false;
+      }
+      loadPrivilege(metalake, username, userId, metadataObject, metadataId);
+      return authorizeByJcasbin(userId, metadataObject, metadataId, privilege);
+    }
+
+    private boolean authorizeByJcasbin(
+        Long userId, MetadataObject metadataObject, Long metadataId, String 
privilege) {
+      return enforcer.enforce(
+          String.valueOf(userId),
+          String.valueOf(metadataObject.type()),
+          String.valueOf(metadataId),
+          privilege);
     }
-    loadPrivilege(metalake, username, userId, metadataObject, metadataId);
-    return authorizeByJcasbin(userId, metadataObject, metadataId, privilege);
   }
 
   private static UserEntity getUserEntity(String username, String metalake) 
throws IOException {
@@ -317,7 +346,8 @@ public class JcasbinAuthorizer implements 
GravitinoAuthorizer {
       if (loadedRoles.contains(roleId)) {
         continue;
       }
-      enforcer.addRoleForUser(String.valueOf(userId), String.valueOf(roleId));
+      allowEnforcer.addRoleForUser(String.valueOf(userId), 
String.valueOf(roleId));
+      denyEnforcer.addRoleForUser(String.valueOf(userId), 
String.valueOf(roleId));
       loadPolicyByRoleEntity(role);
       loadedRoles.add(roleId);
     }
@@ -343,8 +373,8 @@ public class JcasbinAuthorizer implements 
GravitinoAuthorizer {
                   String.valueOf(metadataObject.type()),
                   String.valueOf(metadataId),
                   AuthConstants.OWNER,
-                  "allow");
-          enforcer.addPolicy(policy);
+                  AuthConstants.ALLOW);
+          allowEnforcer.addPolicy(policy);
         }
       }
     } catch (IOException e) {
@@ -359,7 +389,15 @@ public class JcasbinAuthorizer implements 
GravitinoAuthorizer {
     for (SecurableObject securableObject : securableObjects) {
       for (Privilege privilege : securableObject.privileges()) {
         Privilege.Condition condition = privilege.condition();
-        enforcer.addPolicy(
+        if (AuthConstants.DENY.equalsIgnoreCase(condition.name())) {
+          denyEnforcer.addPolicy(
+              String.valueOf(roleEntity.id()),
+              securableObject.type().name(),
+              String.valueOf(MetadataIdConverter.getID(securableObject, 
metalake)),
+              privilege.name().name().toUpperCase(),
+              AuthConstants.ALLOW);
+        }
+        allowEnforcer.addPolicy(
             String.valueOf(roleEntity.id()),
             securableObject.type().name(),
             String.valueOf(MetadataIdConverter.getID(securableObject, 
metalake)),
diff --git 
a/server-common/src/test/java/org/apache/gravitino/server/authorization/MockGravitinoAuthorizer.java
 
b/server-common/src/test/java/org/apache/gravitino/server/authorization/MockGravitinoAuthorizer.java
index dfe021b6d9..82ca4e3651 100644
--- 
a/server-common/src/test/java/org/apache/gravitino/server/authorization/MockGravitinoAuthorizer.java
+++ 
b/server-common/src/test/java/org/apache/gravitino/server/authorization/MockGravitinoAuthorizer.java
@@ -57,6 +57,15 @@ public class MockGravitinoAuthorizer implements 
GravitinoAuthorizer {
         && privilege == Privilege.Name.SELECT_TABLE;
   }
 
+  @Override
+  public boolean deny(
+      Principal principal,
+      String metalake,
+      MetadataObject metadataObject,
+      Privilege.Name privilege) {
+    return false;
+  }
+
   @Override
   public boolean isOwner(Principal principal, String metalake, MetadataObject 
metadataObject) {
     if (!("tester".equals(principal.getName()) && 
"metalakeWithOwner".equals(metalake))) {
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/rest/FilesetOperations.java
 
b/server/src/main/java/org/apache/gravitino/server/web/rest/FilesetOperations.java
index 1d06e82ea2..68f8ef35ce 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/rest/FilesetOperations.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/rest/FilesetOperations.java
@@ -139,7 +139,7 @@ public class FilesetOperations {
       expression =
           "ANY(OWNER, METALAKE, CATALOG) || "
               + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA && SCHEMA::CREATE_FILESET",
+              + "ANY_USE_CATALOG && ANY_USE_SCHEMA && ANY_CREATE_FILESET",
       accessMetadataType = MetadataObject.Type.FILESET)
   public Response createFileset(
       @PathParam("metalake") @AuthorizationMetadata(type = 
Entity.EntityType.METALAKE)
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
 
b/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
index 02c2cc0477..ba09e09f1d 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
@@ -154,6 +154,15 @@ public class TestGravitinoInterceptionService {
           && privilege == Privilege.Name.USE_CATALOG;
     }
 
+    @Override
+    public boolean deny(
+        Principal principal,
+        String metalake,
+        MetadataObject metadataObject,
+        Privilege.Name privilege) {
+      return false;
+    }
+
     @Override
     public boolean isOwner(Principal principal, String metalake, 
MetadataObject metadataObject) {
       return false;
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestCatalogAuthorizationExpression.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestCatalogAuthorizationExpression.java
index 2cc3851255..6728fea8ee 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestCatalogAuthorizationExpression.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestCatalogAuthorizationExpression.java
@@ -63,6 +63,12 @@ public class TestCatalogAuthorizationExpression {
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::OWNER")));
     
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::CREATE_CATALOG")));
     
assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of("METALAKE::USE_CATALOG", 
"CATALOG::DENY_USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of("METALAKE::DENY_USE_CATALOG", 
"CATALOG::USE_CATALOG")));
   }
 
   @Test
@@ -80,6 +86,12 @@ public class TestCatalogAuthorizationExpression {
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::OWNER")));
     
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::CREATE_CATALOG")));
     
assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of("METALAKE::USE_CATALOG", 
"CATALOG::DENY_USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of("METALAKE::DENY_USE_CATALOG", 
"CATALOG::USE_CATALOG")));
   }
 
   @Test
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestFilesetAuthorizationExpression.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestFilesetAuthorizationExpression.java
index 74f8fd45e5..806a7d0036 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestFilesetAuthorizationExpression.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestFilesetAuthorizationExpression.java
@@ -57,6 +57,24 @@ public class TestFilesetAuthorizationExpression {
         mockEvaluator.getResult(
             ImmutableSet.of(
                 "SCHEMA::CREATE_FILESET", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::DENY_CREATE_FILESET", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::DENY_CREATE_FILESET",
+                "CATALOG::CREATE_FILESET",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::CREATE_FILESET",
+                "CATALOG::DENY_CREATE_FILESET",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
   }
 
   @Test
@@ -87,6 +105,20 @@ public class TestFilesetAuthorizationExpression {
         mockEvaluator.getResult(
             ImmutableSet.of(
                 "SCHEMA::WRITE_FILESET", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::WRITE_FILESET",
+                "CATALOG::DENY_WRITE_FILESET",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::DENY_WRITE_FILESET",
+                "CATALOG::WRITE_FILESET",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
 
     
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::READ_FILESET")));
     
assertFalse(mockEvaluator.getResult(ImmutableSet.of("CATALOG::READ_FILESET")));
@@ -96,6 +128,20 @@ public class TestFilesetAuthorizationExpression {
     assertTrue(
         mockEvaluator.getResult(
             ImmutableSet.of("SCHEMA::READ_FILESET", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::READ_FILESET",
+                "CATALOG::DENY_READ_FILESET",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::DENY_READ_FILESET",
+                "CATALOG::READ_FILESET",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
 
     assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"CATALOG::USE_CATALOG")));
@@ -138,6 +184,20 @@ public class TestFilesetAuthorizationExpression {
         mockEvaluator.getResult(
             ImmutableSet.of(
                 "SCHEMA::WRITE_FILESET", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::WRITE_FILESET",
+                "CATALOG::DENY_WRITE_FILESET",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::DENY_WRITE_FILESET",
+                "CATALOG::WRITE_FILESET",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
 
     assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"CATALOG::USE_CATALOG")));
@@ -179,6 +239,10 @@ public class TestFilesetAuthorizationExpression {
 
     assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER", "CATALOG::USE_CATALOG", 
"METALAKE::DENY_USE_CATALOG")));
     assertTrue(
         mockEvaluator.getResult(
             ImmutableSet.of("SCHEMA::OWNER", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestModelAuthorizationExpression.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestModelAuthorizationExpression.java
index 53b1e91536..b000eebf8e 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestModelAuthorizationExpression.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestModelAuthorizationExpression.java
@@ -64,6 +64,20 @@ public class TestModelAuthorizationExpression {
         mockEvaluator.getResult(
             ImmutableSet.of(
                 "METALAKE::CREATE_MODEL", "METALAKE::USE_SCHEMA", 
"METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::CREATE_MODEL",
+                "CATALOG::DENY_CREATE_MODEL",
+                "METALAKE::USE_SCHEMA",
+                "METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::DENY_CREATE_MODEL",
+                "CATALOG::CREATE_MODEL",
+                "METALAKE::USE_SCHEMA",
+                "METALAKE::USE_CATALOG")));
   }
 
   @Test
@@ -99,6 +113,27 @@ public class TestModelAuthorizationExpression {
         mockEvaluator.getResult(
             ImmutableSet.of(
                 "METALAKE::USE_MODEL", "METALAKE::USE_SCHEMA", 
"METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::USE_MODEL",
+                "CATALOG::DENY_USE_MODEL",
+                "METALAKE::USE_SCHEMA",
+                "METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::USE_MODEL",
+                "CATALOG::DENY_USE_SCHEMA",
+                "METALAKE::USE_SCHEMA",
+                "METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::DENY_USE_MODEL",
+                "CATALOG::USE_MODEL",
+                "METALAKE::USE_SCHEMA",
+                "METALAKE::USE_CATALOG")));
   }
 
   @Test
@@ -141,6 +176,14 @@ public class TestModelAuthorizationExpression {
         mockEvaluator.getResult(
             ImmutableSet.of(
                 "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "MODEL::OWNER",
+                "MODEL::USE_MODEL",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::DENY_USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
   }
 
   @Test
@@ -178,6 +221,14 @@ public class TestModelAuthorizationExpression {
         mockEvaluator.getResult(
             ImmutableSet.of(
                 "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "MODEL::OWNER",
+                "MODEL::USE_MODEL",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::DENY_USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
   }
 
   @Test
@@ -238,6 +289,22 @@ public class TestModelAuthorizationExpression {
                 "MODEL::USE_MODEL",
                 "METALAKE::USE_SCHEMA",
                 "METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::CREATE_MODEL_VERSION",
+                "CATALOG::DENY_CREATE_MODEL_VERSION",
+                "MODEL::USE_MODEL",
+                "METALAKE::USE_SCHEMA",
+                "METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::DENY_CREATE_MODEL_VERSION",
+                "CATALOG::CREATE_MODEL_VERSION",
+                "MODEL::USE_MODEL",
+                "METALAKE::USE_SCHEMA",
+                "METALAKE::USE_CATALOG")));
     assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER", 
"CATALOG::USE_CATALOG")));
     assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER", 
"SCHEMA::USE_SCHEMA")));
     assertTrue(
@@ -285,6 +352,10 @@ public class TestModelAuthorizationExpression {
     assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"CATALOG::USE_CATALOG")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER", "METALAKE::USE_CATALOG", 
"CATALOG::DENY_USE_CATALOG")));
     
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL")));
     assertFalse(
         mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL", 
"SCHEMA::USE_SCHEMA")));
@@ -328,6 +399,10 @@ public class TestModelAuthorizationExpression {
     assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"CATALOG::USE_CATALOG")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER", "METALAKE::USE_CATALOG", 
"CATALOG::DENY_USE_CATALOG")));
     
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL")));
     assertFalse(
         mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL", 
"SCHEMA::USE_SCHEMA")));
@@ -370,6 +445,10 @@ public class TestModelAuthorizationExpression {
     assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"CATALOG::USE_CATALOG")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER", "METALAKE::USE_CATALOG", 
"CATALOG::DENY_USE_CATALOG")));
     
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL")));
     assertFalse(
         mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL", 
"SCHEMA::USE_SCHEMA")));
@@ -412,6 +491,10 @@ public class TestModelAuthorizationExpression {
     assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"CATALOG::USE_CATALOG")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER", "METALAKE::USE_CATALOG", 
"CATALOG::DENY_USE_CATALOG")));
     
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL")));
     assertFalse(
         mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL", 
"SCHEMA::USE_SCHEMA")));
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestSchemaAuthorizationExpression.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestSchemaAuthorizationExpression.java
index fe1a85fc9b..b2ddc646d3 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestSchemaAuthorizationExpression.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestSchemaAuthorizationExpression.java
@@ -50,6 +50,16 @@ public class TestSchemaAuthorizationExpression {
     assertTrue(
         mockEvaluator.getResult(
             ImmutableSet.of("METALAKE::CREATE_SCHEMA", 
"METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::CREATE_SCHEMA",
+                "CATALOG::DENY_CREATE_SCHEMA",
+                "METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::CREATE_SCHEMA", "METALAKE::USE_CATALOG", 
"CATALOG::DENY_USE_CATALOG")));
   }
 
   @Test
@@ -70,6 +80,14 @@ public class TestSchemaAuthorizationExpression {
     
assertFalse(mockEvaluator.getResult(ImmutableSet.of("CATALOG::USE_CATALOG")));
     assertTrue(
         mockEvaluator.getResult(ImmutableSet.of("CATALOG::USE_CATALOG", 
"METALAKE::USE_SCHEMA")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "CATALOG::USE_CATALOG", "METALAKE::USE_SCHEMA", 
"SCHEMA::DENY_USE_SCHEMA")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "CATALOG::USE_CATALOG", "METALAKE::DENY_USE_SCHEMA", 
"SCHEMA::USE_SCHEMA")));
   }
 
   @Test
@@ -89,6 +107,10 @@ public class TestSchemaAuthorizationExpression {
     
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::USE_SCHEMA")));
     assertTrue(
         mockEvaluator.getResult(ImmutableSet.of("CATALOG::USE_CATALOG", 
"METALAKE::USE_SCHEMA")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "CATALOG::USE_CATALOG", "METALAKE::DENY_USE_SCHEMA", 
"CATALOG::USE_SCHEMA")));
   }
 
   @Test
@@ -112,6 +134,10 @@ public class TestSchemaAuthorizationExpression {
         mockEvaluator.getResult(ImmutableSet.of("CATALOG::USE_CATALOG", 
"METALAKE::USE_SCHEMA")));
     assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER", "CATALOG::USE_CATALOG", 
"METALAKE::DENY_USE_CATALOG")));
   }
 
   @Test
@@ -133,5 +159,9 @@ public class TestSchemaAuthorizationExpression {
     
assertFalse(mockEvaluator.getResult(ImmutableSet.of("CATALOG::USE_CATALOG")));
     assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER")));
     assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER", "CATALOG::USE_CATALOG", 
"METALAKE::DENY_USE_CATALOG")));
   }
 }
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestTableAuthorizationExpression.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestTableAuthorizationExpression.java
index 2ab74c08b2..5190327aa8 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestTableAuthorizationExpression.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestTableAuthorizationExpression.java
@@ -62,6 +62,20 @@ public class TestTableAuthorizationExpression {
         mockEvaluator.getResult(
             ImmutableSet.of(
                 "METALAKE::CREATE_TABLE", "METALAKE::USE_SCHEMA", 
"METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::CREATE_TABLE",
+                "CATALOG::DENY_CREATE_TABLE",
+                "METALAKE::USE_SCHEMA",
+                "METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::DENY_CREATE_TABLE",
+                "CATALOG::CREATE_TABLE",
+                "METALAKE::USE_SCHEMA",
+                "METALAKE::USE_CATALOG")));
   }
 
   @Test
@@ -98,6 +112,13 @@ public class TestTableAuthorizationExpression {
         mockEvaluator.getResult(
             ImmutableSet.of(
                 "METALAKE::SELECT_TABLE", "METALAKE::USE_SCHEMA", 
"METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::SELECT_TABLE",
+                "CATALOG::DENY_SELECT_TABLE",
+                "METALAKE::USE_SCHEMA",
+                "METALAKE::USE_CATALOG")));
   }
 
   @Test
@@ -136,6 +157,13 @@ public class TestTableAuthorizationExpression {
         mockEvaluator.getResult(
             ImmutableSet.of(
                 "METALAKE::SELECT_TABLE", "METALAKE::USE_SCHEMA", 
"METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::SELECT_TABLE",
+                "CATALOG::DENY_SELECT_TABLE",
+                "METALAKE::USE_SCHEMA",
+                "METALAKE::USE_CATALOG")));
   }
 
   @Test
@@ -192,6 +220,16 @@ public class TestTableAuthorizationExpression {
         mockEvaluator.getResult(
             ImmutableSet.of(
                 "METALAKE::MODIFY_TABLE", "METALAKE::USE_SCHEMA", 
"METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::MODIFY_TABLE", "CATALOG::DENY_MODIFY_TABLE",
+                "METALAKE::USE_SCHEMA", "METALAKE::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "METALAKE::DENY_MODIFY_TABLE", "CATALOG::MODIFY_TABLE",
+                "METALAKE::USE_SCHEMA", "METALAKE::USE_CATALOG")));
   }
 
   @Test
@@ -248,5 +286,19 @@ public class TestTableAuthorizationExpression {
     assertTrue(
         mockEvaluator.getResult(
             ImmutableSet.of("TABLE::OWNER", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "TABLE::OWNER",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::DENY_USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "TABLE::OWNER",
+                "SCHEMA::DENY_USE_SCHEMA",
+                "CATALOG::USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
   }
 }
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestTopicAuthorizationExpression.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestTopicAuthorizationExpression.java
index 8e4b88aeb6..e9f5a32fc1 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestTopicAuthorizationExpression.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestTopicAuthorizationExpression.java
@@ -56,6 +56,20 @@ public class TestTopicAuthorizationExpression {
     assertTrue(
         mockEvaluator.getResult(
             ImmutableSet.of("SCHEMA::CREATE_TOPIC", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::CREATE_TOPIC",
+                "METALAKE::DENY_CREATE_TOPIC",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::DENY_CREATE_TOPIC",
+                "METALAKE::CREATE_TOPIC",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG")));
   }
 
   @Test
@@ -102,6 +116,20 @@ public class TestTopicAuthorizationExpression {
     assertTrue(
         mockEvaluator.getResult(
             ImmutableSet.of("SCHEMA::OWNER", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG",
+                "METALAKE::DENY_USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER",
+                "SCHEMA::DENY_USE_SCHEMA",
+                "CATALOG::DENY_USE_CATALOG",
+                "METALAKE::USE_CATALOG")));
   }
 
   @Test
@@ -144,6 +172,20 @@ public class TestTopicAuthorizationExpression {
     assertTrue(
         mockEvaluator.getResult(
             ImmutableSet.of("SCHEMA::OWNER", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG",
+                "METALAKE::DENY_USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER",
+                "SCHEMA::DENY_USE_SCHEMA",
+                "CATALOG::DENY_USE_CATALOG",
+                "METALAKE::USE_CATALOG")));
   }
 
   @Test
@@ -182,5 +224,19 @@ public class TestTopicAuthorizationExpression {
     assertTrue(
         mockEvaluator.getResult(
             ImmutableSet.of("SCHEMA::OWNER", "SCHEMA::USE_SCHEMA", 
"CATALOG::USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER",
+                "SCHEMA::USE_SCHEMA",
+                "CATALOG::USE_CATALOG",
+                "METALAKE::DENY_USE_CATALOG")));
+    assertFalse(
+        mockEvaluator.getResult(
+            ImmutableSet.of(
+                "SCHEMA::OWNER",
+                "SCHEMA::DENY_USE_SCHEMA",
+                "CATALOG::DENY_USE_CATALOG",
+                "METALAKE::USE_CATALOG")));
   }
 }

Reply via email to