From be82c0b2fa1df75a5361ed677e91fc532b2395f9 Mon Sep 17 00:00:00 2001
From: Kurdakov Alexander <kusancho12@gmail.com>
Date: Thu, 12 Sep 2024 21:46:26 +0300
Subject: [PATCH] fix: do not ban admin console

Do not ban admin console via unix socket
after several failed attempts via `picodata connect`.
---
 CHANGELOG.md            |  2 ++
 src/lib.rs              | 10 +++++++-
 test/int/test_cli_ux.py | 56 ++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8a12449521..5c93ed5148 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -103,6 +103,8 @@ which improves perfomance
 - Fix error "Read access to space '_raft_state' is denied"
   when executing a DML query on global tables
 
+- Fix error "Maximum number of login attempts exceeded" in picodata admin
+
 ### Compatibility
 
 - The current version is NOT compatible with prior releases. It cannot
diff --git a/src/lib.rs b/src/lib.rs
index 3604cc3e43..cb2e810bca 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -453,7 +453,15 @@ fn set_login_check(storage: Clusterwide) {
 
     let lua = ::tarantool::lua_state();
     lua.exec_with(
-        "box.session.on_auth(...)",
+        "
+        local rust_on_auth = ...
+        local function on_auth(user, status)
+            if box.session.type() ~= 'console' then
+                rust_on_auth(user, status)
+            end
+        end
+
+        box.session.on_auth(on_auth)",
         tlua::function3(move |user: String, status: bool, lua: tlua::LuaState| {
             match compute_auth_verdict(user.clone(), status) {
                 Verdict::AuthOk => {
diff --git a/test/int/test_cli_ux.py b/test/int/test_cli_ux.py
index f632c8700e..ab8c94f899 100644
--- a/test/int/test_cli_ux.py
+++ b/test/int/test_cli_ux.py
@@ -1,7 +1,12 @@
 import pexpect  # type: ignore
+import os
+import pytest
 import sys
 import subprocess
-from conftest import Cluster
+from conftest import Cluster, log_crawler
+from tarantool.error import (  # type: ignore
+    NetworkError,
+)
 
 
 def test_connect_ux(cluster: Cluster):
@@ -459,3 +464,52 @@ Delimiter changed to ';'
 Bye
 """.encode()
     )
+
+
+def test_do_not_ban_admin_via_unix_socket(cluster: Cluster):
+    password_file = f"{cluster.data_dir}/service-password.txt"
+    with open(password_file, "w") as f:
+        print("secret", file=f)
+
+    os.chmod(password_file, 0o600)
+
+    i1 = cluster.add_instance(wait_online=False)
+    i1.service_password_file = password_file
+
+    admin_banned_lc = log_crawler(
+        i1, "Maximum number of login attempts exceeded; user blocked"
+    )
+    i1.start()
+    i1.wait_online()
+
+    # auth via pico_service many times
+    for _ in range(100):
+        with pytest.raises(NetworkError):
+            i1.sql("try to auth", user="pico_service", password="wrong_password")
+
+    # pico_service is not banned
+    data = i1.sql(
+        "SELECT name FROM _pico_tier ", user="pico_service", password="secret"
+    )
+
+    assert data[0][0] == "default"
+
+    # auth via admin until ban
+    for _ in range(5):
+        with pytest.raises(NetworkError):
+            i1.sql("try to auth", user="admin", password="wrong_password")
+
+    admin_banned_lc.wait_matched()
+
+    cli = pexpect.spawn(
+        cwd=i1.data_dir,
+        command=i1.binary_path,
+        args=["admin", "./admin.sock"],
+        encoding="utf-8",
+        timeout=1,
+    )
+
+    cli.logfile = sys.stdout
+    cli.expect_exact('Connected to admin console by socket path "./admin.sock"')
+    cli.expect_exact("type '\\help' for interactive help")
+    cli.expect_exact("picodata> ")
-- 
GitLab