Fix table function audit operation type
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
index 820e4aa..3e2eb12 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
@@ -1659,7 +1659,7 @@
   @Override
   public IConfigTask visitCreateFunction(CreateFunction node, MPPQueryContext context) {
     context.setQueryType(QueryType.OTHER);
-    accessControl.checkUserGlobalSysPrivilege(context);
+    accessControl.checkUserGlobalSysPrivilege(context, AuditLogOperation.DDL, node::getUdfName);
     if (node.getUriString().map(ExecutableManager::isUriTrusted).orElse(true)) {
       // 1. user specified uri and that uri is trusted
       // 2. user doesn't specify uri
@@ -1679,7 +1679,7 @@
   @Override
   public IConfigTask visitDropFunction(DropFunction node, MPPQueryContext context) {
     context.setQueryType(QueryType.OTHER);
-    accessControl.checkUserGlobalSysPrivilege(context);
+    accessControl.checkUserGlobalSysPrivilege(context, AuditLogOperation.DDL, node::getUdfName);
     return new DropFunctionTask(Model.TABLE, node.getUdfName());
   }
 
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java
index 95b1786..4ee69aa 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java
@@ -20,6 +20,7 @@
 package org.apache.iotdb.db.queryengine.plan.relational.security;
 
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.audit.AuditLogOperation;
 import org.apache.iotdb.commons.audit.IAuditEntity;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
 import org.apache.iotdb.commons.exception.auth.AccessDeniedException;
@@ -205,6 +206,18 @@
   void checkUserGlobalSysPrivilege(IAuditEntity auditEntity);
 
   /**
+   * Check if user has global SYSTEM privilege and record the authentication audit log with the
+   * specified operation and object.
+   *
+   * @param auditEntity records necessary info for audit log
+   * @param auditLogOperation operation type of the statement being authorized
+   * @param auditObject object affected by the statement
+   * @throws AccessDeniedException if not allowed
+   */
+  void checkUserGlobalSysPrivilege(
+      IAuditEntity auditEntity, AuditLogOperation auditLogOperation, Supplier<String> auditObject);
+
+  /**
    * Check if user has sepecified global privilege
    *
    * @param privilegeType needed privilege
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java
index 1a50f15..9e7e987 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java
@@ -547,6 +547,17 @@
   }
 
   @Override
+  public void checkUserGlobalSysPrivilege(
+      IAuditEntity auditEntity, AuditLogOperation auditLogOperation, Supplier<String> auditObject) {
+    authChecker.checkGlobalPrivilege(
+        auditEntity.getUsername(),
+        TableModelPrivilege.SYSTEM,
+        auditLogOperation,
+        auditEntity,
+        auditObject);
+  }
+
+  @Override
   public boolean hasGlobalPrivilege(IAuditEntity entity, PrivilegeType privilegeType) {
     return AuthorityChecker.SUPER_USER_ID == entity.getUserId()
         || AuthorityChecker.checkSystemPermission(entity.getUsername(), privilegeType);
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AllowAllAccessControl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AllowAllAccessControl.java
index 4777d01..890e54d 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AllowAllAccessControl.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AllowAllAccessControl.java
@@ -20,6 +20,7 @@
 package org.apache.iotdb.db.queryengine.plan.relational.security;
 
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.audit.AuditLogOperation;
 import org.apache.iotdb.commons.audit.IAuditEntity;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
 import org.apache.iotdb.commons.path.PartialPath;
@@ -107,6 +108,12 @@
   public void checkUserGlobalSysPrivilege(IAuditEntity auditEntity) {}
 
   @Override
+  public void checkUserGlobalSysPrivilege(
+      IAuditEntity auditEntity,
+      AuditLogOperation auditLogOperation,
+      Supplier<String> auditObject) {}
+
+  @Override
   public boolean hasGlobalPrivilege(IAuditEntity entity, PrivilegeType privilegeType) {
     return true;
   }
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java
index c3dfa74..c20a2b4 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java
@@ -19,12 +19,14 @@
 
 package org.apache.iotdb.db.queryengine.plan.relational.security;
 
+import org.apache.iotdb.commons.audit.AuditLogOperation;
 import org.apache.iotdb.commons.audit.IAuditEntity;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
 import org.apache.iotdb.commons.exception.auth.AccessDeniedException;
 import org.apache.iotdb.commons.queryengine.plan.relational.metadata.QualifiedObjectName;
 
 import java.util.Collection;
+import java.util.function.Supplier;
 
 public interface ITableAuthChecker {
 
@@ -108,6 +110,17 @@
   void checkGlobalPrivilege(
       String userName, TableModelPrivilege privilege, IAuditEntity auditEntity);
 
+  /**
+   * Check if user has the specified global privilege and record the authentication audit log with
+   * the operation and object of the statement being authorized.
+   */
+  void checkGlobalPrivilege(
+      String userName,
+      TableModelPrivilege privilege,
+      AuditLogOperation auditLogOperation,
+      IAuditEntity auditEntity,
+      Supplier<String> auditObject);
+
   void checkGlobalPrivileges(
       String username, Collection<PrivilegeType> privileges, IAuditEntity auditEntity);
 
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthCheckerImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthCheckerImpl.java
index 7d10285..42579f1 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthCheckerImpl.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthCheckerImpl.java
@@ -400,6 +400,30 @@
   }
 
   @Override
+  public void checkGlobalPrivilege(
+      String userName,
+      TableModelPrivilege privilege,
+      AuditLogOperation auditLogOperation,
+      IAuditEntity auditEntity,
+      Supplier<String> auditObject) {
+    if (AuthorityChecker.SUPER_USER_ID == auditEntity.getUserId()) {
+      AUDIT_LOGGER.recordObjectAuthenticationAuditLog(
+          auditEntity
+              .setAuditLogOperation(auditLogOperation)
+              .setPrivilegeType(privilege.getPrivilegeType())
+              .setResult(true),
+          auditObject);
+      return;
+    }
+    TSStatus result =
+        AuthorityChecker.getTSStatus(
+            AuthorityChecker.checkSystemPermission(userName, privilege.getPrivilegeType()),
+            privilege.getPrivilegeType());
+    recordAuditLogViaAuthenticationResult(
+        auditObject, privilege, auditLogOperation, auditEntity, result);
+  }
+
+  @Override
   public void checkGlobalPrivileges(
       String username, Collection<PrivilegeType> privileges, IAuditEntity auditEntity) {
     if (AuthorityChecker.SUPER_USER_ID == auditEntity.getUserId()) {
@@ -468,10 +492,20 @@
       TableModelPrivilege privilege,
       IAuditEntity auditEntity,
       TSStatus result) {
+    recordAuditLogViaAuthenticationResult(
+        auditObject, privilege, privilege.getAuditLogOperation(), auditEntity, result);
+  }
+
+  private void recordAuditLogViaAuthenticationResult(
+      Supplier<String> auditObject,
+      TableModelPrivilege privilege,
+      AuditLogOperation auditLogOperation,
+      IAuditEntity auditEntity,
+      TSStatus result) {
     if (result.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
       AUDIT_LOGGER.recordObjectAuthenticationAuditLog(
           auditEntity
-              .setAuditLogOperation(privilege.getAuditLogOperation())
+              .setAuditLogOperation(auditLogOperation)
               .setPrivilegeType(privilege.getPrivilegeType())
               .setResult(false),
           auditObject);
@@ -479,7 +513,7 @@
     }
     AUDIT_LOGGER.recordObjectAuthenticationAuditLog(
         auditEntity
-            .setAuditLogOperation(privilege.getAuditLogOperation())
+            .setAuditLogOperation(auditLogOperation)
             .setPrivilegeType(privilege.getPrivilegeType())
             .setResult(true),
         auditObject);
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/event/PipeTsFileInsertionEventTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/event/PipeTsFileInsertionEventTest.java
index 41cbf43..5d5be1d 100644
--- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/event/PipeTsFileInsertionEventTest.java
+++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/event/PipeTsFileInsertionEventTest.java
@@ -20,6 +20,7 @@
 package org.apache.iotdb.db.pipe.event;
 
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.audit.AuditLogOperation;
 import org.apache.iotdb.commons.audit.IAuditEntity;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
@@ -375,6 +376,12 @@
     public void checkUserGlobalSysPrivilege(IAuditEntity auditEntity) {}
 
     @Override
+    public void checkUserGlobalSysPrivilege(
+        IAuditEntity auditEntity,
+        AuditLogOperation auditLogOperation,
+        Supplier<String> auditObject) {}
+
+    @Override
     public boolean hasGlobalPrivilege(IAuditEntity auditEntity, PrivilegeType privilegeType) {
       return false;
     }
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AuthTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AuthTest.java
index e758e49..9aaada2 100644
--- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AuthTest.java
+++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AuthTest.java
@@ -19,6 +19,9 @@
 
 package org.apache.iotdb.db.queryengine.plan.relational.analyzer;
 
+import org.apache.iotdb.commons.audit.AuditLogOperation;
+import org.apache.iotdb.commons.audit.UserEntity;
+import org.apache.iotdb.commons.auth.entity.PrivilegeType;
 import org.apache.iotdb.commons.exception.auth.AccessDeniedException;
 import org.apache.iotdb.commons.queryengine.common.SessionInfo;
 import org.apache.iotdb.commons.queryengine.common.SqlDialect;
@@ -30,6 +33,7 @@
 import org.apache.iotdb.db.queryengine.plan.execution.config.TableConfigTaskVisitor;
 import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControlImpl;
 import org.apache.iotdb.db.queryengine.plan.relational.security.ITableAuthChecker;
+import org.apache.iotdb.db.queryengine.plan.relational.security.ITableAuthCheckerImpl;
 import org.apache.iotdb.db.queryengine.plan.relational.security.TableModelPrivilege;
 import org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckVisitor;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser;
@@ -47,9 +51,13 @@
 import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.QUERY_ID;
 import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.TEST_MATADATA;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 public class AuthTest {
 
@@ -206,6 +214,49 @@
 
   }
 
+  @Test
+  public void testFunctionManagementAuditOperation() {
+    ITableAuthChecker authChecker = Mockito.mock(ITableAuthChecker.class);
+    String functionName = "test_function";
+
+    analyzeConfigTask(
+        String.format(
+            "CREATE FUNCTION %s AS 'org.apache.iotdb.db.query.udf.example.relational.AllSum'",
+            functionName),
+        user1,
+        authChecker);
+    analyzeConfigTask(String.format("DROP FUNCTION %s", functionName), user1, authChecker);
+
+    verify(authChecker, times(2))
+        .checkGlobalPrivilege(
+            eq(user1),
+            eq(TableModelPrivilege.SYSTEM),
+            eq(AuditLogOperation.DDL),
+            any(),
+            argThat(auditObject -> functionName.equals(auditObject.get())));
+  }
+
+  @Test
+  public void testExplicitGlobalPrivilegeAuditOperation() {
+    ITableAuthCheckerImpl authChecker = new ITableAuthCheckerImpl();
+    UserEntity auditEntity = new UserEntity(0, userRoot, "127.0.0.1");
+
+    authChecker.checkGlobalPrivilege(
+        userRoot,
+        TableModelPrivilege.SYSTEM,
+        AuditLogOperation.DDL,
+        auditEntity,
+        () -> "test_function");
+
+    assertEquals(AuditLogOperation.DDL, auditEntity.getAuditLogOperation());
+    assertEquals(Collections.singletonList(PrivilegeType.SYSTEM), auditEntity.getPrivilegeTypes());
+    assertTrue(auditEntity.getResult());
+
+    UserEntity defaultAuditEntity = new UserEntity(0, userRoot, "127.0.0.1");
+    authChecker.checkGlobalPrivilege(userRoot, TableModelPrivilege.SYSTEM, defaultAuditEntity);
+    assertEquals(AuditLogOperation.CONTROL, defaultAuditEntity.getAuditLogOperation());
+  }
+
   private void analyzeSQL(String sql, String userName, ITableAuthChecker authChecker) {
     analyzeSQL(sql, userName, authChecker, null);
   }