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

shuwenwei pushed a commit to branch sync-generic-changes
in repository https://gitbox.apache.org/repos/asf/iotdb.git

commit b59462d40e0cbf67d84e60e9f284847eab096c47
Author: shuwenwei <[email protected]>
AuthorDate: Thu Sep 17 15:26:49 2026 +0800

    [Auth] Persist user/role profiles with a versioned extra segment region
---
 .../schema/CNPhysicalPlanGenerator.java            | 33 ++++++++++-
 .../commons/auth/role/LocalFileRoleAccessor.java   | 69 +++++++++++++++++++++-
 .../commons/auth/user/LocalFileUserAccessor.java   |  6 +-
 3 files changed, 103 insertions(+), 5 deletions(-)

diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/CNPhysicalPlanGenerator.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/CNPhysicalPlanGenerator.java
index e4366c45fc4..c2e0e08fe21 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/CNPhysicalPlanGenerator.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/CNPhysicalPlanGenerator.java
@@ -21,6 +21,7 @@ package org.apache.iotdb.confignode.persistence.schema;
 
 import org.apache.iotdb.commons.auth.entity.PrivilegeModelType;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
+import 
org.apache.iotdb.commons.auth.role.LocalFileRoleAccessor.ExtraSegmentType;
 import org.apache.iotdb.commons.exception.IllegalPathException;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.commons.schema.SchemaConstant;
@@ -224,7 +225,7 @@ public class CNPhysicalPlanGenerator
         createUser.setPermissions(new HashSet<>());
         createUser.setNodeNameList(new ArrayList<>());
         planDeque.add(createUser);
-        if (tag == 2) {
+        if (tag >= 2) {
           final AuthorTreePlan updateUserMaxSession =
               new AuthorTreePlan(ConfigPhysicalPlanType.UpdateUserMaxSession);
           updateUserMaxSession.setMaxSessionPerUser(dataInputStream.readInt());
@@ -296,6 +297,10 @@ public class CNPhysicalPlanGenerator
           }
         }
       }
+
+      if (tag >= 3) {
+        generateExtraSegmentPhysicalPlans(dataInputStream, user, isUser);
+      }
     } catch (IOException ioException) {
       logger.error(
           
ManagerMessages.LOG_GOT_IOEXCEPTION_DESERIALIZE_USE_ROLE_FILE_TYPE_ARG_1B548759,
@@ -307,6 +312,32 @@ public class CNPhysicalPlanGenerator
     }
   }
 
+  /**
+   * Reads the extra segment region appended after the RBAC privileges of a 
user/role profile file.
+   * Each segment is encoded as {@code [type: int32][length: int32][payload: 
bytes]} and dispatched
+   * by its type. No extra segment types are handled in this branch, so every 
payload is ignored,
+   * while segments of unknown types written by newer versions stay forward 
compatible.
+   */
+  private void generateExtraSegmentPhysicalPlans(
+      final DataInputStream dataInputStream, final String granteeName, final 
boolean isUser)
+      throws IOException {
+    final int extraSegmentCount = dataInputStream.readInt();
+    for (int i = 0; i < extraSegmentCount; i++) {
+      final ExtraSegmentType segmentType = 
ExtraSegmentType.fromType(dataInputStream.readInt());
+      final int length = dataInputStream.readInt();
+      final byte[] segmentData = new byte[length];
+      dataInputStream.readFully(segmentData);
+      if (segmentType == null) {
+        continue;
+      }
+      switch (segmentType) {
+        default:
+          // No extra segment types are handled in this branch.
+          break;
+      }
+    }
+  }
+
   private void generateGrantRolePhysicalPlan() {
     try (final DataInputStream roleInputStream =
         new DataInputStream(new BufferedInputStream((inputStream)))) {
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java
index df7d7f28427..4f57760183d 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java
@@ -85,8 +85,34 @@ public class LocalFileRoleAccessor implements 
IEntityAccessor {
   protected final String entityDirPath;
 
   // It might be a good idea to use a Version number to control upgrade 
compatibility.
-  // Now it's version 1
-  protected static final int VERSION = 2;
+  // Version 3 appends an extra segment region after the RBAC privileges.
+  protected static final int VERSION = 3;
+
+  /** Types of the extra segments appended after the RBAC privileges in a 
profile file. */
+  public enum ExtraSegmentType {
+    /** The subject's LBAC label grants and rule exemptions. */
+    LBAC_GRANT_INFO(1);
+
+    private final int type;
+
+    ExtraSegmentType(final int type) {
+      this.type = type;
+    }
+
+    public int getType() {
+      return type;
+    }
+
+    /** Returns the segment type matching the given value, or {@code null} if 
unknown. */
+    public static ExtraSegmentType fromType(final int type) {
+      for (final ExtraSegmentType segmentType : values()) {
+        if (segmentType.type == type) {
+          return segmentType;
+        }
+      }
+      return null;
+    }
+  }
 
   /**
    * Reused buffer for primitive types encoding/decoding, which aim to reduce 
memory fragments. Use
@@ -151,6 +177,38 @@ public class LocalFileRoleAccessor implements 
IEntityAccessor {
     role.setObjectPrivilegeMap(objectPrivilegeMap);
   }
 
+  /**
+   * Writes the extra segment region appended after the RBAC privileges. The 
region starts with the
+   * segment count. No extra segments are written by this branch, so the 
region is empty.
+   */
+  protected void saveExtraSegments(BufferedOutputStream outputStream, Role 
role)
+      throws IOException {
+    IOUtils.writeInt(outputStream, 0, encodingBufferLocal);
+  }
+
+  /**
+   * Reads the extra segment region appended after the RBAC privileges. Each 
segment is encoded as
+   * [type: int32][length: int32][payload: bytes]. This branch handles no 
segment type, so every
+   * payload is ignored while profile files written by newer versions can 
still be loaded.
+   */
+  protected void loadExtraSegments(DataInputStream dataInputStream, Role role) 
throws IOException {
+    final int extraSegmentCount = dataInputStream.readInt();
+    for (int i = 0; i < extraSegmentCount; i++) {
+      final ExtraSegmentType segmentType = 
ExtraSegmentType.fromType(dataInputStream.readInt());
+      final int length = dataInputStream.readInt();
+      final byte[] segmentData = new byte[length];
+      dataInputStream.readFully(segmentData);
+      if (segmentType == null) {
+        continue;
+      }
+      switch (segmentType) {
+        default:
+          // No extra segment types are handled in this branch.
+          break;
+      }
+    }
+  }
+
   protected void saveSessionPerUser(BufferedOutputStream outputStream, Role 
role)
       throws IOException {
     // Just used in LocalFileUserAccessor.java.
@@ -218,10 +276,14 @@ public class LocalFileRoleAccessor implements 
IEntityAccessor {
         loadPrivileges(dataInputStream, role);
         return role;
       } else {
-        assert tag == VERSION;
+        // tag >= 2: version 2 and version 3 share the same leading layout; 
version 3 additionally
+        // appends the extra segment region.
         entityName = IOUtils.readString(dataInputStream, STRING_ENCODING, 
strBufferLocal);
         Role role = new Role(entityName);
         loadPrivileges(dataInputStream, role);
+        if (tag >= 3) {
+          loadExtraSegments(dataInputStream, role);
+        }
         return role;
       }
 
@@ -276,6 +338,7 @@ public class LocalFileRoleAccessor implements 
IEntityAccessor {
       saveEntityName(outputStream, entity);
       saveSessionPerUser(outputStream, entity);
       savePrivileges(outputStream, entity);
+      saveExtraSegments(outputStream, entity);
       outputStream.flush();
       fileOutputStream.getFD().sync();
     } catch (Exception e) {
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java
index 5ad0d08fc42..bac866a5291 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java
@@ -172,13 +172,17 @@ public class LocalFileUserAccessor extends 
LocalFileRoleAccessor {
         user.setPassword(IOUtils.readString(dataInputStream, STRING_ENCODING, 
strBufferLocal));
         loadPrivileges(dataInputStream, user);
       } else {
-        assert (tag == VERSION);
+        // tag >= 2: version 2 and version 3 share the same leading layout; 
version 3 additionally
+        // appends the extra segment region.
         user.setUserId(dataInputStream.readLong());
         user.setName(IOUtils.readString(dataInputStream, STRING_ENCODING, 
strBufferLocal));
         user.setPassword(IOUtils.readString(dataInputStream, STRING_ENCODING, 
strBufferLocal));
         user.setMaxSessionPerUser(dataInputStream.readInt());
         user.setMinSessionPerUser(dataInputStream.readInt());
         loadPrivileges(dataInputStream, user);
+        if (tag >= 3) {
+          loadExtraSegments(dataInputStream, user);
+        }
       }
 
       File roleOfUser = checkFileAvailable(entityName, "_role");

Reply via email to