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

yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 35629f416f3 branch-4.1: [fix](password) password lock failed after 
invalid login #61592 (#61656)
35629f416f3 is described below

commit 35629f416f385191c2ea2587537b07d197bd11b6
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Mar 25 11:43:17 2026 +0800

    branch-4.1: [fix](password) password lock failed after invalid login #61592 
(#61656)
    
    Cherry-picked from #61592
    
    Co-authored-by: Mingyu Chen (Rayner) <[email protected]>
---
 .../doris/mysql/privilege/PasswordPolicy.java      |   6 +-
 .../doris/mysql/privilege/PasswordPolicyTest.java  | 123 +++++++++++++++++++++
 .../suites/account_p0/test_alter_user.groovy       |  27 +++++
 3 files changed, 155 insertions(+), 1 deletion(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PasswordPolicy.java 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PasswordPolicy.java
index de055d702d5..996e2ddec61 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PasswordPolicy.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PasswordPolicy.java
@@ -358,7 +358,11 @@ public class PasswordPolicy {
                 return false;
             }
             if (failedLoginCounter.get() >= numFailedLogin) {
-                return true;
+                if (isLocked()) {
+                    return true;
+                }
+                // Lock has expired, reset counter to allow re-locking on new 
failed attempts
+                unlock();
             }
             if (failedLoginCounter.incrementAndGet() >= numFailedLogin) {
                 lockTime.set(System.currentTimeMillis());
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/PasswordPolicyTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/PasswordPolicyTest.java
new file mode 100644
index 00000000000..942a098182b
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/PasswordPolicyTest.java
@@ -0,0 +1,123 @@
+// 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.mysql.privilege;
+
+import org.apache.doris.mysql.privilege.PasswordPolicy.FailedLoginPolicy;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PasswordPolicyTest {
+
+    @Test
+    public void testFailedLoginPolicyBasicLock() {
+        FailedLoginPolicy policy = new FailedLoginPolicy();
+        policy.numFailedLogin = 3;
+        policy.passwordLockSeconds = 60;
+
+        // First 2 failures should not lock
+        Assert.assertFalse(policy.onFailedLogin());
+        Assert.assertFalse(policy.onFailedLogin());
+        // 3rd failure should lock
+        Assert.assertTrue(policy.onFailedLogin());
+        Assert.assertTrue(policy.isLocked());
+    }
+
+    @Test
+    public void testFailedLoginPolicyRelockAfterExpiry() {
+        FailedLoginPolicy policy = new FailedLoginPolicy();
+        policy.numFailedLogin = 3;
+        policy.passwordLockSeconds = 5;
+
+        // Trigger first lock
+        Assert.assertFalse(policy.onFailedLogin());
+        Assert.assertFalse(policy.onFailedLogin());
+        Assert.assertTrue(policy.onFailedLogin());
+        Assert.assertTrue(policy.isLocked());
+
+        // Simulate lock expiry by setting lockTime to the past
+        policy.lockTime.set(System.currentTimeMillis() - 6000);
+        Assert.assertFalse(policy.isLocked());
+
+        // Now trigger re-lock: counter should reset and start counting again
+        // 1st failed login after expiry — counter resets from 3 to 0, then 
increments to 1
+        Assert.assertFalse(policy.onFailedLogin());
+        Assert.assertFalse(policy.isLocked());
+        // 2nd
+        Assert.assertFalse(policy.onFailedLogin());
+        Assert.assertFalse(policy.isLocked());
+        // 3rd should lock again
+        Assert.assertTrue(policy.onFailedLogin());
+        Assert.assertTrue(policy.isLocked());
+    }
+
+    @Test
+    public void testFailedLoginPolicyStillLockedWhileActive() {
+        FailedLoginPolicy policy = new FailedLoginPolicy();
+        policy.numFailedLogin = 2;
+        policy.passwordLockSeconds = 60;
+
+        // Lock the account
+        Assert.assertFalse(policy.onFailedLogin());
+        Assert.assertTrue(policy.onFailedLogin());
+        Assert.assertTrue(policy.isLocked());
+
+        // While still locked, onFailedLogin should still return true
+        Assert.assertTrue(policy.onFailedLogin());
+        Assert.assertTrue(policy.isLocked());
+    }
+
+    @Test
+    public void testFailedLoginPolicyManualUnlock() {
+        FailedLoginPolicy policy = new FailedLoginPolicy();
+        policy.numFailedLogin = 2;
+        policy.passwordLockSeconds = 60;
+
+        // Lock the account
+        Assert.assertFalse(policy.onFailedLogin());
+        Assert.assertTrue(policy.onFailedLogin());
+        Assert.assertTrue(policy.isLocked());
+
+        // Manual unlock
+        policy.unlock();
+        Assert.assertFalse(policy.isLocked());
+
+        // Should be able to re-lock
+        Assert.assertFalse(policy.onFailedLogin());
+        Assert.assertTrue(policy.onFailedLogin());
+        Assert.assertTrue(policy.isLocked());
+    }
+
+    @Test
+    public void testFailedLoginPolicyDisabled() {
+        FailedLoginPolicy policy = new FailedLoginPolicy();
+        // Both disabled by default (0)
+        Assert.assertFalse(policy.onFailedLogin());
+        Assert.assertFalse(policy.isLocked());
+
+        // Only numFailedLogin set
+        policy.numFailedLogin = 3;
+        policy.passwordLockSeconds = 0;
+        Assert.assertFalse(policy.onFailedLogin());
+
+        // Only passwordLockSeconds set
+        policy.numFailedLogin = 0;
+        policy.passwordLockSeconds = 60;
+        Assert.assertFalse(policy.onFailedLogin());
+    }
+}
diff --git a/regression-test/suites/account_p0/test_alter_user.groovy 
b/regression-test/suites/account_p0/test_alter_user.groovy
index c0d0a26fb60..6021c2c1546 100644
--- a/regression-test/suites/account_p0/test_alter_user.groovy
+++ b/regression-test/suites/account_p0/test_alter_user.groovy
@@ -122,6 +122,33 @@ suite("test_alter_user", "account,nonConcurrent") {
         sql 'select 1'
     }
 
+    // DORIS-24183: test re-locking after lock expiry
+    // after lock expires, entering wrong passwords again should trigger lock 
again
+    try {
+        connect('test_auth_user3', 'wrong', context.config.jdbcUrl) {}
+        assertTrue(false, "should not be able to login")
+    } catch (Exception e) {
+        assertTrue(e.getMessage().contains("Access denied for user 
'test_auth_user3"), e.getMessage())
+    }
+    try {
+        connect('test_auth_user3', 'wrong', context.config.jdbcUrl) {}
+        assertTrue(false, "should not be able to login")
+    } catch (Exception e) {
+        assertTrue(e.getMessage().contains("Access denied for user 
'test_auth_user3"), e.getMessage())
+    }
+    // account should be locked again
+    try {
+        connect('test_auth_user3', '12345', context.config.jdbcUrl) {}
+        assertTrue(false, "should not be able to login")
+    } catch (Exception e) {
+        assertTrue(e.getMessage().contains("Account is blocked"), 
e.getMessage())
+    }
+    // wait for lock to expire again
+    sleep(5000)
+    result1 = connect('test_auth_user3', '12345', context.config.jdbcUrl) {
+        sql 'select 1'
+    }
+
     // 4. test password validation
     sql """set global validate_password_policy=STRONG"""
     test {


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

Reply via email to