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

kirs pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new b263bb8a504 [feat](authentication)Support AuthenticationIntegration 
DDL (#60902)
b263bb8a504 is described below

commit b263bb8a504726ad99eef95657cc4f63a3c13df0
Author: Calvin Kirs <[email protected]>
AuthorDate: Thu Mar 5 14:10:38 2026 +0800

    [feat](authentication)Support AuthenticationIntegration DDL (#60902)
    
    This PR adds DDL support for managing AUTHENTICATION INTEGRATION objects
    in Doris FE.
    https://github.com/apache/doris/issues/60361
      It introduces:
    
      1. CREATE / ALTER / DROP AUTHENTICATION INTEGRATION syntax.
      2. Validation rules:
          - type is required on CREATE.
          - type cannot be changed in ALTER.
      3. ADMIN privilege checks for these operations.
    4. Metadata persistence and replay support (edit log + image load/save).
    5. Unit tests and regression tests for parser, command behavior, and
    persistence.
    ```
    
    
    CREATE AUTHENTICATION INTEGRATION <integration_name>
      WITH PROPERTIES (
          'type' = '<type>'
          [, '<property_key>' = '<property_value>' ...]
      )
      [COMMENT '<comment>'];
    
      ALTER AUTHENTICATION INTEGRATION <integration_name>
      SET PROPERTIES (
          '<property_key>' = '<property_value>'
          [, '<property_key>' = '<property_value>' ...]
      );
    
      ALTER AUTHENTICATION INTEGRATION <integration_name>
      SET COMMENT '<comment>';
    
      DROP AUTHENTICATION INTEGRATION [IF EXISTS] <integration_name>;
    -- Optional cleanup
      DROP AUTHENTICATION INTEGRATION IF EXISTS corp_ldap;
    
      -- Create an authentication integration
      CREATE AUTHENTICATION INTEGRATION corp_ldap
      WITH PROPERTIES (
          'type'='ldap',
          'ldap.server'='ldap://127.0.0.1:389',
          'ldap.base_dn'='dc=example,dc=com',
          'ldap.admin_dn'='cn=admin,dc=example,dc=com',
          'ldap.admin_password'='123456'
      )
      COMMENT 'Corporate LDAP integration';
    
      -- Alter properties (type cannot be changed)
      ALTER AUTHENTICATION INTEGRATION corp_ldap
      SET PROPERTIES (
          'ldap.server'='ldap://127.0.0.1:1389',
          'ldap.admin_password'='abcdef'
      );
    
      -- Alter comment
      ALTER AUTHENTICATION INTEGRATION corp_ldap
      SET COMMENT 'Updated LDAP integration config';
    
      -- Drop integration
      DROP AUTHENTICATION INTEGRATION corp_ldap;
    
      -- Drop safely if not exists
      DROP AUTHENTICATION INTEGRATION IF EXISTS corp_ldap_not_exist;
    
      -- Negative case: missing required 'type' (should fail)
      CREATE AUTHENTICATION INTEGRATION bad_case
      WITH PROPERTIES (
          'ldap.server'='ldap://127.0.0.1:389'
      );
    
      -- Negative case: modifying 'type' in ALTER (should fail)
      ALTER AUTHENTICATION INTEGRATION corp_ldap
      SET PROPERTIES (
          'type'='oidc'
      );
    
    
    ```
---
 .../antlr4/org/apache/doris/nereids/DorisLexer.g4  |   2 +
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |  15 ++
 .../AuthenticationIntegrationMeta.java             | 162 ++++++++++++++
 .../AuthenticationIntegrationMgr.java              | 204 ++++++++++++++++++
 .../main/java/org/apache/doris/catalog/Env.java    |  26 +++
 .../org/apache/doris/journal/JournalEntity.java    |  13 ++
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  77 +++++++
 .../parser/LogicalPlanBuilderForEncryption.java    |  25 +++
 .../apache/doris/nereids/trees/plans/PlanType.java |   3 +
 .../AlterAuthenticationIntegrationCommand.java     | 135 ++++++++++++
 .../CreateAuthenticationIntegrationCommand.java    |  94 ++++++++
 .../DropAuthenticationIntegrationCommand.java      |  65 ++++++
 .../trees/plans/visitor/CommandVisitor.java        |  18 ++
 .../DropAuthenticationIntegrationOperationLog.java |  53 +++++
 .../java/org/apache/doris/persist/EditLog.java     |  29 +++
 .../org/apache/doris/persist/OperationType.java    |   3 +
 .../doris/persist/meta/MetaPersistMethod.java      |   9 +
 .../doris/persist/meta/PersistMetaModules.java     |   8 +-
 .../AuthenticationIntegrationMetaTest.java         | 168 +++++++++++++++
 .../AuthenticationIntegrationMgrTest.java          | 206 ++++++++++++++++++
 .../AuthenticationIntegrationParserTest.java       | 111 ++++++++++
 .../AuthenticationIntegrationCommandTest.java      | 238 +++++++++++++++++++++
 ...pAuthenticationIntegrationOperationLogTest.java |  47 ++++
 .../test_authentication_integration_auth.groovy    |  78 +++++++
 24 files changed, 1787 insertions(+), 2 deletions(-)

diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4 
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
index 0b2b5bf1ef7..7dbefd71193 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
@@ -86,6 +86,7 @@ AS: 'AS';
 ASC: 'ASC';
 AT: 'AT';
 AUTHORS: 'AUTHORS';
+AUTHENTICATION: 'AUTHENTICATION';
 AUTO: 'AUTO';
 AUTO_INCREMENT: 'AUTO_INCREMENT';
 ALWAYS: 'ALWAYS';
@@ -294,6 +295,7 @@ IGNORE: 'IGNORE';
 IMMEDIATE: 'IMMEDIATE';
 IN: 'IN';
 INCREMENTAL: 'INCREMENTAL';
+INTEGRATION: 'INTEGRATION';
 INDEX: 'INDEX';
 INDEXES: 'INDEXES';
 INFILE: 'INFILE';
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index 535fc479716..556073971a9 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -208,6 +208,8 @@ supportedCreateStatement
         LIKE existedTable=multipartIdentifier
         (WITH ROLLUP (rollupNames=identifierList)?)?                      
#createTableLike
     | CREATE ROLE (IF NOT EXISTS)? name=identifierOrText (COMMENT 
STRING_LITERAL)?    #createRole
+    | CREATE AUTHENTICATION INTEGRATION (IF NOT EXISTS)? 
integrationName=identifier
+        properties=propertyClause commentSpec?                                 
 #createAuthenticationIntegration
     | CREATE WORKLOAD GROUP (IF NOT EXISTS)?
         name=identifierOrText (FOR computeGroup=identifierOrText)? 
properties=propertyClause? #createWorkloadGroup
     | CREATE CATALOG (IF NOT EXISTS)? catalogName=identifier
@@ -292,6 +294,12 @@ supportedAlterStatement
         properties=propertyClause?                                             
             #alterComputeGroup
     | ALTER CATALOG name=identifier SET PROPERTIES
         LEFT_PAREN propertyItemList RIGHT_PAREN                                
             #alterCatalogProperties        
+    | ALTER AUTHENTICATION INTEGRATION integrationName=identifier
+        SET properties=propertyClause                                          
             #alterAuthenticationIntegrationProperties
+    | ALTER AUTHENTICATION INTEGRATION integrationName=identifier
+        UNSET properties=propertyKeyClause                                     
             #alterAuthenticationIntegrationUnsetProperties
+    | ALTER AUTHENTICATION INTEGRATION integrationName=identifier
+        SET COMMENT comment=STRING_LITERAL                                     
              #alterAuthenticationIntegrationComment
     | ALTER WORKLOAD POLICY name=identifierOrText
         properties=propertyClause?                                             
             #alterWorkloadPolicy
     | ALTER SQL_BLOCK_RULE name=identifier properties=propertyClause?          
             #alterSqlBlockRule
@@ -335,6 +343,7 @@ supportedDropStatement
     | DROP STORAGE POLICY (IF EXISTS)? name=identifier                         
 #dropStoragePolicy
     | DROP WORKLOAD GROUP (IF EXISTS)? name=identifierOrText (FOR 
computeGroup=identifierOrText)?                    #dropWorkloadGroup
     | DROP CATALOG (IF EXISTS)? name=identifier                                
 #dropCatalog
+    | DROP AUTHENTICATION INTEGRATION (IF EXISTS)? name=identifier             
 #dropAuthenticationIntegration
     | DROP FILE name=STRING_LITERAL
         ((FROM | IN) database=identifier)? properties=propertyClause           
 #dropFile
     | DROP WORKLOAD POLICY (IF EXISTS)? name=identifierOrText                  
 #dropWorkloadPolicy
@@ -1456,6 +1465,10 @@ propertyClause
     : PROPERTIES LEFT_PAREN fileProperties=propertyItemList RIGHT_PAREN
     ;
 
+propertyKeyClause
+    : PROPERTIES LEFT_PAREN keys+=propertyKey (COMMA keys+=propertyKey)* 
RIGHT_PAREN
+    ;
+
 propertyItemList
     : properties+=propertyItem (COMMA properties+=propertyItem)*
     ;
@@ -1957,6 +1970,7 @@ nonReserved
     | ARRAY
     | AT
     | AUTHORS
+    | AUTHENTICATION
     | AUTO_INCREMENT
     | BACKENDS
     | BACKUP
@@ -2108,6 +2122,7 @@ nonReserved
     | IGNORE
     | IMMEDIATE
     | INCREMENTAL
+    | INTEGRATION
     | INDEXES
     | INSERT
     | INVERTED
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/authentication/AuthenticationIntegrationMeta.java
 
b/fe/fe-core/src/main/java/org/apache/doris/authentication/AuthenticationIntegrationMeta.java
new file mode 100644
index 00000000000..6fb64a55a95
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/authentication/AuthenticationIntegrationMeta.java
@@ -0,0 +1,162 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.authentication;
+
+import org.apache.doris.common.DdlException;
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.io.Writable;
+import org.apache.doris.persist.gson.GsonUtils;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Persistent metadata for AUTHENTICATION INTEGRATION.
+ */
+public class AuthenticationIntegrationMeta implements Writable {
+    public static final String TYPE_PROPERTY = "type";
+
+    @SerializedName(value = "n")
+    private String name;
+    @SerializedName(value = "t")
+    private String type;
+    @SerializedName(value = "p")
+    private Map<String, String> properties;
+    @SerializedName(value = "c")
+    private String comment;
+
+    private AuthenticationIntegrationMeta() {
+        this.name = "";
+        this.type = "";
+        this.properties = Collections.emptyMap();
+        this.comment = null;
+    }
+
+    public AuthenticationIntegrationMeta(String name, String type, Map<String, 
String> properties, String comment) {
+        this.name = Objects.requireNonNull(name, "name can not be null");
+        this.type = Objects.requireNonNull(type, "type can not be null");
+        this.properties = Collections.unmodifiableMap(
+                new LinkedHashMap<>(Objects.requireNonNull(properties, 
"properties can not be null")));
+        this.comment = comment;
+    }
+
+    /**
+     * Build metadata from CREATE SQL arguments.
+     */
+    public static AuthenticationIntegrationMeta fromCreateSql(
+            String integrationName, Map<String, String> properties, String 
comment) throws DdlException {
+        if (properties == null || properties.isEmpty()) {
+            throw new DdlException("Property 'type' is required in CREATE 
AUTHENTICATION INTEGRATION");
+        }
+        String type = null;
+        Map<String, String> copiedProperties = new LinkedHashMap<>();
+        for (Map.Entry<String, String> entry : properties.entrySet()) {
+            String key = Objects.requireNonNull(entry.getKey(), "property key 
can not be null");
+            if (TYPE_PROPERTY.equalsIgnoreCase(key)) {
+                if (type != null) {
+                    throw new DdlException("Property 'type' is duplicated in 
CREATE AUTHENTICATION INTEGRATION");
+                }
+                type = entry.getValue();
+                continue;
+            }
+            copiedProperties.put(key, entry.getValue());
+        }
+        if (type == null || type.isEmpty()) {
+            throw new DdlException("Property 'type' is required in CREATE 
AUTHENTICATION INTEGRATION");
+        }
+        return new AuthenticationIntegrationMeta(integrationName, type, 
copiedProperties, comment);
+    }
+
+    /**
+     * Build a new metadata object after ALTER ... SET PROPERTIES.
+     */
+    public AuthenticationIntegrationMeta withAlterProperties(Map<String, 
String> propertiesDelta) throws DdlException {
+        if (propertiesDelta == null || propertiesDelta.isEmpty()) {
+            throw new DdlException("ALTER AUTHENTICATION INTEGRATION should 
contain at least one property");
+        }
+        for (String key : propertiesDelta.keySet()) {
+            if (TYPE_PROPERTY.equalsIgnoreCase(key)) {
+                throw new DdlException("ALTER AUTHENTICATION INTEGRATION does 
not allow modifying property 'type'");
+            }
+        }
+        Map<String, String> mergedProperties = new LinkedHashMap<>(properties);
+        mergedProperties.putAll(propertiesDelta);
+        return new AuthenticationIntegrationMeta(name, type, mergedProperties, 
comment);
+    }
+
+    /**
+     * Build a new metadata object after ALTER ... UNSET PROPERTIES.
+     */
+    public AuthenticationIntegrationMeta withUnsetProperties(Set<String> 
propertiesToUnset) throws DdlException {
+        if (propertiesToUnset == null || propertiesToUnset.isEmpty()) {
+            throw new DdlException("ALTER AUTHENTICATION INTEGRATION should 
contain at least one property");
+        }
+        Map<String, String> reducedProperties = new 
LinkedHashMap<>(properties);
+        for (String key : propertiesToUnset) {
+            if (TYPE_PROPERTY.equalsIgnoreCase(key)) {
+                throw new DdlException("ALTER AUTHENTICATION INTEGRATION does 
not allow modifying property 'type'");
+            }
+            reducedProperties.remove(key);
+        }
+        return new AuthenticationIntegrationMeta(name, type, 
reducedProperties, comment);
+    }
+
+    public AuthenticationIntegrationMeta withComment(String newComment) {
+        return new AuthenticationIntegrationMeta(name, type, properties, 
newComment);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    public String getComment() {
+        return comment;
+    }
+
+    public Map<String, String> toSqlPropertiesView() {
+        Map<String, String> allProperties = new LinkedHashMap<>();
+        allProperties.put(TYPE_PROPERTY, type);
+        allProperties.putAll(properties);
+        return allProperties;
+    }
+
+    @Override
+    public void write(DataOutput out) throws IOException {
+        Text.writeString(out, GsonUtils.GSON.toJson(this));
+    }
+
+    public static AuthenticationIntegrationMeta read(DataInput in) throws 
IOException {
+        return GsonUtils.GSON.fromJson(Text.readString(in), 
AuthenticationIntegrationMeta.class);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/authentication/AuthenticationIntegrationMgr.java
 
b/fe/fe-core/src/main/java/org/apache/doris/authentication/AuthenticationIntegrationMgr.java
new file mode 100644
index 00000000000..6b821883054
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/authentication/AuthenticationIntegrationMgr.java
@@ -0,0 +1,204 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.authentication;
+
+import org.apache.doris.common.DdlException;
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.io.Writable;
+import org.apache.doris.persist.DropAuthenticationIntegrationOperationLog;
+import org.apache.doris.persist.gson.GsonUtils;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Manager for AUTHENTICATION INTEGRATION metadata.
+ */
+public class AuthenticationIntegrationMgr implements Writable {
+    private final ReentrantReadWriteLock lock = new 
ReentrantReadWriteLock(true);
+
+    @SerializedName(value = "nTi")
+    private Map<String, AuthenticationIntegrationMeta> nameToIntegration = new 
LinkedHashMap<>();
+
+    private void readLock() {
+        lock.readLock().lock();
+    }
+
+    private void readUnlock() {
+        lock.readLock().unlock();
+    }
+
+    private void writeLock() {
+        lock.writeLock().lock();
+    }
+
+    private void writeUnlock() {
+        lock.writeLock().unlock();
+    }
+
+    public void createAuthenticationIntegration(
+            String integrationName, boolean ifNotExists, Map<String, String> 
properties, String comment)
+            throws DdlException {
+        AuthenticationIntegrationMeta meta =
+                AuthenticationIntegrationMeta.fromCreateSql(integrationName, 
properties, comment);
+        writeLock();
+        try {
+            if (nameToIntegration.containsKey(integrationName)) {
+                if (ifNotExists) {
+                    return;
+                }
+                throw new DdlException("Authentication integration " + 
integrationName + " already exists");
+            }
+            nameToIntegration.put(integrationName, meta);
+            // TODO(authentication-integration): Re-enable edit log persistence
+            // when authentication integration is fully integrated.
+            // 
Env.getCurrentEnv().getEditLog().logCreateAuthenticationIntegration(meta);
+        } finally {
+            writeUnlock();
+        }
+    }
+
+    public void alterAuthenticationIntegrationProperties(
+            String integrationName, Map<String, String> properties) throws 
DdlException {
+        writeLock();
+        try {
+            AuthenticationIntegrationMeta current = 
getOrThrow(integrationName);
+            AuthenticationIntegrationMeta updated = 
current.withAlterProperties(properties);
+            nameToIntegration.put(integrationName, updated);
+            // TODO(authentication-integration): Re-enable edit log persistence
+            // when authentication integration is fully integrated.
+            // 
Env.getCurrentEnv().getEditLog().logAlterAuthenticationIntegration(updated);
+        } finally {
+            writeUnlock();
+        }
+    }
+
+    public void alterAuthenticationIntegrationUnsetProperties(
+            String integrationName, Set<String> propertiesToUnset) throws 
DdlException {
+        writeLock();
+        try {
+            AuthenticationIntegrationMeta current = 
getOrThrow(integrationName);
+            AuthenticationIntegrationMeta updated = 
current.withUnsetProperties(propertiesToUnset);
+            nameToIntegration.put(integrationName, updated);
+            // TODO(authentication-integration): Re-enable edit log persistence
+            // when authentication integration is fully integrated.
+            // 
Env.getCurrentEnv().getEditLog().logAlterAuthenticationIntegration(updated);
+        } finally {
+            writeUnlock();
+        }
+    }
+
+    public void alterAuthenticationIntegrationComment(String integrationName, 
String comment) throws DdlException {
+        writeLock();
+        try {
+            AuthenticationIntegrationMeta current = 
getOrThrow(integrationName);
+            AuthenticationIntegrationMeta updated = 
current.withComment(comment);
+            nameToIntegration.put(integrationName, updated);
+            // TODO(authentication-integration): Re-enable edit log persistence
+            // when authentication integration is fully integrated.
+            // 
Env.getCurrentEnv().getEditLog().logAlterAuthenticationIntegration(updated);
+        } finally {
+            writeUnlock();
+        }
+    }
+
+    public void dropAuthenticationIntegration(String integrationName, boolean 
ifExists) throws DdlException {
+        writeLock();
+        try {
+            if (!nameToIntegration.containsKey(integrationName)) {
+                if (ifExists) {
+                    return;
+                }
+                throw new DdlException("Authentication integration " + 
integrationName + " does not exist");
+            }
+            nameToIntegration.remove(integrationName);
+            // TODO(authentication-integration): Re-enable edit log persistence
+            // when authentication integration is fully integrated.
+            // 
Env.getCurrentEnv().getEditLog().logDropAuthenticationIntegration(
+            //         new 
DropAuthenticationIntegrationOperationLog(integrationName));
+        } finally {
+            writeUnlock();
+        }
+    }
+
+    public void 
replayCreateAuthenticationIntegration(AuthenticationIntegrationMeta meta) {
+        writeLock();
+        try {
+            nameToIntegration.put(meta.getName(), meta);
+        } finally {
+            writeUnlock();
+        }
+    }
+
+    public void 
replayAlterAuthenticationIntegration(AuthenticationIntegrationMeta meta) {
+        writeLock();
+        try {
+            nameToIntegration.put(meta.getName(), meta);
+        } finally {
+            writeUnlock();
+        }
+    }
+
+    public void 
replayDropAuthenticationIntegration(DropAuthenticationIntegrationOperationLog 
log) {
+        writeLock();
+        try {
+            nameToIntegration.remove(log.getIntegrationName());
+        } finally {
+            writeUnlock();
+        }
+    }
+
+    public Map<String, AuthenticationIntegrationMeta> 
getAuthenticationIntegrations() {
+        readLock();
+        try {
+            return Collections.unmodifiableMap(new 
LinkedHashMap<>(nameToIntegration));
+        } finally {
+            readUnlock();
+        }
+    }
+
+    @Override
+    public void write(DataOutput out) throws IOException {
+        Text.writeString(out, GsonUtils.GSON.toJson(this));
+    }
+
+    public static AuthenticationIntegrationMgr read(DataInput in) throws 
IOException {
+        String json = Text.readString(in);
+        AuthenticationIntegrationMgr mgr = GsonUtils.GSON.fromJson(json, 
AuthenticationIntegrationMgr.class);
+        if (mgr.nameToIntegration == null) {
+            mgr.nameToIntegration = new LinkedHashMap<>();
+        }
+        return mgr;
+    }
+
+    private AuthenticationIntegrationMeta getOrThrow(String integrationName) 
throws DdlException {
+        AuthenticationIntegrationMeta meta = 
nameToIntegration.get(integrationName);
+        if (meta == null) {
+            throw new DdlException("Authentication integration " + 
integrationName + " does not exist");
+        }
+        return meta;
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
index 77fa6a0f355..45f3d5513bd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
@@ -27,6 +27,7 @@ import org.apache.doris.alter.SystemHandler;
 import org.apache.doris.analysis.DistributionDesc;
 import org.apache.doris.analysis.Expr;
 import org.apache.doris.analysis.SlotRef;
+import org.apache.doris.authentication.AuthenticationIntegrationMgr;
 import org.apache.doris.backup.BackupHandler;
 import org.apache.doris.backup.RestoreJob;
 import org.apache.doris.binlog.BinlogGcer;
@@ -375,6 +376,7 @@ public class Env {
     private RoutineLoadManager routineLoadManager;
     private GroupCommitManager groupCommitManager;
     private SqlBlockRuleMgr sqlBlockRuleMgr;
+    private AuthenticationIntegrationMgr authenticationIntegrationMgr;
     private ExportMgr exportMgr;
     private Alter alter;
     private ConsistencyChecker consistencyChecker;
@@ -704,6 +706,7 @@ public class Env {
         this.routineLoadManager = 
EnvFactory.getInstance().createRoutineLoadManager();
         this.groupCommitManager = new GroupCommitManager();
         this.sqlBlockRuleMgr = new SqlBlockRuleMgr();
+        this.authenticationIntegrationMgr = new AuthenticationIntegrationMgr();
         this.exportMgr = new ExportMgr();
         this.alter = new Alter();
         this.consistencyChecker = new ConsistencyChecker();
@@ -2488,6 +2491,17 @@ public class Env {
         return checksum;
     }
 
+    public long loadAuthenticationIntegrations(DataInputStream in, long 
checksum) throws IOException {
+        // TODO(authentication-integration): Re-enable image persistence
+        // when authentication integration is fully integrated.
+        // Consume persisted bytes to keep image stream alignment,
+        // but do not restore into in-memory state for now.
+        AuthenticationIntegrationMgr.read(in);
+        authenticationIntegrationMgr = new AuthenticationIntegrationMgr();
+        LOG.info("skip replay authentication integrations from image 
temporarily");
+        return checksum;
+    }
+
     /**
      * Load policy through file.
      **/
@@ -2800,6 +2814,14 @@ public class Env {
         return checksum;
     }
 
+    public long saveAuthenticationIntegrations(CountingDataOutputStream out, 
long checksum) throws IOException {
+        // TODO(authentication-integration): Re-enable image persistence
+        // when authentication integration is fully integrated.
+        // Persist an empty manager temporarily.
+        new AuthenticationIntegrationMgr().write(out);
+        return checksum;
+    }
+
     public long savePolicy(CountingDataOutputStream out, long checksum) throws 
IOException {
         Env.getCurrentEnv().getPolicyMgr().write(out);
         return checksum;
@@ -5152,6 +5174,10 @@ public class Env {
         return sqlBlockRuleMgr;
     }
 
+    public AuthenticationIntegrationMgr getAuthenticationIntegrationMgr() {
+        return authenticationIntegrationMgr;
+    }
+
     public RoutineLoadTaskScheduler getRoutineLoadTaskScheduler() {
         return routineLoadTaskScheduler;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java 
b/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
index 8f5e3f17865..0f1712110f1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
@@ -21,6 +21,7 @@ import org.apache.doris.alter.AlterJobV2;
 import org.apache.doris.alter.BatchAlterJobPersistInfo;
 import org.apache.doris.alter.IndexChangeJob;
 import org.apache.doris.analysis.UserIdentity;
+import org.apache.doris.authentication.AuthenticationIntegrationMeta;
 import org.apache.doris.backup.BackupJob;
 import org.apache.doris.backup.Repository;
 import org.apache.doris.backup.RestoreJob;
@@ -89,6 +90,7 @@ import org.apache.doris.persist.CreateTableInfo;
 import org.apache.doris.persist.DatabaseInfo;
 import org.apache.doris.persist.DictionaryDecreaseVersionInfo;
 import org.apache.doris.persist.DictionaryIncreaseVersionInfo;
+import org.apache.doris.persist.DropAuthenticationIntegrationOperationLog;
 import org.apache.doris.persist.DropDbInfo;
 import org.apache.doris.persist.DropDictionaryPersistInfo;
 import org.apache.doris.persist.DropInfo;
@@ -709,6 +711,17 @@ public class JournalEntity implements Writable {
                 isRead = true;
                 break;
             }
+            case OperationType.OP_CREATE_AUTHENTICATION_INTEGRATION:
+            case OperationType.OP_ALTER_AUTHENTICATION_INTEGRATION: {
+                data = AuthenticationIntegrationMeta.read(in);
+                isRead = true;
+                break;
+            }
+            case OperationType.OP_DROP_AUTHENTICATION_INTEGRATION: {
+                data = DropAuthenticationIntegrationOperationLog.read(in);
+                isRead = true;
+                break;
+            }
             case OperationType.OP_MODIFY_TABLE_ENGINE: {
                 data = ModifyTableEngineOperationLog.read(in);
                 isRead = true;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 2675541b4f8..9970b7a4e5f 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -634,6 +634,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.AdminSetPartitionVersionCom
 import 
org.apache.doris.nereids.trees.plans.commands.AdminSetReplicaStatusCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.AdminSetReplicaVersionCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.AdminSetTableStatusCommand;
+import 
org.apache.doris.nereids.trees.plans.commands.AlterAuthenticationIntegrationCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.AlterCatalogCommentCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.AlterCatalogPropertiesCommand;
 import org.apache.doris.nereids.trees.plans.commands.AlterCatalogRenameCommand;
@@ -675,6 +676,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.CleanQueryStatsCommand;
 import org.apache.doris.nereids.trees.plans.commands.Command;
 import org.apache.doris.nereids.trees.plans.commands.Constraint;
 import org.apache.doris.nereids.trees.plans.commands.CopyIntoCommand;
+import 
org.apache.doris.nereids.trees.plans.commands.CreateAuthenticationIntegrationCommand;
 import org.apache.doris.nereids.trees.plans.commands.CreateCatalogCommand;
 import org.apache.doris.nereids.trees.plans.commands.CreateDatabaseCommand;
 import org.apache.doris.nereids.trees.plans.commands.CreateDictionaryCommand;
@@ -706,6 +708,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.DeleteFromCommand;
 import org.apache.doris.nereids.trees.plans.commands.DeleteFromUsingCommand;
 import org.apache.doris.nereids.trees.plans.commands.DescribeCommand;
 import org.apache.doris.nereids.trees.plans.commands.DropAnalyzeJobCommand;
+import 
org.apache.doris.nereids.trees.plans.commands.DropAuthenticationIntegrationCommand;
 import org.apache.doris.nereids.trees.plans.commands.DropCachedStatsCommand;
 import org.apache.doris.nereids.trees.plans.commands.DropCatalogCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.DropCatalogRecycleBinCommand;
@@ -2121,6 +2124,18 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         return propertiesMap.build();
     }
 
+    @Override
+    public Set<String> 
visitPropertyKeyClause(DorisParser.PropertyKeyClauseContext ctx) {
+        if (ctx == null || ctx.keys == null) {
+            return ImmutableSet.of();
+        }
+        ImmutableSet.Builder<String> propertyKeys = ImmutableSet.builder();
+        for (PropertyKeyContext propertyKey : ctx.keys) {
+            propertyKeys.add(parsePropertyKey(propertyKey));
+        }
+        return propertyKeys.build();
+    }
+
     @Override
     public BrokerDesc 
visitWithRemoteStorageSystem(WithRemoteStorageSystemContext ctx) {
         BrokerDesc brokerDesc = null;
@@ -5042,6 +5057,15 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         return item.getText();
     }
 
+    private boolean containsPropertyKeyIgnoreCase(Iterable<String> 
propertyKeys, String expectedKey) {
+        for (String key : propertyKeys) {
+            if (key.equalsIgnoreCase(expectedKey)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private ExplainLevel parseExplainPlanType(PlanTypeContext planTypeContext) 
{
         if (planTypeContext == null || planTypeContext.ALL() != null) {
             return ExplainLevel.ALL_PLAN;
@@ -7061,6 +7085,19 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         return new CreateCatalogCommand(catalogName, ifNotExists, 
resourceName, comment, properties);
     }
 
+    @Override
+    public LogicalPlan visitCreateAuthenticationIntegration(
+            DorisParser.CreateAuthenticationIntegrationContext ctx) {
+        boolean ifNotExists = ctx.IF() != null;
+        String integrationName = stripQuotes(ctx.integrationName.getText());
+        Map<String, String> properties = 
Maps.newHashMap(visitPropertyClause(ctx.properties));
+        if (!containsPropertyKeyIgnoreCase(properties.keySet(), "type")) {
+            throw new ParseException("Property 'type' is required in CREATE 
AUTHENTICATION INTEGRATION", ctx);
+        }
+        String comment = ctx.commentSpec() == null ? null : 
stripQuotes(ctx.commentSpec().STRING_LITERAL().getText());
+        return new CreateAuthenticationIntegrationCommand(integrationName, 
ifNotExists, properties, comment);
+    }
+
     @Override
     public LogicalPlan visitShowStages(ShowStagesContext ctx) {
         return new ShowStagesCommand();
@@ -7097,6 +7134,38 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         return new AlterCatalogPropertiesCommand(catalogName, properties);
     }
 
+    @Override
+    public LogicalPlan visitAlterAuthenticationIntegrationProperties(
+            DorisParser.AlterAuthenticationIntegrationPropertiesContext ctx) {
+        String integrationName = stripQuotes(ctx.integrationName.getText());
+        Map<String, String> properties = 
Maps.newHashMap(visitPropertyClause(ctx.properties));
+        if (containsPropertyKeyIgnoreCase(properties.keySet(), "type")) {
+            throw new ParseException(
+                    "ALTER AUTHENTICATION INTEGRATION does not allow modifying 
property 'type'", ctx);
+        }
+        return 
AlterAuthenticationIntegrationCommand.forSetProperties(integrationName, 
properties);
+    }
+
+    @Override
+    public LogicalPlan visitAlterAuthenticationIntegrationUnsetProperties(
+            DorisParser.AlterAuthenticationIntegrationUnsetPropertiesContext 
ctx) {
+        String integrationName = stripQuotes(ctx.integrationName.getText());
+        Set<String> unsetProperties = visitPropertyKeyClause(ctx.properties);
+        if (containsPropertyKeyIgnoreCase(unsetProperties, "type")) {
+            throw new ParseException(
+                    "ALTER AUTHENTICATION INTEGRATION does not allow modifying 
property 'type'", ctx);
+        }
+        return 
AlterAuthenticationIntegrationCommand.forUnsetProperties(integrationName, 
unsetProperties);
+    }
+
+    @Override
+    public LogicalPlan visitAlterAuthenticationIntegrationComment(
+            DorisParser.AlterAuthenticationIntegrationCommentContext ctx) {
+        String integrationName = stripQuotes(ctx.integrationName.getText());
+        String comment = stripQuotes(ctx.comment.getText());
+        return 
AlterAuthenticationIntegrationCommand.forSetComment(integrationName, comment);
+    }
+
     @Override
     public RecoverTableCommand visitRecoverTable(RecoverTableContext ctx) {
         List<String> dbTblNameParts = visitMultipartIdentifier(ctx.name);
@@ -7215,6 +7284,14 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         return new DropCatalogCommand(catalogName, ifExists);
     }
 
+    @Override
+    public LogicalPlan visitDropAuthenticationIntegration(
+            DorisParser.DropAuthenticationIntegrationContext ctx) {
+        String integrationName = stripQuotes(ctx.name.getText());
+        boolean ifExists = ctx.EXISTS() != null;
+        return new DropAuthenticationIntegrationCommand(ifExists, 
integrationName);
+    }
+
     @Override
     public LogicalPlan visitCreateEncryptkey(CreateEncryptkeyContext ctx) {
         List<String> nameParts = 
visitMultipartIdentifier(ctx.multipartIdentifier());
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForEncryption.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForEncryption.java
index 3c9eea1d386..0555ea108dc 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForEncryption.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForEncryption.java
@@ -132,6 +132,18 @@ public class LogicalPlanBuilderForEncryption extends 
LogicalPlanBuilder {
         return super.visitCreateStorageVault(ctx);
     }
 
+    // create authentication integration clause
+    @Override
+    public LogicalPlan 
visitCreateAuthenticationIntegration(DorisParser.CreateAuthenticationIntegrationContext
 ctx) {
+        if (ctx.properties != null && ctx.properties.fileProperties != null) {
+            DorisParser.PropertyClauseContext propertyClauseContext = 
ctx.properties;
+            encryptProperty(visitPropertyClause(propertyClauseContext),
+                    propertyClauseContext.fileProperties.start.getStartIndex(),
+                    propertyClauseContext.fileProperties.stop.getStopIndex());
+        }
+        return super.visitCreateAuthenticationIntegration(ctx);
+    }
+
     // alter storage vault clause
     @Override
     public LogicalPlan 
visitAlterStorageVault(DorisParser.AlterStorageVaultContext ctx) {
@@ -144,6 +156,19 @@ public class LogicalPlanBuilderForEncryption extends 
LogicalPlanBuilder {
         return super.visitAlterStorageVault(ctx);
     }
 
+    // alter authentication integration properties clause
+    @Override
+    public LogicalPlan visitAlterAuthenticationIntegrationProperties(
+            DorisParser.AlterAuthenticationIntegrationPropertiesContext ctx) {
+        if (ctx.properties != null && ctx.properties.fileProperties != null) {
+            DorisParser.PropertyClauseContext propertyClauseContext = 
ctx.properties;
+            encryptProperty(visitPropertyClause(propertyClauseContext),
+                    propertyClauseContext.fileProperties.start.getStartIndex(),
+                    propertyClauseContext.fileProperties.stop.getStopIndex());
+        }
+        return super.visitAlterAuthenticationIntegrationProperties(ctx);
+    }
+
     // select from tvf
     @Override
     public LogicalPlan 
visitTableValuedFunction(DorisParser.TableValuedFunctionContext ctx) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
index dcff41a3175..486d23b9216 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
@@ -158,6 +158,7 @@ public enum PlanType {
     COPY_INTO_COMMAND,
     CREATE_POLICY_COMMAND,
     CREATE_TABLE_COMMAND,
+    CREATE_AUTHENTICATION_INTEGRATION_COMMAND,
     CREATE_DICTIONARY_COMMAND,
     DROP_DICTIONARY_COMMAND,
     CREATE_SQL_BLOCK_RULE_COMMAND,
@@ -186,10 +187,12 @@ public enum PlanType {
     CANCEL_JOB_COMMAND,
     DROP_CATALOG_COMMAND,
     DROP_DATABASE_COMMAND,
+    DROP_AUTHENTICATION_INTEGRATION_COMMAND,
     DROP_JOB_COMMAND,
     RESUME_JOB_COMMAND,
     ALTER_MTMV_COMMAND,
     ALTER_CATALOG_PROPERTIES_COMMAND,
+    ALTER_AUTHENTICATION_INTEGRATION_COMMAND,
     ADD_CONSTRAINT_COMMAND,
     ADMIN_COMPACT_TABLE_COMMAND,
     DROP_CONSTRAINT_COMMAND,
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AlterAuthenticationIntegrationCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AlterAuthenticationIntegrationCommand.java
new file mode 100644
index 00000000000..2955d43dc54
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AlterAuthenticationIntegrationCommand.java
@@ -0,0 +1,135 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.commands;
+
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.StmtExecutor;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * ALTER AUTHENTICATION INTEGRATION command entry.
+ */
+public class AlterAuthenticationIntegrationCommand extends AlterCommand 
implements NeedAuditEncryption {
+    /** alter action. */
+    public enum AlterType {
+        SET_PROPERTIES,
+        UNSET_PROPERTIES,
+        SET_COMMENT
+    }
+
+    private final String integrationName;
+    private final AlterType alterType;
+    private final Map<String, String> properties;
+    private final Set<String> unsetProperties;
+    private final String comment;
+
+    private AlterAuthenticationIntegrationCommand(String integrationName, 
AlterType alterType,
+            Map<String, String> properties, Set<String> unsetProperties, 
String comment) {
+        super(PlanType.ALTER_AUTHENTICATION_INTEGRATION_COMMAND);
+        this.integrationName = Objects.requireNonNull(integrationName, 
"integrationName can not be null");
+        this.alterType = Objects.requireNonNull(alterType, "alterType can not 
be null");
+        this.properties = Collections.unmodifiableMap(
+                new LinkedHashMap<>(Objects.requireNonNull(properties, 
"properties can not be null")));
+        this.unsetProperties = Collections.unmodifiableSet(
+                new LinkedHashSet<>(Objects.requireNonNull(unsetProperties, 
"unsetProperties can not be null")));
+        this.comment = comment;
+    }
+
+    public static AlterAuthenticationIntegrationCommand 
forSetProperties(String integrationName,
+            Map<String, String> properties) {
+        return new AlterAuthenticationIntegrationCommand(
+                integrationName, AlterType.SET_PROPERTIES, properties, 
Collections.emptySet(), null);
+    }
+
+    public static AlterAuthenticationIntegrationCommand 
forUnsetProperties(String integrationName,
+            Set<String> unsetProperties) {
+        return new AlterAuthenticationIntegrationCommand(
+                integrationName, AlterType.UNSET_PROPERTIES, 
Collections.emptyMap(), unsetProperties, null);
+    }
+
+    public static AlterAuthenticationIntegrationCommand forSetComment(String 
integrationName, String comment) {
+        return new AlterAuthenticationIntegrationCommand(
+                integrationName, AlterType.SET_COMMENT, 
Collections.emptyMap(), Collections.emptySet(), comment);
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitAlterAuthenticationIntegrationCommand(this, 
context);
+    }
+
+    @Override
+    public void doRun(ConnectContext ctx, StmtExecutor executor) throws 
Exception {
+        if 
(!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.ADMIN)) {
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"ADMIN");
+        }
+        switch (alterType) {
+            case SET_PROPERTIES:
+                Env.getCurrentEnv().getAuthenticationIntegrationMgr()
+                        
.alterAuthenticationIntegrationProperties(integrationName, properties);
+                return;
+            case UNSET_PROPERTIES:
+                Env.getCurrentEnv().getAuthenticationIntegrationMgr()
+                        
.alterAuthenticationIntegrationUnsetProperties(integrationName, 
unsetProperties);
+                return;
+            case SET_COMMENT:
+                Env.getCurrentEnv().getAuthenticationIntegrationMgr()
+                        
.alterAuthenticationIntegrationComment(integrationName, comment);
+                return;
+            default:
+                throw new AnalysisException("Unsupported alter type for 
AUTHENTICATION INTEGRATION: " + alterType);
+        }
+    }
+
+    @Override
+    public boolean needAuditEncryption() {
+        return true;
+    }
+
+    public String getIntegrationName() {
+        return integrationName;
+    }
+
+    public AlterType getAlterType() {
+        return alterType;
+    }
+
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    public Set<String> getUnsetProperties() {
+        return unsetProperties;
+    }
+
+    public String getComment() {
+        return comment;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateAuthenticationIntegrationCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateAuthenticationIntegrationCommand.java
new file mode 100644
index 00000000000..af0531eb871
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateAuthenticationIntegrationCommand.java
@@ -0,0 +1,94 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.commands;
+
+import org.apache.doris.analysis.StmtType;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.StmtExecutor;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * CREATE AUTHENTICATION INTEGRATION command entry.
+ */
+public class CreateAuthenticationIntegrationCommand extends Command implements 
ForwardWithSync, NeedAuditEncryption {
+    private final String integrationName;
+    private final boolean ifNotExists;
+    private final Map<String, String> properties;
+    private final String comment;
+
+    /** Constructor. */
+    public CreateAuthenticationIntegrationCommand(String integrationName, 
boolean ifNotExists,
+            Map<String, String> properties, String comment) {
+        super(PlanType.CREATE_AUTHENTICATION_INTEGRATION_COMMAND);
+        this.integrationName = Objects.requireNonNull(integrationName, 
"integrationName can not be null");
+        this.ifNotExists = ifNotExists;
+        this.properties = Collections.unmodifiableMap(
+                new LinkedHashMap<>(Objects.requireNonNull(properties, 
"properties can not be null")));
+        this.comment = comment;
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitCreateAuthenticationIntegrationCommand(this, 
context);
+    }
+
+    @Override
+    public void run(ConnectContext ctx, StmtExecutor executor) throws 
Exception {
+        if 
(!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.ADMIN)) {
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"ADMIN");
+        }
+        Env.getCurrentEnv().getAuthenticationIntegrationMgr()
+                .createAuthenticationIntegration(integrationName, ifNotExists, 
properties, comment);
+    }
+
+    @Override
+    public StmtType stmtType() {
+        return StmtType.CREATE;
+    }
+
+    @Override
+    public boolean needAuditEncryption() {
+        return true;
+    }
+
+    public String getIntegrationName() {
+        return integrationName;
+    }
+
+    public boolean isSetIfNotExists() {
+        return ifNotExists;
+    }
+
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    public String getComment() {
+        return comment;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DropAuthenticationIntegrationCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DropAuthenticationIntegrationCommand.java
new file mode 100644
index 00000000000..aa041d44678
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DropAuthenticationIntegrationCommand.java
@@ -0,0 +1,65 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.commands;
+
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.StmtExecutor;
+
+import java.util.Objects;
+
+/**
+ * DROP AUTHENTICATION INTEGRATION command entry.
+ */
+public class DropAuthenticationIntegrationCommand extends DropCommand {
+    private final boolean ifExists;
+    private final String integrationName;
+
+    public DropAuthenticationIntegrationCommand(boolean ifExists, String 
integrationName) {
+        super(PlanType.DROP_AUTHENTICATION_INTEGRATION_COMMAND);
+        this.ifExists = ifExists;
+        this.integrationName = Objects.requireNonNull(integrationName, 
"integrationName can not be null");
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitDropAuthenticationIntegrationCommand(this, 
context);
+    }
+
+    @Override
+    public void doRun(ConnectContext ctx, StmtExecutor executor) throws 
Exception {
+        if 
(!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.ADMIN)) {
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"ADMIN");
+        }
+        Env.getCurrentEnv().getAuthenticationIntegrationMgr()
+                .dropAuthenticationIntegration(integrationName, ifExists);
+    }
+
+    public boolean isIfExists() {
+        return ifExists;
+    }
+
+    public String getIntegrationName() {
+        return integrationName;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
index 0f1625c629b..78b5f83395f 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
@@ -36,6 +36,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.AdminSetPartitionVersionCom
 import 
org.apache.doris.nereids.trees.plans.commands.AdminSetReplicaStatusCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.AdminSetReplicaVersionCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.AdminSetTableStatusCommand;
+import 
org.apache.doris.nereids.trees.plans.commands.AlterAuthenticationIntegrationCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.AlterCatalogCommentCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.AlterCatalogPropertiesCommand;
 import org.apache.doris.nereids.trees.plans.commands.AlterCatalogRenameCommand;
@@ -72,6 +73,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.CleanAllProfileCommand;
 import org.apache.doris.nereids.trees.plans.commands.CleanQueryStatsCommand;
 import org.apache.doris.nereids.trees.plans.commands.Command;
 import org.apache.doris.nereids.trees.plans.commands.CopyIntoCommand;
+import 
org.apache.doris.nereids.trees.plans.commands.CreateAuthenticationIntegrationCommand;
 import org.apache.doris.nereids.trees.plans.commands.CreateCatalogCommand;
 import org.apache.doris.nereids.trees.plans.commands.CreateDatabaseCommand;
 import org.apache.doris.nereids.trees.plans.commands.CreateDictionaryCommand;
@@ -103,6 +105,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.DeleteFromCommand;
 import org.apache.doris.nereids.trees.plans.commands.DeleteFromUsingCommand;
 import org.apache.doris.nereids.trees.plans.commands.DescribeCommand;
 import org.apache.doris.nereids.trees.plans.commands.DropAnalyzeJobCommand;
+import 
org.apache.doris.nereids.trees.plans.commands.DropAuthenticationIntegrationCommand;
 import org.apache.doris.nereids.trees.plans.commands.DropCachedStatsCommand;
 import org.apache.doris.nereids.trees.plans.commands.DropCatalogCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.DropCatalogRecycleBinCommand;
@@ -517,6 +520,11 @@ public interface CommandVisitor<R, C> {
         return visitCommand(createCatalogCommand, context);
     }
 
+    default R visitCreateAuthenticationIntegrationCommand(
+            CreateAuthenticationIntegrationCommand 
createAuthenticationIntegrationCommand, C context) {
+        return visitCommand(createAuthenticationIntegrationCommand, context);
+    }
+
     default R visitShowWarningErrorsCommand(ShowWarningErrorsCommand 
showWarningErrorsCommand, C context) {
         return visitCommand(showWarningErrorsCommand, context);
     }
@@ -545,6 +553,11 @@ public interface CommandVisitor<R, C> {
         return visitCommand(dropCatalogCommand, context);
     }
 
+    default R visitDropAuthenticationIntegrationCommand(
+            DropAuthenticationIntegrationCommand 
dropAuthenticationIntegrationCommand, C context) {
+        return visitCommand(dropAuthenticationIntegrationCommand, context);
+    }
+
     default R visitAlterCatalogCommentCommand(AlterCatalogCommentCommand 
alterCatalogCommentCommand, C context) {
         return visitCommand(alterCatalogCommentCommand, context);
     }
@@ -868,6 +881,11 @@ public interface CommandVisitor<R, C> {
         return visitCommand(alterCatalogPropsCmd, context);
     }
 
+    default R visitAlterAuthenticationIntegrationCommand(
+            AlterAuthenticationIntegrationCommand 
alterAuthenticationIntegrationCommand, C context) {
+        return visitCommand(alterAuthenticationIntegrationCommand, context);
+    }
+
     default R 
visitAlterDatabasePropertiesCommand(AlterDatabasePropertiesCommand 
alterDatabasePropsCmd, C context) {
         return visitCommand(alterDatabasePropsCmd, context);
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/persist/DropAuthenticationIntegrationOperationLog.java
 
b/fe/fe-core/src/main/java/org/apache/doris/persist/DropAuthenticationIntegrationOperationLog.java
new file mode 100644
index 00000000000..a2f2015cdc2
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/persist/DropAuthenticationIntegrationOperationLog.java
@@ -0,0 +1,53 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.persist;
+
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.io.Writable;
+import org.apache.doris.persist.gson.GsonUtils;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+ * Drop log for AUTHENTICATION INTEGRATION.
+ */
+public class DropAuthenticationIntegrationOperationLog implements Writable {
+    @SerializedName(value = "in")
+    private String integrationName;
+
+    public DropAuthenticationIntegrationOperationLog(String integrationName) {
+        this.integrationName = integrationName;
+    }
+
+    public String getIntegrationName() {
+        return integrationName;
+    }
+
+    @Override
+    public void write(DataOutput out) throws IOException {
+        Text.writeString(out, GsonUtils.GSON.toJson(this));
+    }
+
+    public static DropAuthenticationIntegrationOperationLog read(DataInput in) 
throws IOException {
+        return GsonUtils.GSON.fromJson(Text.readString(in), 
DropAuthenticationIntegrationOperationLog.class);
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java 
b/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
index c7384c7e715..2fd5097ff50 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
@@ -22,6 +22,7 @@ import org.apache.doris.alter.AlterJobV2.JobState;
 import org.apache.doris.alter.BatchAlterJobPersistInfo;
 import org.apache.doris.alter.IndexChangeJob;
 import org.apache.doris.analysis.UserIdentity;
+import org.apache.doris.authentication.AuthenticationIntegrationMeta;
 import org.apache.doris.backup.BackupJob;
 import org.apache.doris.backup.Repository;
 import org.apache.doris.backup.RestoreJob;
@@ -1083,6 +1084,22 @@ public class EditLog {
                     env.getSqlBlockRuleMgr().replayDrop(log.getRuleNames());
                     break;
                 }
+                case OperationType.OP_CREATE_AUTHENTICATION_INTEGRATION: {
+                    AuthenticationIntegrationMeta log = 
(AuthenticationIntegrationMeta) journal.getData();
+                    
env.getAuthenticationIntegrationMgr().replayCreateAuthenticationIntegration(log);
+                    break;
+                }
+                case OperationType.OP_ALTER_AUTHENTICATION_INTEGRATION: {
+                    AuthenticationIntegrationMeta log = 
(AuthenticationIntegrationMeta) journal.getData();
+                    
env.getAuthenticationIntegrationMgr().replayAlterAuthenticationIntegration(log);
+                    break;
+                }
+                case OperationType.OP_DROP_AUTHENTICATION_INTEGRATION: {
+                    DropAuthenticationIntegrationOperationLog log =
+                            (DropAuthenticationIntegrationOperationLog) 
journal.getData();
+                    
env.getAuthenticationIntegrationMgr().replayDropAuthenticationIntegration(log);
+                    break;
+                }
                 case OperationType.OP_MODIFY_TABLE_ENGINE: {
                     ModifyTableEngineOperationLog log = 
(ModifyTableEngineOperationLog) journal.getData();
                     env.getAlterInstance().replayProcessModifyEngine(log);
@@ -2318,6 +2335,18 @@ public class EditLog {
         logEdit(OperationType.OP_DROP_SQL_BLOCK_RULE, new 
DropSqlBlockRuleOperationLog(ruleNames));
     }
 
+    public void 
logCreateAuthenticationIntegration(AuthenticationIntegrationMeta meta) {
+        logEdit(OperationType.OP_CREATE_AUTHENTICATION_INTEGRATION, meta);
+    }
+
+    public void 
logAlterAuthenticationIntegration(AuthenticationIntegrationMeta meta) {
+        logEdit(OperationType.OP_ALTER_AUTHENTICATION_INTEGRATION, meta);
+    }
+
+    public void 
logDropAuthenticationIntegration(DropAuthenticationIntegrationOperationLog log) 
{
+        logEdit(OperationType.OP_DROP_AUTHENTICATION_INTEGRATION, log);
+    }
+
     public void logModifyTableEngine(ModifyTableEngineOperationLog log) {
         logEdit(OperationType.OP_MODIFY_TABLE_ENGINE, log);
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java 
b/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java
index 016903129c2..96bb4669e41 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java
@@ -418,6 +418,9 @@ public class OperationType {
     public static final short OP_DROP_INDEX_POLICY = 491;
 
     public static final short OP_OPERATE_KEY = 492;
+    public static final short OP_CREATE_AUTHENTICATION_INTEGRATION = 493;
+    public static final short OP_ALTER_AUTHENTICATION_INTEGRATION = 494;
+    public static final short OP_DROP_AUTHENTICATION_INTEGRATION = 495;
 
     // For cloud.
     public static final short OP_UPDATE_CLOUD_REPLICA = 1000;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/persist/meta/MetaPersistMethod.java 
b/fe/fe-core/src/main/java/org/apache/doris/persist/meta/MetaPersistMethod.java
index 0114bde9eee..ea3585d493b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/persist/meta/MetaPersistMethod.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/persist/meta/MetaPersistMethod.java
@@ -186,6 +186,15 @@ public class MetaPersistMethod {
                 metaPersistMethod.writeMethod =
                         Env.class.getDeclaredMethod("saveSqlBlockRule", 
CountingDataOutputStream.class, long.class);
                 break;
+            // TODO: Re-enable this module once AuthenticationIntegrations 
should be persisted again.
+            // case "authenticationIntegrations":
+            //     metaPersistMethod.readMethod =
+            //             
Env.class.getDeclaredMethod("loadAuthenticationIntegrations", 
DataInputStream.class,
+            //                     long.class);
+            //     metaPersistMethod.writeMethod =
+            //             
Env.class.getDeclaredMethod("saveAuthenticationIntegrations",
+            //                     CountingDataOutputStream.class, long.class);
+            //     break;
             case "policy":
                 metaPersistMethod.readMethod =
                         Env.class.getDeclaredMethod("loadPolicy", 
DataInputStream.class, long.class);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/persist/meta/PersistMetaModules.java
 
b/fe/fe-core/src/main/java/org/apache/doris/persist/meta/PersistMetaModules.java
index 665f3cb09ed..44809035406 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/persist/meta/PersistMetaModules.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/persist/meta/PersistMetaModules.java
@@ -40,9 +40,13 @@ public class PersistMetaModules {
             "masterInfo", "frontends", "backends", "datasource", "db", 
"alterJob", "recycleBin",
             "globalVariable", "cluster", "broker", "resources", "exportJob", 
"backupHandler",
             "paloAuth", "transactionState", "colocateTableIndex", 
"routineLoadJobs", "loadJobV2", "smallFiles",
-            "plugins", "deleteHandler", "sqlBlockRule", "policy", 
"globalFunction", "workloadGroups",
+            "plugins", "deleteHandler", "sqlBlockRule", "policy",
+            "globalFunction", "workloadGroups",
             "binlogs", "resourceGroups", "AnalysisMgrV2", "AsyncJobManager", 
"workloadSchedPolicy",
-            "insertOverwrite", "plsql", "dictionaryManager", "indexPolicy", 
"KeyManagerStore");
+            "insertOverwrite", "plsql", "dictionaryManager", "indexPolicy", 
"KeyManagerStore"
+            // TODO: Re-enable "authenticationIntegrations" after persistence 
requirements are confirmed.
+            // , "authenticationIntegrations"
+    );
 
     // The modules in `CloudEnv`.
     public static final ImmutableList<String> CLOUD_MODULE_NAMES = 
ImmutableList.of("cloudWarmUpJob");
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/authentication/AuthenticationIntegrationMetaTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/authentication/AuthenticationIntegrationMetaTest.java
new file mode 100644
index 00000000000..b11268c7cc3
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/authentication/AuthenticationIntegrationMetaTest.java
@@ -0,0 +1,168 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.authentication;
+
+import org.apache.doris.common.DdlException;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class AuthenticationIntegrationMetaTest {
+
+    private static Map<String, String> map(String... kvs) {
+        Map<String, String> result = new LinkedHashMap<>();
+        for (int i = 0; i < kvs.length; i += 2) {
+            result.put(kvs[i], kvs[i + 1]);
+        }
+        return result;
+    }
+
+    private static Set<String> set(String... keys) {
+        Set<String> result = new LinkedHashSet<>();
+        Collections.addAll(result, keys);
+        return result;
+    }
+
+    @Test
+    public void testFromCreateSqlSuccessAndTypeFiltered() throws Exception {
+        Map<String, String> properties = new LinkedHashMap<>();
+        properties.put("TYPE", "ldap");
+        properties.put("ldap.server", "ldap://127.0.0.1:389";);
+        properties.put("ldap.admin_dn", "cn=admin,dc=example,dc=com");
+
+        AuthenticationIntegrationMeta meta =
+                AuthenticationIntegrationMeta.fromCreateSql("corp_ldap", 
properties, "ldap integration");
+
+        Assertions.assertEquals("corp_ldap", meta.getName());
+        Assertions.assertEquals("ldap", meta.getType());
+        Assertions.assertEquals("ldap integration", meta.getComment());
+        Assertions.assertEquals(2, meta.getProperties().size());
+        Assertions.assertEquals("ldap://127.0.0.1:389";, 
meta.getProperties().get("ldap.server"));
+        Assertions.assertFalse(meta.getProperties().containsKey("type"));
+        Assertions.assertFalse(meta.getProperties().containsKey("TYPE"));
+
+        Assertions.assertThrows(UnsupportedOperationException.class,
+                () -> meta.getProperties().put("x", "y"));
+
+        Map<String, String> sqlProperties = meta.toSqlPropertiesView();
+        Assertions.assertEquals("ldap", sqlProperties.get("type"));
+        Assertions.assertEquals("cn=admin,dc=example,dc=com", 
sqlProperties.get("ldap.admin_dn"));
+    }
+
+    @Test
+    public void testFromCreateSqlRequireType() {
+        Assertions.assertThrows(DdlException.class,
+                () -> AuthenticationIntegrationMeta.fromCreateSql("i1", null, 
null));
+        Assertions.assertThrows(DdlException.class,
+                () -> AuthenticationIntegrationMeta.fromCreateSql("i1", 
Collections.emptyMap(), null));
+        Assertions.assertThrows(DdlException.class,
+                () -> AuthenticationIntegrationMeta.fromCreateSql("i1", 
map("k", "v"), null));
+        Assertions.assertThrows(DdlException.class,
+                () -> AuthenticationIntegrationMeta.fromCreateSql("i1", 
map("type", ""), null));
+    }
+
+    @Test
+    public void testFromCreateSqlRejectDuplicatedTypeIgnoreCase() {
+        Map<String, String> properties = new LinkedHashMap<>();
+        properties.put("type", "ldap");
+        properties.put("TYPE", "oidc");
+
+        Assertions.assertThrows(DdlException.class,
+                () -> AuthenticationIntegrationMeta.fromCreateSql("i1", 
properties, null));
+    }
+
+    @Test
+    public void testWithAlterProperties() throws Exception {
+        AuthenticationIntegrationMeta meta = 
AuthenticationIntegrationMeta.fromCreateSql(
+                "corp_ldap",
+                map("type", "ldap",
+                        "ldap.server", "ldap://old";,
+                        "ldap.base_dn", "dc=example,dc=com"),
+                "old comment");
+
+        AuthenticationIntegrationMeta altered = meta.withAlterProperties(map(
+                "ldap.server", "ldap://new";,
+                "ldap.user_filter", "(uid={login})"));
+
+        Assertions.assertEquals("ldap", altered.getType());
+        Assertions.assertEquals("old comment", altered.getComment());
+        Assertions.assertEquals("ldap://new";, 
altered.getProperties().get("ldap.server"));
+        Assertions.assertEquals("(uid={login})", 
altered.getProperties().get("ldap.user_filter"));
+
+        Assertions.assertThrows(DdlException.class,
+                () -> meta.withAlterProperties(Collections.emptyMap()));
+        Assertions.assertThrows(DdlException.class,
+                () -> meta.withAlterProperties(map("TYPE", "oidc")));
+    }
+
+    @Test
+    public void testWithUnsetProperties() throws Exception {
+        AuthenticationIntegrationMeta meta = 
AuthenticationIntegrationMeta.fromCreateSql(
+                "corp_ldap",
+                map("type", "ldap",
+                        "ldap.server", "ldap://old";,
+                        "ldap.base_dn", "dc=example,dc=com"),
+                "old comment");
+
+        AuthenticationIntegrationMeta altered = 
meta.withUnsetProperties(set("ldap.base_dn"));
+        Assertions.assertEquals("ldap", altered.getType());
+        
Assertions.assertFalse(altered.getProperties().containsKey("ldap.base_dn"));
+        Assertions.assertEquals("ldap://old";, 
altered.getProperties().get("ldap.server"));
+
+        Assertions.assertThrows(DdlException.class,
+                () -> meta.withUnsetProperties(Collections.emptySet()));
+        Assertions.assertThrows(DdlException.class,
+                () -> meta.withUnsetProperties(set("TYPE")));
+    }
+
+    @Test
+    public void testWriteReadRoundTrip() throws IOException, DdlException {
+        AuthenticationIntegrationMeta meta = 
AuthenticationIntegrationMeta.fromCreateSql(
+                "corp_ldap",
+                map("type", "ldap",
+                        "ldap.server", "ldap://127.0.0.1:389";,
+                        "ldap.admin_password", "123456"),
+                "comment");
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (DataOutputStream dos = new DataOutputStream(bos)) {
+            meta.write(dos);
+        }
+
+        AuthenticationIntegrationMeta read;
+        try (DataInputStream dis = new DataInputStream(new 
ByteArrayInputStream(bos.toByteArray()))) {
+            read = AuthenticationIntegrationMeta.read(dis);
+        }
+
+        Assertions.assertEquals(meta.getName(), read.getName());
+        Assertions.assertEquals(meta.getType(), read.getType());
+        Assertions.assertEquals(meta.getComment(), read.getComment());
+        Assertions.assertEquals(meta.getProperties(), read.getProperties());
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/authentication/AuthenticationIntegrationMgrTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/authentication/AuthenticationIntegrationMgrTest.java
new file mode 100644
index 00000000000..18f1dd530c7
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/authentication/AuthenticationIntegrationMgrTest.java
@@ -0,0 +1,206 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.authentication;
+
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.DdlException;
+import org.apache.doris.persist.DropAuthenticationIntegrationOperationLog;
+import org.apache.doris.persist.EditLog;
+
+import mockit.Expectations;
+import mockit.Mocked;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class AuthenticationIntegrationMgrTest {
+
+    @Mocked
+    private Env env;
+
+    @Mocked
+    private EditLog editLog;
+
+    private static Map<String, String> map(String... kvs) {
+        Map<String, String> result = new LinkedHashMap<>();
+        for (int i = 0; i < kvs.length; i += 2) {
+            result.put(kvs[i], kvs[i + 1]);
+        }
+        return result;
+    }
+
+    private static Set<String> set(String... keys) {
+        Set<String> result = new LinkedHashSet<>();
+        Collections.addAll(result, keys);
+        return result;
+    }
+
+    @Test
+    public void testCreateAlterDropFlow() throws Exception {
+        new Expectations() {
+            {
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getEditLog();
+                minTimes = 0;
+                result = editLog;
+
+                
editLog.logCreateAuthenticationIntegration((AuthenticationIntegrationMeta) any);
+                minTimes = 0;
+
+                
editLog.logAlterAuthenticationIntegration((AuthenticationIntegrationMeta) any);
+                minTimes = 0;
+
+                
editLog.logDropAuthenticationIntegration((DropAuthenticationIntegrationOperationLog)
 any);
+                minTimes = 0;
+            }
+        };
+
+        AuthenticationIntegrationMgr mgr = new AuthenticationIntegrationMgr();
+        Map<String, String> createProperties = new LinkedHashMap<>();
+        createProperties.put("type", "ldap");
+        createProperties.put("ldap.server", "ldap://127.0.0.1:389";);
+        createProperties.put("ldap.admin_password", "123456");
+
+        mgr.createAuthenticationIntegration("corp_ldap", false, 
createProperties, "comment");
+        AuthenticationIntegrationMeta created = 
mgr.getAuthenticationIntegrations().get("corp_ldap");
+        Assertions.assertNotNull(created);
+        Assertions.assertEquals("ldap", created.getType());
+        Assertions.assertEquals("ldap://127.0.0.1:389";, 
created.getProperties().get("ldap.server"));
+
+        mgr.alterAuthenticationIntegrationProperties("corp_ldap", 
map("ldap.server", "ldap://127.0.0.1:1389";));
+        Assertions.assertEquals("ldap://127.0.0.1:1389";,
+                
mgr.getAuthenticationIntegrations().get("corp_ldap").getProperties().get("ldap.server"));
+
+        mgr.alterAuthenticationIntegrationUnsetProperties("corp_ldap", 
set("ldap.admin_password"));
+        Assertions.assertFalse(mgr.getAuthenticationIntegrations()
+                
.get("corp_ldap").getProperties().containsKey("ldap.admin_password"));
+
+        mgr.alterAuthenticationIntegrationComment("corp_ldap", "new comment");
+        Assertions.assertEquals("new comment", 
mgr.getAuthenticationIntegrations().get("corp_ldap").getComment());
+
+        mgr.dropAuthenticationIntegration("corp_ldap", false);
+        Assertions.assertTrue(mgr.getAuthenticationIntegrations().isEmpty());
+    }
+
+    @Test
+    public void testCreateDuplicateAndDropIfExists() throws Exception {
+        new Expectations() {
+            {
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getEditLog();
+                minTimes = 0;
+                result = editLog;
+
+                
editLog.logCreateAuthenticationIntegration((AuthenticationIntegrationMeta) any);
+                minTimes = 0;
+
+                
editLog.logDropAuthenticationIntegration((DropAuthenticationIntegrationOperationLog)
 any);
+                minTimes = 0;
+            }
+        };
+
+        AuthenticationIntegrationMgr mgr = new AuthenticationIntegrationMgr();
+        mgr.createAuthenticationIntegration("corp_ldap", false, map(
+                "type", "ldap",
+                "ldap.server", "ldap://127.0.0.1:389";), null);
+
+        Assertions.assertThrows(DdlException.class,
+                () -> mgr.createAuthenticationIntegration("corp_ldap", false, 
map("type", "ldap"), null));
+        Assertions.assertDoesNotThrow(
+                () -> mgr.createAuthenticationIntegration("corp_ldap", true, 
map("type", "ldap"), null));
+
+        Assertions.assertDoesNotThrow(() -> 
mgr.dropAuthenticationIntegration("not_exist", true));
+        Assertions.assertThrows(DdlException.class,
+                () -> mgr.dropAuthenticationIntegration("not_exist", false));
+    }
+
+    @Test
+    public void testAlterNotExistThrows() {
+        AuthenticationIntegrationMgr mgr = new AuthenticationIntegrationMgr();
+        Assertions.assertThrows(DdlException.class,
+                () -> 
mgr.alterAuthenticationIntegrationProperties("not_exist", map("k", "v")));
+        Assertions.assertThrows(DdlException.class,
+                () -> 
mgr.alterAuthenticationIntegrationUnsetProperties("not_exist", set("k")));
+        Assertions.assertThrows(DdlException.class,
+                () -> mgr.alterAuthenticationIntegrationComment("not_exist", 
"comment"));
+    }
+
+    @Test
+    public void testReplayAndGetUnmodifiableView() throws Exception {
+        AuthenticationIntegrationMgr mgr = new AuthenticationIntegrationMgr();
+
+        AuthenticationIntegrationMeta meta1 = 
AuthenticationIntegrationMeta.fromCreateSql(
+                "corp_ldap", map("type", "ldap", "ldap.server", "ldap://old";), 
null);
+        AuthenticationIntegrationMeta meta2 = 
meta1.withAlterProperties(map("ldap.server", "ldap://new";));
+
+        mgr.replayCreateAuthenticationIntegration(meta1);
+        mgr.replayAlterAuthenticationIntegration(meta2);
+
+        Map<String, AuthenticationIntegrationMeta> copy = 
mgr.getAuthenticationIntegrations();
+        Assertions.assertEquals(1, copy.size());
+        Assertions.assertThrows(UnsupportedOperationException.class,
+                () -> copy.put("x", meta1));
+
+        mgr.replayDropAuthenticationIntegration(new 
DropAuthenticationIntegrationOperationLog("corp_ldap"));
+        Assertions.assertTrue(mgr.getAuthenticationIntegrations().isEmpty());
+    }
+
+    @Test
+    public void testWriteReadRoundTrip() throws IOException, DdlException {
+        AuthenticationIntegrationMgr mgr = new AuthenticationIntegrationMgr();
+        AuthenticationIntegrationMeta meta = 
AuthenticationIntegrationMeta.fromCreateSql(
+                "corp_ldap", map(
+                        "type", "ldap",
+                        "ldap.server", "ldap://127.0.0.1:389";),
+                "comment");
+        mgr.replayCreateAuthenticationIntegration(meta);
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (DataOutputStream dos = new DataOutputStream(bos)) {
+            mgr.write(dos);
+        }
+
+        AuthenticationIntegrationMgr read;
+        try (DataInputStream dis = new DataInputStream(new 
ByteArrayInputStream(bos.toByteArray()))) {
+            read = AuthenticationIntegrationMgr.read(dis);
+        }
+
+        Assertions.assertEquals(1, 
read.getAuthenticationIntegrations().size());
+        AuthenticationIntegrationMeta readMeta = 
read.getAuthenticationIntegrations().get("corp_ldap");
+        Assertions.assertNotNull(readMeta);
+        Assertions.assertEquals("ldap", readMeta.getType());
+        Assertions.assertEquals("ldap://127.0.0.1:389";, 
readMeta.getProperties().get("ldap.server"));
+        Assertions.assertEquals("comment", readMeta.getComment());
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/AuthenticationIntegrationParserTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/AuthenticationIntegrationParserTest.java
new file mode 100644
index 00000000000..2a01a237209
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/AuthenticationIntegrationParserTest.java
@@ -0,0 +1,111 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.parser;
+
+import org.apache.doris.nereids.exceptions.ParseException;
+import 
org.apache.doris.nereids.trees.plans.commands.AlterAuthenticationIntegrationCommand;
+import 
org.apache.doris.nereids.trees.plans.commands.CreateAuthenticationIntegrationCommand;
+import 
org.apache.doris.nereids.trees.plans.commands.DropAuthenticationIntegrationCommand;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class AuthenticationIntegrationParserTest {
+
+    private final NereidsParser parser = new NereidsParser();
+
+    @Test
+    public void testCreateAuthenticationIntegrationParse() {
+        LogicalPlan plan = parser.parseSingle("CREATE AUTHENTICATION 
INTEGRATION IF NOT EXISTS corp_ldap "
+                + "PROPERTIES ('type'='ldap', 
'ldap.server'='ldap://127.0.0.1:389') "
+                + "COMMENT 'ldap integration'");
+
+        
Assertions.assertInstanceOf(CreateAuthenticationIntegrationCommand.class, plan);
+        CreateAuthenticationIntegrationCommand command = 
(CreateAuthenticationIntegrationCommand) plan;
+        Assertions.assertEquals("corp_ldap", command.getIntegrationName());
+        Assertions.assertTrue(command.isSetIfNotExists());
+        Assertions.assertEquals("ldap", command.getProperties().get("type"));
+        Assertions.assertEquals("ldap://127.0.0.1:389";, 
command.getProperties().get("ldap.server"));
+        Assertions.assertEquals("ldap integration", command.getComment());
+    }
+
+    @Test
+    public void testCreateAuthenticationIntegrationRequireType() {
+        Assertions.assertThrows(ParseException.class, () -> parser.parseSingle(
+                "CREATE AUTHENTICATION INTEGRATION corp_ldap "
+                        + "PROPERTIES 
('ldap.server'='ldap://127.0.0.1:389')"));
+    }
+
+    @Test
+    public void testAlterAuthenticationIntegrationParse() {
+        LogicalPlan alterProperties = parser.parseSingle("ALTER AUTHENTICATION 
INTEGRATION corp_ldap "
+                + "SET PROPERTIES ('ldap.server'='ldap://127.0.0.1:1389')");
+        
Assertions.assertInstanceOf(AlterAuthenticationIntegrationCommand.class, 
alterProperties);
+
+        AlterAuthenticationIntegrationCommand alterPropertiesCommand =
+                (AlterAuthenticationIntegrationCommand) alterProperties;
+        Assertions.assertEquals("corp_ldap", 
alterPropertiesCommand.getIntegrationName());
+        
Assertions.assertEquals(AlterAuthenticationIntegrationCommand.AlterType.SET_PROPERTIES,
+                alterPropertiesCommand.getAlterType());
+        Assertions.assertEquals("ldap://127.0.0.1:1389";,
+                alterPropertiesCommand.getProperties().get("ldap.server"));
+
+        LogicalPlan unsetProperties = parser.parseSingle("ALTER AUTHENTICATION 
INTEGRATION corp_ldap "
+                + "UNSET PROPERTIES ('ldap.server')");
+        
Assertions.assertInstanceOf(AlterAuthenticationIntegrationCommand.class, 
unsetProperties);
+
+        AlterAuthenticationIntegrationCommand unsetPropertiesCommand =
+                (AlterAuthenticationIntegrationCommand) unsetProperties;
+        
Assertions.assertEquals(AlterAuthenticationIntegrationCommand.AlterType.UNSET_PROPERTIES,
+                unsetPropertiesCommand.getAlterType());
+        
Assertions.assertTrue(unsetPropertiesCommand.getUnsetProperties().contains("ldap.server"));
+
+        LogicalPlan alterComment = parser.parseSingle(
+                "ALTER AUTHENTICATION INTEGRATION corp_ldap SET COMMENT 'new 
comment'");
+        
Assertions.assertInstanceOf(AlterAuthenticationIntegrationCommand.class, 
alterComment);
+
+        AlterAuthenticationIntegrationCommand alterCommentCommand =
+                (AlterAuthenticationIntegrationCommand) alterComment;
+        
Assertions.assertEquals(AlterAuthenticationIntegrationCommand.AlterType.SET_COMMENT,
+                alterCommentCommand.getAlterType());
+        Assertions.assertEquals("new comment", 
alterCommentCommand.getComment());
+    }
+
+    @Test
+    public void testAlterAuthenticationIntegrationRejectType() {
+        Assertions.assertThrows(ParseException.class, () -> parser.parseSingle(
+                "ALTER AUTHENTICATION INTEGRATION corp_ldap SET PROPERTIES 
('TYPE'='oidc')"));
+        Assertions.assertThrows(ParseException.class, () -> parser.parseSingle(
+                "ALTER AUTHENTICATION INTEGRATION corp_ldap UNSET PROPERTIES 
('TYPE')"));
+    }
+
+    @Test
+    public void testDropAuthenticationIntegrationParse() {
+        LogicalPlan plan1 = parser.parseSingle("DROP AUTHENTICATION 
INTEGRATION corp_ldap");
+        
Assertions.assertInstanceOf(DropAuthenticationIntegrationCommand.class, plan1);
+        DropAuthenticationIntegrationCommand drop1 = 
(DropAuthenticationIntegrationCommand) plan1;
+        Assertions.assertEquals("corp_ldap", drop1.getIntegrationName());
+        Assertions.assertFalse(drop1.isIfExists());
+
+        LogicalPlan plan2 = parser.parseSingle("DROP AUTHENTICATION 
INTEGRATION IF EXISTS corp_ldap");
+        
Assertions.assertInstanceOf(DropAuthenticationIntegrationCommand.class, plan2);
+        DropAuthenticationIntegrationCommand drop2 = 
(DropAuthenticationIntegrationCommand) plan2;
+        Assertions.assertTrue(drop2.isIfExists());
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/AuthenticationIntegrationCommandTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/AuthenticationIntegrationCommandTest.java
new file mode 100644
index 00000000000..ed1a70b03d5
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/AuthenticationIntegrationCommandTest.java
@@ -0,0 +1,238 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.commands;
+
+import org.apache.doris.analysis.StmtType;
+import org.apache.doris.authentication.AuthenticationIntegrationMgr;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.mysql.privilege.AccessControllerManager;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.qe.ConnectContext;
+
+import mockit.Expectations;
+import mockit.Mocked;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class AuthenticationIntegrationCommandTest {
+
+    @Mocked
+    private Env env;
+
+    @Mocked
+    private AccessControllerManager accessManager;
+
+    @Mocked
+    private AuthenticationIntegrationMgr authenticationIntegrationMgr;
+
+    @Mocked
+    private ConnectContext connectContext;
+
+    private static Map<String, String> map(String... kvs) {
+        Map<String, String> result = new LinkedHashMap<>();
+        for (int i = 0; i < kvs.length; i += 2) {
+            result.put(kvs[i], kvs[i + 1]);
+        }
+        return result;
+    }
+
+    private static Set<String> set(String... keys) {
+        Set<String> result = new LinkedHashSet<>();
+        Collections.addAll(result, keys);
+        return result;
+    }
+
+    @Test
+    public void testCreateCommandRunAndDenied() throws Exception {
+        CreateAuthenticationIntegrationCommand createCommand =
+                new CreateAuthenticationIntegrationCommand("corp_ldap", false,
+                        map("type", "ldap", "ldap.server", 
"ldap://127.0.0.1:389";), "comment");
+
+        new Expectations() {
+            {
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getAccessManager();
+                minTimes = 0;
+                result = accessManager;
+
+                accessManager.checkGlobalPriv((ConnectContext) any, 
PrivPredicate.ADMIN);
+                result = true;
+
+                env.getAuthenticationIntegrationMgr();
+                minTimes = 0;
+                result = authenticationIntegrationMgr;
+
+                authenticationIntegrationMgr.createAuthenticationIntegration(
+                        anyString, anyBoolean, (Map<String, String>) any, 
anyString);
+                times = 1;
+            }
+        };
+
+        Assertions.assertDoesNotThrow(() -> createCommand.run(connectContext, 
null));
+        Assertions.assertEquals(StmtType.CREATE, createCommand.stmtType());
+        Assertions.assertTrue(createCommand.needAuditEncryption());
+
+        new Expectations() {
+            {
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getAccessManager();
+                minTimes = 0;
+                result = accessManager;
+
+                accessManager.checkGlobalPriv((ConnectContext) any, 
PrivPredicate.ADMIN);
+                result = false;
+            }
+        };
+
+        Assertions.assertThrows(AnalysisException.class, () -> 
createCommand.run(connectContext, null));
+    }
+
+    @Test
+    public void testAlterCommandRun() throws Exception {
+        AlterAuthenticationIntegrationCommand setPropertiesCommand =
+                AlterAuthenticationIntegrationCommand.forSetProperties(
+                        "corp_ldap", map("ldap.server", 
"ldap://127.0.0.1:1389";));
+        AlterAuthenticationIntegrationCommand unsetPropertiesCommand =
+                AlterAuthenticationIntegrationCommand.forUnsetProperties(
+                        "corp_ldap", set("ldap.server"));
+        AlterAuthenticationIntegrationCommand setCommentCommand =
+                
AlterAuthenticationIntegrationCommand.forSetComment("corp_ldap", "new comment");
+
+        new Expectations() {
+            {
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getAccessManager();
+                minTimes = 0;
+                result = accessManager;
+
+                accessManager.checkGlobalPriv((ConnectContext) any, 
PrivPredicate.ADMIN);
+                minTimes = 0;
+                result = true;
+
+                env.getAuthenticationIntegrationMgr();
+                minTimes = 0;
+                result = authenticationIntegrationMgr;
+
+                
authenticationIntegrationMgr.alterAuthenticationIntegrationProperties(
+                        anyString, (Map<String, String>) any);
+                times = 1;
+
+                
authenticationIntegrationMgr.alterAuthenticationIntegrationUnsetProperties(
+                        anyString, (Set<String>) any);
+                times = 1;
+
+                
authenticationIntegrationMgr.alterAuthenticationIntegrationComment(anyString, 
anyString);
+                times = 1;
+            }
+        };
+
+        Assertions.assertDoesNotThrow(() -> 
setPropertiesCommand.doRun(connectContext, null));
+        Assertions.assertDoesNotThrow(() -> 
unsetPropertiesCommand.doRun(connectContext, null));
+        Assertions.assertDoesNotThrow(() -> 
setCommentCommand.doRun(connectContext, null));
+        Assertions.assertTrue(setPropertiesCommand.needAuditEncryption());
+        Assertions.assertTrue(unsetPropertiesCommand.needAuditEncryption());
+        Assertions.assertTrue(setCommentCommand.needAuditEncryption());
+    }
+
+    @Test
+    public void testAlterCommandDenied() {
+        AlterAuthenticationIntegrationCommand setPropertiesCommand =
+                AlterAuthenticationIntegrationCommand.forSetProperties(
+                        "corp_ldap", map("ldap.server", 
"ldap://127.0.0.1:1389";));
+
+        new Expectations() {
+            {
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getAccessManager();
+                minTimes = 0;
+                result = accessManager;
+
+                accessManager.checkGlobalPriv((ConnectContext) any, 
PrivPredicate.ADMIN);
+                result = false;
+            }
+        };
+
+        Assertions.assertThrows(AnalysisException.class, () -> 
setPropertiesCommand.doRun(connectContext, null));
+    }
+
+    @Test
+    public void testDropCommandRunAndDenied() throws Exception {
+        DropAuthenticationIntegrationCommand dropCommand =
+                new DropAuthenticationIntegrationCommand(true, "corp_ldap");
+
+        new Expectations() {
+            {
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getAccessManager();
+                minTimes = 0;
+                result = accessManager;
+
+                accessManager.checkGlobalPriv((ConnectContext) any, 
PrivPredicate.ADMIN);
+                result = true;
+
+                env.getAuthenticationIntegrationMgr();
+                minTimes = 0;
+                result = authenticationIntegrationMgr;
+
+                
authenticationIntegrationMgr.dropAuthenticationIntegration(anyString, 
anyBoolean);
+                times = 1;
+            }
+        };
+
+        Assertions.assertDoesNotThrow(() -> dropCommand.doRun(connectContext, 
null));
+
+        new Expectations() {
+            {
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getAccessManager();
+                minTimes = 0;
+                result = accessManager;
+
+                accessManager.checkGlobalPriv((ConnectContext) any, 
PrivPredicate.ADMIN);
+                result = false;
+            }
+        };
+
+        Assertions.assertThrows(AnalysisException.class, () -> 
dropCommand.doRun(connectContext, null));
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/persist/DropAuthenticationIntegrationOperationLogTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/persist/DropAuthenticationIntegrationOperationLogTest.java
new file mode 100644
index 00000000000..042a7fc40d2
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/persist/DropAuthenticationIntegrationOperationLogTest.java
@@ -0,0 +1,47 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.persist;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+
+public class DropAuthenticationIntegrationOperationLogTest {
+
+    @Test
+    public void testWriteReadRoundTrip() throws Exception {
+        DropAuthenticationIntegrationOperationLog log =
+                new DropAuthenticationIntegrationOperationLog("corp_ldap");
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (DataOutputStream dos = new DataOutputStream(bos)) {
+            log.write(dos);
+        }
+
+        DropAuthenticationIntegrationOperationLog read;
+        try (DataInputStream dis = new DataInputStream(new 
ByteArrayInputStream(bos.toByteArray()))) {
+            read = DropAuthenticationIntegrationOperationLog.read(dis);
+        }
+
+        Assertions.assertEquals("corp_ldap", read.getIntegrationName());
+    }
+}
diff --git 
a/regression-test/suites/auth_p0/test_authentication_integration_auth.groovy 
b/regression-test/suites/auth_p0/test_authentication_integration_auth.groovy
new file mode 100644
index 00000000000..c65ac577574
--- /dev/null
+++ b/regression-test/suites/auth_p0/test_authentication_integration_auth.groovy
@@ -0,0 +1,78 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_authentication_integration_auth", "p0,auth") {
+    String suiteName = "test_authentication_integration_auth"
+    String integrationName = "${suiteName}_ldap"
+
+    try_sql("DROP AUTHENTICATION INTEGRATION IF EXISTS ${integrationName}")
+
+    try {
+        test {
+            sql """
+                CREATE AUTHENTICATION INTEGRATION ${integrationName}
+                 PROPERTIES ('ldap.server'='ldap://127.0.0.1:389')
+            """
+            exception "Property 'type' is required"
+        }
+
+        sql """
+            CREATE AUTHENTICATION INTEGRATION ${integrationName}
+             PROPERTIES (
+                'type'='ldap',
+                'ldap.server'='ldap://127.0.0.1:389',
+                'ldap.admin_password'='123456'
+            )
+            COMMENT 'for regression test'
+        """
+
+        test {
+            sql """
+                CREATE AUTHENTICATION INTEGRATION ${integrationName}
+                 PROPERTIES ('type'='ldap', 
'ldap.server'='ldap://127.0.0.1:1389')
+            """
+            exception "already exists"
+        }
+
+        test {
+            sql """
+                ALTER AUTHENTICATION INTEGRATION ${integrationName}
+                SET PROPERTIES ('type'='oidc')
+            """
+            exception "does not allow modifying property 'type'"
+        }
+
+        sql """
+            ALTER AUTHENTICATION INTEGRATION ${integrationName}
+            SET PROPERTIES (
+                'ldap.server'='ldap://127.0.0.1:1389',
+                'ldap.admin_password'='abcdef'
+            )
+        """
+
+        sql """ALTER AUTHENTICATION INTEGRATION ${integrationName} SET COMMENT 
'updated comment'"""
+
+        test {
+            sql """DROP AUTHENTICATION INTEGRATION 
${integrationName}_not_exist"""
+            exception "does not exist"
+        }
+
+        sql """DROP AUTHENTICATION INTEGRATION IF EXISTS 
${integrationName}_not_exist"""
+    } finally {
+        try_sql("DROP AUTHENTICATION INTEGRATION IF EXISTS ${integrationName}")
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to