From 4600545d4fed44ea5ebc47dc6286be7b0c506330 Mon Sep 17 00:00:00 2001 From: LimJiaWenBrenda Date: Wed, 4 Mar 2026 09:45:06 +0800 Subject: [PATCH 1/2] Clear REST user cache when invalidating user cache --- .../org/apache/iotdb/rest/RestService.java | 5 ++ .../iotdb/rest/protocol/filter/UserCache.java | 10 ++++ .../iotdb/db/auth/AuthorityChecker.java | 48 +++++++++++++++++++ .../conf/rest/IoTDBRestServiceDescriptor.java | 25 ++++++---- .../iotdb/commons/conf/IoTDBConstant.java | 1 + 5 files changed, 79 insertions(+), 10 deletions(-) diff --git a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/RestService.java b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/RestService.java index 9a90e6b18f2ea..8ef56481fafa8 100644 --- a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/RestService.java +++ b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/RestService.java @@ -20,6 +20,7 @@ import org.apache.iotdb.db.conf.rest.IoTDBRestServiceDescriptor; import org.apache.iotdb.externalservice.api.IExternalService; import org.apache.iotdb.rest.protocol.filter.ApiOriginFilter; +import org.apache.iotdb.rest.protocol.filter.UserCache; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.HttpConfiguration; @@ -141,4 +142,8 @@ public void stop() { server.destroy(); } } + + public static void clearUserCache(String userName) { + UserCache.getInstance().clearUserCache(userName); + } } diff --git a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/filter/UserCache.java b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/filter/UserCache.java index 8c8a55f25b596..b09c763253a25 100644 --- a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/filter/UserCache.java +++ b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/filter/UserCache.java @@ -23,6 +23,7 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import java.util.Map; import java.util.concurrent.TimeUnit; public class UserCache { @@ -51,6 +52,15 @@ public void setUser(String key, User user) { cache.put(key, user); } + public void clearUserCache(String userName) { + for (Map.Entry mapValue : cache.asMap().entrySet()) { + if (mapValue.getValue().getUsername().equals(userName)) { + cache.invalidate(mapValue.getKey()); + return; + } + } + } + private static class UserCacheHolder { private static final UserCache INSTANCE = new UserCache(); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java index 21c3bc787ee2e..71827ef264d4d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java @@ -19,6 +19,7 @@ package org.apache.iotdb.db.auth; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceEntry; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.audit.IAuditEntity; import org.apache.iotdb.commons.audit.UserEntity; @@ -26,6 +27,7 @@ import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.conf.CommonDescriptor; +import org.apache.iotdb.commons.externalservice.ServiceInfo; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathPatternTree; import org.apache.iotdb.commons.schema.column.ColumnHeader; @@ -51,6 +53,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; import org.apache.iotdb.db.queryengine.plan.statement.Statement; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; +import org.apache.iotdb.db.service.externalservice.BuiltinExternalServices; +import org.apache.iotdb.db.service.externalservice.ExternalServiceManagementService; import org.apache.iotdb.rpc.TSStatusCode; import com.google.common.util.concurrent.SettableFuture; @@ -59,7 +63,11 @@ import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.Binary; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -77,6 +85,8 @@ // It checks permission in local. DCL statement will send to configNode. public class AuthorityChecker { + private static final Logger LOGGER = LoggerFactory.getLogger(AuthorityChecker.class); + public static int SUPER_USER_ID = 0; public static String SUPER_USER = CommonDescriptor.getInstance().getConfig().getDefaultAdminName(); @@ -130,9 +140,47 @@ public static IAuthorityFetcher getAuthorityFetcher() { public static boolean invalidateCache(String username, String roleName) { PipeInsertionDataNodeListener.getInstance().invalidateAllCache(); + tryClearRestUserCache(username); return authorityFetcher.get().getAuthorCache().invalidateCache(username, roleName); } + private static void tryClearRestUserCache(String userName) { + List builtInServices = + ExternalServiceManagementService.getInstance().getBuiltInServices(); + for (TExternalServiceEntry service : builtInServices) { + if (service.getServiceName().equals(BuiltinExternalServices.REST.getServiceName())) { + if (service.getState() == ServiceInfo.State.RUNNING.getValue()) { + String restClassName = service.getClassName(); + try { + ClassLoader loader = ExternalServiceManagementService.class.getClassLoader(); + Class clz = loader.loadClass(restClassName); + Method clearUserCacheMethod = clz.getMethod("clearUserCache", String.class); + clearUserCacheMethod.invoke(null, userName); + } catch (ClassNotFoundException e) { + LOGGER.warn( + "Failed to load class {} for external service {}, error: {}", + restClassName, + service.getServiceName(), + e.getMessage()); + } catch (NoSuchMethodException e) { + LOGGER.warn( + "Failed to find method clearUserCache in class {} for external service {}, error: {}", + restClassName, + service.getServiceName(), + e.getMessage()); + } catch (IllegalAccessException | InvocationTargetException e) { + LOGGER.warn( + "Failed to invoke clearUserCache for user {} in external service {}, error: {}", + userName, + service.getServiceName(), + e.getMessage()); + } + } + } + } + LOGGER.info("Cleared cache for user {} in REST service", userName); + } + public static User getUser(String username) { return authorityFetcher.get().getUser(username); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java index 826c0fbdb01f2..11c00e543d5df 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java @@ -138,17 +138,22 @@ public URL getPropsUrl(String configName) { if (urlString != null) { urlString = urlString + File.separatorChar + "conf" + File.separatorChar + configName; } else { - // If this too wasn't provided, try to find a default config in the root of the classpath. - URL uri = IoTDBConfig.class.getResource("/" + configName); - if (uri != null) { - return uri; + urlString = System.getProperty(IoTDBConstant.CONFIGNODE_HOME, null); + if (urlString != null) { + urlString = urlString + File.separatorChar + "conf" + File.separatorChar + configName; + } else { + // If this too wasn't provided, try to find a default config in the root of the classpath. + URL uri = IoTDBConfig.class.getResource("/" + configName); + if (uri != null) { + return uri; + } + logger.warn( + "Cannot find IOTDB_HOME or IOTDB_CONF or CONFIGNODE_HOME environment variable when loading " + + "config file {}, use default configuration", + configName); + // update all data seriesPath + return null; } - logger.warn( - "Cannot find IOTDB_HOME or IOTDB_CONF environment variable when loading " - + "config file {}, use default configuration", - configName); - // update all data seriesPath - return null; } } // If a config location was provided, but it doesn't end with a properties file, diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java index a94f472b606dd..707c779fc91e4 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java @@ -127,6 +127,7 @@ private IoTDBConstant() {} public static final long KB = 1L << 10; public static final String IOTDB_HOME = "IOTDB_HOME"; + public static final String CONFIGNODE_HOME = "CONFIGNODE_HOME"; public static final String IOTDB_DATA_HOME = "IOTDB_DATA_HOME"; From 3f968288588534b14aa0d918ae96551f99d15587 Mon Sep 17 00:00:00 2001 From: LimJiaWenBrenda Date: Wed, 4 Mar 2026 17:03:57 +0800 Subject: [PATCH 2/2] Changed implementation method to clear rest user cache --- .../org/apache/iotdb/mqtt/MQTTService.java | 5 +++ .../org/apache/iotdb/rest/RestService.java | 3 +- .../externalservice/api/IExternalService.java | 3 ++ .../iotdb/db/auth/AuthorityChecker.java | 44 +------------------ .../conf/rest/IoTDBRestServiceDescriptor.java | 25 +++++------ .../ExternalServiceManagementService.java | 19 ++++++++ .../iotdb/commons/conf/IoTDBConstant.java | 1 - 7 files changed, 40 insertions(+), 60 deletions(-) diff --git a/external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/MQTTService.java b/external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/MQTTService.java index 3b2be5ac3f4f1..00b6c28028a6c 100644 --- a/external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/MQTTService.java +++ b/external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/MQTTService.java @@ -52,6 +52,11 @@ public void stop() { shutdown(); } + @Override + public void clearUserCache(String userName) { + // do nothing + } + public void startup() { IoTDBConfig iotDBConfig = IoTDBDescriptor.getInstance().getConfig(); IConfig config = createBrokerConfig(iotDBConfig); diff --git a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/RestService.java b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/RestService.java index 8ef56481fafa8..aadbda01066ab 100644 --- a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/RestService.java +++ b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/RestService.java @@ -143,7 +143,8 @@ public void stop() { } } - public static void clearUserCache(String userName) { + @Override + public void clearUserCache(String userName) { UserCache.getInstance().clearUserCache(userName); } } diff --git a/iotdb-api/external-service-api/src/main/java/org/apache/iotdb/externalservice/api/IExternalService.java b/iotdb-api/external-service-api/src/main/java/org/apache/iotdb/externalservice/api/IExternalService.java index 1ae5f30e7a9d3..9f5085f3c8bbf 100644 --- a/iotdb-api/external-service-api/src/main/java/org/apache/iotdb/externalservice/api/IExternalService.java +++ b/iotdb-api/external-service-api/src/main/java/org/apache/iotdb/externalservice/api/IExternalService.java @@ -30,4 +30,7 @@ public interface IExternalService { * guarantee to putBack thread or thread pool. */ void stop(); + + /** Clear user cache of current service. */ + void clearUserCache(String userName); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java index 71827ef264d4d..edb4d43284f8d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java @@ -19,7 +19,6 @@ package org.apache.iotdb.db.auth; -import org.apache.iotdb.common.rpc.thrift.TExternalServiceEntry; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.audit.IAuditEntity; import org.apache.iotdb.commons.audit.UserEntity; @@ -27,7 +26,6 @@ import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.conf.CommonDescriptor; -import org.apache.iotdb.commons.externalservice.ServiceInfo; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathPatternTree; import org.apache.iotdb.commons.schema.column.ColumnHeader; @@ -53,7 +51,6 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; import org.apache.iotdb.db.queryengine.plan.statement.Statement; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; -import org.apache.iotdb.db.service.externalservice.BuiltinExternalServices; import org.apache.iotdb.db.service.externalservice.ExternalServiceManagementService; import org.apache.iotdb.rpc.TSStatusCode; @@ -66,8 +63,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -140,47 +135,10 @@ public static IAuthorityFetcher getAuthorityFetcher() { public static boolean invalidateCache(String username, String roleName) { PipeInsertionDataNodeListener.getInstance().invalidateAllCache(); - tryClearRestUserCache(username); + ExternalServiceManagementService.getInstance().clearServiceUserCache(username); return authorityFetcher.get().getAuthorCache().invalidateCache(username, roleName); } - private static void tryClearRestUserCache(String userName) { - List builtInServices = - ExternalServiceManagementService.getInstance().getBuiltInServices(); - for (TExternalServiceEntry service : builtInServices) { - if (service.getServiceName().equals(BuiltinExternalServices.REST.getServiceName())) { - if (service.getState() == ServiceInfo.State.RUNNING.getValue()) { - String restClassName = service.getClassName(); - try { - ClassLoader loader = ExternalServiceManagementService.class.getClassLoader(); - Class clz = loader.loadClass(restClassName); - Method clearUserCacheMethod = clz.getMethod("clearUserCache", String.class); - clearUserCacheMethod.invoke(null, userName); - } catch (ClassNotFoundException e) { - LOGGER.warn( - "Failed to load class {} for external service {}, error: {}", - restClassName, - service.getServiceName(), - e.getMessage()); - } catch (NoSuchMethodException e) { - LOGGER.warn( - "Failed to find method clearUserCache in class {} for external service {}, error: {}", - restClassName, - service.getServiceName(), - e.getMessage()); - } catch (IllegalAccessException | InvocationTargetException e) { - LOGGER.warn( - "Failed to invoke clearUserCache for user {} in external service {}, error: {}", - userName, - service.getServiceName(), - e.getMessage()); - } - } - } - } - LOGGER.info("Cleared cache for user {} in REST service", userName); - } - public static User getUser(String username) { return authorityFetcher.get().getUser(username); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java index 11c00e543d5df..c20ca022d5a00 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java @@ -138,22 +138,17 @@ public URL getPropsUrl(String configName) { if (urlString != null) { urlString = urlString + File.separatorChar + "conf" + File.separatorChar + configName; } else { - urlString = System.getProperty(IoTDBConstant.CONFIGNODE_HOME, null); - if (urlString != null) { - urlString = urlString + File.separatorChar + "conf" + File.separatorChar + configName; - } else { - // If this too wasn't provided, try to find a default config in the root of the classpath. - URL uri = IoTDBConfig.class.getResource("/" + configName); - if (uri != null) { - return uri; - } - logger.warn( - "Cannot find IOTDB_HOME or IOTDB_CONF or CONFIGNODE_HOME environment variable when loading " - + "config file {}, use default configuration", - configName); - // update all data seriesPath - return null; + // If this too wasn't provided, try to find a default config in the root of the classpath. + URL uri = IoTDBConfig.class.getResource("/" + configName); + if (uri != null) { + return uri; } + logger.warn( + "Cannot find IOTDB_HOME or IOTDB_CONF or CONFIGNODE_HOME environment variable when loading " + + "config file {}, use default configuration", + configName); + // update all data seriesPath + return null; } } // If a config location was provided, but it doesn't end with a properties file, diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/externalservice/ExternalServiceManagementService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/externalservice/ExternalServiceManagementService.java index 1d108bc941a6f..3850902a502e9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/externalservice/ExternalServiceManagementService.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/externalservice/ExternalServiceManagementService.java @@ -417,6 +417,25 @@ public static ExternalServiceManagementService getInstance() { return ExternalServiceManagementServiceHolder.INSTANCE; } + public void clearServiceUserCache(String userName) { + serviceInfos + .values() + .forEach( + serviceInfo -> { + // clear user cache of service with RUNNING state + if (serviceInfo.getState() == RUNNING) { + IExternalService serviceInstance = serviceInfo.getServiceInstance(); + // serviceInstance maybe null when an exception occurs during the start of certain + // service in restoreRunningServiceInstance method + if (serviceInstance != null) { + // only clear user cache of the instance successfully started and running + runWithServiceClassLoader( + serviceInfo, () -> serviceInstance.clearUserCache(userName)); + } + } + }); + } + private static class ExternalServiceManagementServiceHolder { private static final ExternalServiceManagementService INSTANCE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java index 707c779fc91e4..a94f472b606dd 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java @@ -127,7 +127,6 @@ private IoTDBConstant() {} public static final long KB = 1L << 10; public static final String IOTDB_HOME = "IOTDB_HOME"; - public static final String CONFIGNODE_HOME = "CONFIGNODE_HOME"; public static final String IOTDB_DATA_HOME = "IOTDB_DATA_HOME";