This is an automated email from the ASF dual-hosted git repository.
yuqi4733 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 7f94ffdb51 [#10117] improvement(authz): Remove ownership relation
cache (#10119)
7f94ffdb51 is described below
commit 7f94ffdb51117346b092716eb00a9c318fc76705
Author: roryqi <[email protected]>
AuthorDate: Fri Mar 6 11:19:55 2026 +0800
[#10117] improvement(authz): Remove ownership relation cache (#10119)
### What changes were proposed in this pull request?
Remove ownership relation cache
### Why are the changes needed?
Fix: #10117
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
Existing test cases.
---
.../main/java/org/apache/gravitino/Configs.java | 9 ----
.../authorization/GravitinoAuthorizer.java | 12 -----
.../gravitino/authorization/OwnerManager.java | 30 ------------
docs/security/access-control.md | 7 +--
.../authorization/PassThroughAuthorizer.java | 4 --
.../authorization/jcasbin/JcasbinAuthorizer.java | 55 ++++++----------------
.../authorization/MockGravitinoAuthorizer.java | 4 --
.../jcasbin/TestJcasbinAuthorizer.java | 44 -----------------
.../filter/TestGravitinoInterceptionService.java | 4 --
9 files changed, 16 insertions(+), 153 deletions(-)
diff --git a/core/src/main/java/org/apache/gravitino/Configs.java
b/core/src/main/java/org/apache/gravitino/Configs.java
index 3cc9d703e6..a26f3c36a5 100644
--- a/core/src/main/java/org/apache/gravitino/Configs.java
+++ b/core/src/main/java/org/apache/gravitino/Configs.java
@@ -320,15 +320,6 @@ public class Configs {
.longConf()
.createWithDefault(DEFAULT_GRAVITINO_AUTHORIZATION_ROLE_CACHE_SIZE);
- public static final long DEFAULT_GRAVITINO_AUTHORIZATION_OWNER_CACHE_SIZE =
100000L;
-
- public static final ConfigEntry<Long>
GRAVITINO_AUTHORIZATION_OWNER_CACHE_SIZE =
- new ConfigBuilder("gravitino.authorization.jcasbin.ownerCacheSize")
- .doc("The maximum size of the owner cache for authorization")
- .version(ConfigConstants.VERSION_1_1_1)
- .longConf()
- .createWithDefault(DEFAULT_GRAVITINO_AUTHORIZATION_OWNER_CACHE_SIZE);
-
public static final ConfigEntry<List<String>> SERVICE_ADMINS =
new ConfigBuilder("gravitino.authorization.serviceAdmins")
.doc("The admins of Gravitino service")
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 7965f173ad..3a4fbfc0a9 100644
---
a/core/src/main/java/org/apache/gravitino/authorization/GravitinoAuthorizer.java
+++
b/core/src/main/java/org/apache/gravitino/authorization/GravitinoAuthorizer.java
@@ -152,16 +152,4 @@ public interface GravitinoAuthorizer extends Closeable {
throw new RuntimeException("Can not get Role Entity", e);
}
}
-
- /**
- * This method is called to clear the owner relationship in jcasbin when the
owner of the metadata
- * changes.
- *
- * @param metalake metalake;
- * @param oldOwnerId The old owner id;
- * @param nameIdentifier The metadata name identifier;
- * @param type entity type
- */
- void handleMetadataOwnerChange(
- String metalake, Long oldOwnerId, NameIdentifier nameIdentifier,
Entity.EntityType type);
}
diff --git
a/core/src/main/java/org/apache/gravitino/authorization/OwnerManager.java
b/core/src/main/java/org/apache/gravitino/authorization/OwnerManager.java
index 5b7ce28c98..13fa04a80d 100644
--- a/core/src/main/java/org/apache/gravitino/authorization/OwnerManager.java
+++ b/core/src/main/java/org/apache/gravitino/authorization/OwnerManager.java
@@ -25,7 +25,6 @@ import java.util.Optional;
import lombok.Getter;
import org.apache.gravitino.Entity;
import org.apache.gravitino.EntityStore;
-import org.apache.gravitino.GravitinoEnv;
import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.SupportsRelationOperations;
@@ -37,7 +36,6 @@ import org.apache.gravitino.lock.TreeLockUtils;
import org.apache.gravitino.meta.GroupEntity;
import org.apache.gravitino.meta.UserEntity;
import org.apache.gravitino.utils.MetadataObjectUtil;
-import org.apache.gravitino.utils.NameIdentifierUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -120,7 +118,6 @@ public class OwnerManager implements OwnerDispatcher {
metadataObject,
authorizationPlugin ->
authorizationPlugin.onOwnerSet(metadataObject,
originOwner.orElse(null), newOwner));
- originOwner.ifPresent(owner -> notifyOwnerChange(owner, metalake,
metadataObject));
} catch (NoSuchEntityException nse) {
LOG.warn(
"Metadata object {} or owner {} is not found",
metadataObject.fullName(), ownerName, nse);
@@ -136,33 +133,6 @@ public class OwnerManager implements OwnerDispatcher {
}
}
- private void notifyOwnerChange(Owner oldOwner, String metalake,
MetadataObject metadataObject) {
- GravitinoAuthorizer gravitinoAuthorizer =
GravitinoEnv.getInstance().gravitinoAuthorizer();
- if (gravitinoAuthorizer != null) {
- if (oldOwner.type() == Owner.Type.USER) {
- try {
- UserEntity userEntity =
- GravitinoEnv.getInstance()
- .entityStore()
- .get(
- NameIdentifierUtil.ofUser(metalake, oldOwner.name()),
- Entity.EntityType.USER,
- UserEntity.class);
- gravitinoAuthorizer.handleMetadataOwnerChange(
- metalake,
- userEntity.id(),
- MetadataObjectUtil.toEntityIdent(metalake, metadataObject),
- Entity.EntityType.valueOf(metadataObject.type().name()));
- } catch (IOException e) {
- LOG.warn(e.getMessage(), e);
- }
- } else {
- throw new UnsupportedOperationException(
- "Notification for Group Owner is not supported yet.");
- }
- }
- }
-
@Override
public Optional<Owner> getOwner(String metalake, MetadataObject
metadataObject) {
NameIdentifier ident = MetadataObjectUtil.toEntityIdent(metalake,
metadataObject);
diff --git a/docs/security/access-control.md b/docs/security/access-control.md
index 1724cdde07..bb8e85aee1 100644
--- a/docs/security/access-control.md
+++ b/docs/security/access-control.md
@@ -446,20 +446,17 @@ To enable access control in Gravitino, configure the
following settings in your
| `gravitino.authorization.serviceAdmins` | Comma-separated
list of service administrator usernames | (none) | Yes
(when authorization is enabled) | 0.5.0 |
| `gravitino.authorization.jcasbin.cacheExpirationSecs` | The expiration
time in seconds for authorization cache entries | `3600` | No
| 1.1.1 |
| `gravitino.authorization.jcasbin.roleCacheSize` | The maximum size
of the role cache for authorization | `10000` | No
| 1.1.1 |
-| `gravitino.authorization.jcasbin.ownerCacheSize` | The maximum size
of the owner cache for authorization | `100000` | No
| 1.1.1 |
### Authorization Cache
-Gravitino uses Caffeine caches to improve authorization performance by caching
role and owner information. The cache configuration options allow you to tune
the cache behavior:
+Gravitino uses Caffeine caches to improve authorization performance by caching
role information. The cache configuration options allow you to tune the cache
behavior:
- **`cacheExpirationSecs`**: Controls how long cache entries remain valid.
After this time, entries are automatically evicted and reloaded from the
backend on the next access. Lower values provide more up-to-date authorization
decisions but may increase load on the backend.
- **`roleCacheSize`**: Controls the maximum number of role entries that can be
cached. When the cache reaches this size, the least recently used entries are
evicted.
-- **`ownerCacheSize`**: Controls the maximum number of owner relationship
entries that can be cached. This cache maps metadata object IDs to their owner
IDs.
-
:::info
-When role privileges or ownership are changed through the Gravitino API, the
corresponding cache entries are automatically invalidated to ensure
authorization decisions reflect the latest state.
+When role privileges are changed through the Gravitino API, the corresponding
cache entries are automatically invalidated to ensure authorization decisions
reflect the latest state.
:::
### Important Notes
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 06e20b8ada..82a5d3a82a 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
@@ -109,10 +109,6 @@ public class PassThroughAuthorizer implements
GravitinoAuthorizer {
@Override
public void handleRolePrivilegeChange(String metalake, String roleName) {}
- @Override
- public void handleMetadataOwnerChange(
- String metalake, Long oldOwnerId, NameIdentifier nameIdentifier,
Entity.EntityType type) {}
-
@Override
public void close() throws IOException {}
}
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 cea47e353b..524c90f032 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
@@ -29,7 +29,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
-import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -87,8 +86,6 @@ public class JcasbinAuthorizer implements GravitinoAuthorizer
{
*/
private Cache<Long, Boolean> loadedRoles;
- private Cache<Long, Optional<Long>> ownerRel;
-
private Executor executor = null;
@Override
@@ -99,8 +96,6 @@ public class JcasbinAuthorizer implements GravitinoAuthorizer
{
.get(Configs.GRAVITINO_AUTHORIZATION_CACHE_EXPIRATION_SECS);
long roleCacheSize =
GravitinoEnv.getInstance().config().get(Configs.GRAVITINO_AUTHORIZATION_ROLE_CACHE_SIZE);
- long ownerCacheSize =
-
GravitinoEnv.getInstance().config().get(Configs.GRAVITINO_AUTHORIZATION_OWNER_CACHE_SIZE);
// Initialize enforcers before the caches that reference them in removal
listeners
allowEnforcer = new SyncedEnforcer(getModel("/jcasbin_model.conf"), new
GravitinoAdapter());
@@ -121,11 +116,7 @@ public class JcasbinAuthorizer implements
GravitinoAuthorizer {
}
})
.build();
- ownerRel =
- Caffeine.newBuilder()
- .expireAfterAccess(cacheExpirationSecs, TimeUnit.SECONDS)
- .maximumSize(ownerCacheSize)
- .build();
+
executor =
Executors.newFixedThreadPool(
GravitinoEnv.getInstance()
@@ -218,15 +209,9 @@ public class JcasbinAuthorizer implements
GravitinoAuthorizer {
String metalake,
MetadataObject metadataObject,
AuthorizationRequestContext requestContext) {
- Long userId;
boolean result;
try {
- Long metadataId = MetadataIdConverter.getID(metadataObject, metalake);
- loadOwnerPolicy(metalake, metadataObject, metadataId);
- UserEntity userEntity = getUserEntity(principal.getName(), metalake);
- userId = userEntity.id();
- metadataId = MetadataIdConverter.getID(metadataObject, metalake);
- result = Objects.equals(Optional.of(userId),
ownerRel.getIfPresent(metadataId));
+ result = checkOwner(metalake, metadataObject, principal.getName());
} catch (Exception e) {
LOG.debug("Can not get entity id", e);
result = false;
@@ -387,14 +372,6 @@ public class JcasbinAuthorizer implements
GravitinoAuthorizer {
loadedRoles.invalidate(roleId);
}
- @Override
- public void handleMetadataOwnerChange(
- String metalake, Long oldOwnerId, NameIdentifier nameIdentifier,
Entity.EntityType type) {
- MetadataObject metadataObject =
NameIdentifierUtil.toMetadataObject(nameIdentifier, type);
- Long metadataId = MetadataIdConverter.getID(metadataObject, metalake);
- ownerRel.invalidate(metadataId);
- }
-
@Override
public void close() throws IOException {
if (executor != null) {
@@ -431,6 +408,9 @@ public class JcasbinAuthorizer implements
GravitinoAuthorizer {
AuthorizationRequestContext requestContext) {
Long metadataId;
Long userId;
+ if (AuthConstants.OWNER.equals(privilege)) {
+ return checkOwner(metalake, metadataObject, username);
+ }
try {
UserEntity userEntity = getUserEntity(username, metalake);
userId = userEntity.id();
@@ -439,16 +419,13 @@ public class JcasbinAuthorizer implements
GravitinoAuthorizer {
LOG.debug("Can not get entity id", e);
return false;
}
+
loadRolePrivilege(metalake, username, userId, requestContext);
return authorizeByJcasbin(userId, metadataObject, metadataId, privilege);
}
private boolean authorizeByJcasbin(
Long userId, MetadataObject metadataObject, Long metadataId, String
privilege) {
- if (AuthConstants.OWNER.equals(privilege)) {
- Optional<Long> owner = ownerRel.getIfPresent(metadataId);
- return Objects.equals(Optional.of(userId), owner);
- }
return enforcer.enforce(
String.valueOf(userId),
String.valueOf(metadataObject.type()),
@@ -518,11 +495,7 @@ public class JcasbinAuthorizer implements
GravitinoAuthorizer {
});
}
- private void loadOwnerPolicy(String metalake, MetadataObject metadataObject,
Long metadataId) {
- if (ownerRel.getIfPresent(metadataId) != null) {
- LOG.debug("Metadata {} OWNER has been loaded.", metadataId);
- return;
- }
+ private boolean checkOwner(String metalake, MetadataObject metadataObject,
String userName) {
try {
NameIdentifier entityIdent = MetadataObjectUtil.toEntityIdent(metalake,
metadataObject);
EntityStore entityStore = GravitinoEnv.getInstance().entityStore();
@@ -533,19 +506,19 @@ public class JcasbinAuthorizer implements
GravitinoAuthorizer {
SupportsRelationOperations.Type.OWNER_REL,
entityIdent,
Entity.EntityType.valueOf(metadataObject.type().name()));
- if (owners.isEmpty()) {
- ownerRel.put(metadataId, Optional.empty());
- } else {
- for (Entity ownerEntity : owners) {
- if (ownerEntity instanceof UserEntity) {
- UserEntity user = (UserEntity) ownerEntity;
- ownerRel.put(metadataId, Optional.of(user.id()));
+
+ for (Entity ownerEntity : owners) {
+ if (ownerEntity instanceof UserEntity) {
+ UserEntity user = (UserEntity) ownerEntity;
+ if (user.name().equals(userName)) {
+ return true;
}
}
}
} catch (IOException e) {
LOG.warn("Can not load metadata owner", e);
}
+ return false;
}
private void loadPolicyByRoleEntity(RoleEntity roleEntity) {
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 f9ef64bab0..acb388c689 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
@@ -112,10 +112,6 @@ public class MockGravitinoAuthorizer implements
GravitinoAuthorizer {
@Override
public void handleRolePrivilegeChange(Long roleId) {}
- @Override
- public void handleMetadataOwnerChange(
- String metalake, Long oldOwnerId, NameIdentifier nameIdentifier,
Entity.EntityType type) {}
-
@Override
public void close() {}
}
diff --git
a/server-common/src/test/java/org/apache/gravitino/server/authorization/jcasbin/TestJcasbinAuthorizer.java
b/server-common/src/test/java/org/apache/gravitino/server/authorization/jcasbin/TestJcasbinAuthorizer.java
index 4388ef0952..41088df93c 100644
---
a/server-common/src/test/java/org/apache/gravitino/server/authorization/jcasbin/TestJcasbinAuthorizer.java
+++
b/server-common/src/test/java/org/apache/gravitino/server/authorization/jcasbin/TestJcasbinAuthorizer.java
@@ -36,9 +36,7 @@ import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.lang.reflect.Field;
import java.security.Principal;
-import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import org.apache.commons.lang3.reflect.FieldUtils;
@@ -245,17 +243,7 @@ public class TestJcasbinAuthorizer {
eq(SupportsRelationOperations.Type.OWNER_REL),
eq(catalogIdent),
eq(Entity.EntityType.CATALOG));
- getOwnerRelCache(jcasbinAuthorizer).invalidateAll();
assertTrue(doAuthorizeOwner(currentPrincipal));
- doReturn(new ArrayList<>())
- .when(supportsRelationOperations)
- .listEntitiesByRelation(
- eq(SupportsRelationOperations.Type.OWNER_REL),
- eq(catalogIdent),
- eq(Entity.EntityType.CATALOG));
- jcasbinAuthorizer.handleMetadataOwnerChange(
- METALAKE, USER_ID, catalogIdent, Entity.EntityType.CATALOG);
- assertFalse(doAuthorizeOwner(currentPrincipal));
}
private Boolean doAuthorize(Principal currentPrincipal) {
@@ -379,28 +367,6 @@ public class TestJcasbinAuthorizer {
assertNull(loadedRoles.getIfPresent(testRoleId));
}
- @Test
- public void testOwnerCacheInvalidation() throws Exception {
- // Get the ownerRel cache via reflection
- Cache<Long, Optional<Long>> ownerRel = getOwnerRelCache(jcasbinAuthorizer);
-
- // Manually add an owner relation to the cache
- ownerRel.put(CATALOG_ID, Optional.of(USER_ID));
-
- // Verify it's in the cache
- assertNotNull(ownerRel.getIfPresent(CATALOG_ID));
-
- // Create a mock NameIdentifier for the metadata object
- NameIdentifier catalogIdent = NameIdentifierUtil.ofCatalog(METALAKE,
"testCatalog");
-
- // Call handleMetadataOwnerChange which should invalidate the cache entry
- jcasbinAuthorizer.handleMetadataOwnerChange(
- METALAKE, USER_ID, catalogIdent, Entity.EntityType.CATALOG);
-
- // Verify it's removed from the cache
- assertNull(ownerRel.getIfPresent(CATALOG_ID));
- }
-
@Test
public void testRoleCacheSynchronousRemovalListenerDeletesPolicy() throws
Exception {
makeCompletableFutureUseCurrentThread(jcasbinAuthorizer);
@@ -440,10 +406,8 @@ public class TestJcasbinAuthorizer {
public void testCacheInitialization() throws Exception {
// Verify that caches are initialized
Cache<Long, Boolean> loadedRoles = getLoadedRolesCache(jcasbinAuthorizer);
- Cache<Long, Optional<Long>> ownerRel = getOwnerRelCache(jcasbinAuthorizer);
assertNotNull(loadedRoles, "loadedRoles cache should be initialized");
- assertNotNull(ownerRel, "ownerRel cache should be initialized");
}
@SuppressWarnings("unchecked")
@@ -454,14 +418,6 @@ public class TestJcasbinAuthorizer {
return (Cache<Long, Boolean>) field.get(authorizer);
}
- @SuppressWarnings("unchecked")
- private static Cache<Long, Optional<Long>>
getOwnerRelCache(JcasbinAuthorizer authorizer)
- throws Exception {
- Field field = JcasbinAuthorizer.class.getDeclaredField("ownerRel");
- field.setAccessible(true);
- return (Cache<Long, Optional<Long>>) field.get(authorizer);
- }
-
private static Enforcer getAllowEnforcer(JcasbinAuthorizer authorizer)
throws Exception {
Field field = JcasbinAuthorizer.class.getDeclaredField("allowEnforcer");
field.setAccessible(true);
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 a96160da84..7a7cbad24a 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
@@ -337,10 +337,6 @@ public class TestGravitinoInterceptionService {
@Override
public void handleRolePrivilegeChange(Long roleId) {}
- @Override
- public void handleMetadataOwnerChange(
- String metalake, Long oldOwnerId, NameIdentifier nameIdentifier,
Entity.EntityType type) {}
-
@Override
public void close() throws IOException {}
}