diff --git a/programs/client/Client.cpp b/programs/client/Client.cpp
index 6e289b57845..8a81a72be3c 100644
--- a/programs/client/Client.cpp
+++ b/programs/client/Client.cpp
@@ -16,6 +16,8 @@
#include
+#include
+
#include "config_version.h"
#include
#include
@@ -180,6 +182,64 @@ std::vector Client::loadWarningMessages()
}
+/// Make query to get all server password complexity rules
+void Client::loadPasswordComplexityRules()
+{
+ /// Older server versions cannot execute the query loading password complexity rules.
+ constexpr UInt64 min_server_revision_to_load_warnings = DBMS_MIN_PROTOCOL_VERSION_WITH_VIEW_IF_PERMITTED;
+
+ if (server_revision < min_server_revision_to_load_warnings)
+ return;
+
+ connection->sendQuery(connection_parameters.timeouts,
+ "SELECT * FROM viewIfPermitted(SELECT pattern, message FROM system.password_rules ELSE null('pattern String, message String'))",
+ {} /* query_parameters */,
+ "" /* query_id */,
+ QueryProcessingStage::Complete,
+ &global_context->getSettingsRef(),
+ &global_context->getClientInfo(), false, {});
+ while (true)
+ {
+ Packet packet = connection->receivePacket();
+ switch (packet.type)
+ {
+ case Protocol::Server::Data:
+ if (packet.block)
+ {
+ const ColumnString & column_pattern = typeid_cast(*packet.block.getByPosition(0).column);
+ const ColumnString & column_message = typeid_cast(*packet.block.getByPosition(1).column);
+
+ auto & access_control = global_context->getAccessControl();
+ size_t rows = packet.block.rows();
+ for (size_t i = 0; i < rows; ++i)
+ access_control.addPasswordComplexityRule({column_pattern[i].get(), column_message[i].get()});
+ }
+ continue;
+
+ case Protocol::Server::Progress:
+ case Protocol::Server::ProfileInfo:
+ case Protocol::Server::Totals:
+ case Protocol::Server::Extremes:
+ case Protocol::Server::Log:
+ continue;
+
+ case Protocol::Server::Exception:
+ packet.exception->rethrow();
+ return;
+
+ case Protocol::Server::EndOfStream:
+ return;
+
+ case Protocol::Server::ProfileEvents:
+ continue;
+
+ default:
+ throw Exception(ErrorCodes::UNKNOWN_PACKET_FROM_SERVER, "Unknown packet {} from server {}",
+ packet.type, connection->getDescription());
+ }
+ }
+}
+
void Client::initialize(Poco::Util::Application & self)
{
Poco::Util::Application::initialize(self);
@@ -258,6 +318,16 @@ try
if (is_interactive && !config().has("no-warnings"))
showWarnings();
+ /// Load password complexity rules
+ try
+ {
+ loadPasswordComplexityRules();
+ }
+ catch (...)
+ {
+ /// Ignore exception
+ }
+
if (is_interactive && !delayed_interactive)
{
runInteractive();
diff --git a/programs/client/Client.h b/programs/client/Client.h
index 63f28ca96a2..0f4e0b2dd13 100644
--- a/programs/client/Client.h
+++ b/programs/client/Client.h
@@ -48,5 +48,6 @@ private:
void printChangedSettings() const;
void showWarnings();
std::vector loadWarningMessages();
+ void loadPasswordComplexityRules();
};
}
diff --git a/programs/server/config.xml b/programs/server/config.xml
index 3d7904c8ed8..3e7e55d9071 100644
--- a/programs/server/config.xml
+++ b/programs/server/config.xml
@@ -467,28 +467,28 @@
1
-
+
-
-
-
- users.xml
-
-
-
- ./
-
-
+
+
+
+
+
+
+
+
diff --git a/tests/queries/0_stateless/02474_password_complexity_rules.reference b/tests/queries/0_stateless/02474_password_complexity_rules.reference
index 21da4d2be9e..b462a5a7baa 100644
--- a/tests/queries/0_stateless/02474_password_complexity_rules.reference
+++ b/tests/queries/0_stateless/02474_password_complexity_rules.reference
@@ -2,4 +2,3 @@ OK
OK
OK
OK
-OK
diff --git a/tests/queries/0_stateless/02474_password_complexity_rules.sh b/tests/queries/0_stateless/02474_password_complexity_rules.sh
index ab33665bdba..6ef0c42d803 100755
--- a/tests/queries/0_stateless/02474_password_complexity_rules.sh
+++ b/tests/queries/0_stateless/02474_password_complexity_rules.sh
@@ -1,27 +1,83 @@
#!/usr/bin/env bash
-# Tags: no-parallel
+# Tags: long, no-parallel
-CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=../shell_config.sh
-. "$CURDIR"/../shell_config.sh
+. "$CUR_DIR"/../shell_config.sh
-$CLICKHOUSE_CLIENT -q "DROP USER IF EXISTS u_2474"
+server_opts=(
+ "--config-file=$CUR_DIR/$(basename "${BASH_SOURCE[0]}" .sh).config.xml"
+ "--"
+ # to avoid multiple listen sockets (complexity for port discovering)
+ "--listen_host=127.1"
+ # we will discover the real port later.
+ "--tcp_port=0"
+ "--shutdown_wait_unfinished=0"
+)
+CLICKHOUSE_WATCHDOG_ENABLE=0 $CLICKHOUSE_SERVER_BINARY "${server_opts[@]}" >& clickhouse-server.log &
+server_pid=$!
+
+trap cleanup EXIT
+function cleanup()
+{
+ kill -9 $server_pid
+ kill -9 $client_pid
+
+ echo "Test failed. Server log:"
+ cat clickhouse-server.log
+ rm -f clickhouse-server.log
+
+ exit 1
+}
+
+server_port=
+i=0 retries=300
+# wait until server will start to listen (max 30 seconds)
+while [[ -z $server_port ]] && [[ $i -lt $retries ]]; do
+ server_port=$(lsof -n -a -P -i tcp -s tcp:LISTEN -p $server_pid 2>/dev/null | awk -F'[ :]' '/LISTEN/ { print $(NF-1) }')
+ ((++i))
+ sleep 0.1
+done
+if [[ -z $server_port ]]; then
+ echo "Cannot wait for LISTEN socket" >&2
+ exit 1
+fi
+
+# wait for the server to start accepting tcp connections (max 30 seconds)
+i=0 retries=300
+while ! $CLICKHOUSE_CLIENT_BINARY --host 127.1 --port "$server_port" --format Null -q 'select 1' 2>/dev/null && [[ $i -lt $retries ]]; do
+ sleep 0.1
+done
+if ! $CLICKHOUSE_CLIENT_BINARY --host 127.1 --port "$server_port" --format Null -q 'select 1'; then
+ echo "Cannot wait until server will start accepting connections on " >&2
+ exit 1
+fi
-$CLICKHOUSE_CLIENT -q "CREATE USER u_2474 IDENTIFIED WITH plaintext_password BY ''" 2>&1 | grep -qF \
- "DB::Exception: Invalid password. The password should: be at least 12 characters long, contain at least 1 numeric character, contain at least 1 lowercase character, contain at least 1 uppercase character, contain at least 1 special character." && echo 'OK' || echo 'FAIL' ||:
+$CLICKHOUSE_CLIENT_BINARY --host 127.1 --port "$server_port" -q "CREATE USER u_2474 IDENTIFIED WITH plaintext_password BY ''" 2>&1 | grep -qF \
+ "DB::Exception: Invalid password. The password should: be at least 12 characters long, contain at least 1 numeric character, contain at least 1 lowercase character, contain at least 1 uppercase character, contain at least 1 special character" && echo 'OK' || echo 'FAIL' ||:
-$CLICKHOUSE_CLIENT -q "CREATE USER u_2474 IDENTIFIED WITH plaintext_password BY '000000000000'" 2>&1 | grep -qF \
- "DB::Exception: Invalid password. The password should: contain at least 1 lowercase character, contain at least 1 uppercase character, contain at least 1 special character." && echo 'OK' || echo 'FAIL' ||:
+$CLICKHOUSE_CLIENT_BINARY --host 127.1 --port "$server_port" -q "CREATE USER u_2474 IDENTIFIED WITH plaintext_password BY '000000000000'" 2>&1 | grep -qF \
+ "DB::Exception: Invalid password. The password should: contain at least 1 lowercase character, contain at least 1 uppercase character, contain at least 1 special character" && echo 'OK' || echo 'FAIL' ||:
-$CLICKHOUSE_CLIENT -q "CREATE USER u_2474 IDENTIFIED WITH plaintext_password BY 'a00000000000'" 2>&1 | grep -qF \
- "DB::Exception: Invalid password. The password should: contain at least 1 uppercase character, contain at least 1 special character." && echo 'OK' || echo 'FAIL' ||:
+$CLICKHOUSE_CLIENT_BINARY --host 127.1 --port "$server_port" -q "CREATE USER u_2474 IDENTIFIED WITH plaintext_password BY 'a00000000000'" 2>&1 | grep -qF \
+ "DB::Exception: Invalid password. The password should: contain at least 1 uppercase character, contain at least 1 special character" && echo 'OK' || echo 'FAIL' ||:
-$CLICKHOUSE_CLIENT -q "CREATE USER u_2474 IDENTIFIED WITH plaintext_password BY 'aA0000000000'" 2>&1 | grep -qF \
- "DB::Exception: Invalid password. The password should: contain at least 1 special character." && echo 'OK' || echo 'FAIL' ||:
-
-$CLICKHOUSE_CLIENT -q "CREATE USER u_2474 IDENTIFIED WITH plaintext_password BY 'aA!000000000'" 2>&1 | grep -qF \
- "DB::Exception:" && echo 'FAIL' || echo 'OK' ||:
+$CLICKHOUSE_CLIENT_BINARY --host 127.1 --port "$server_port" -q "CREATE USER u_2474 IDENTIFIED WITH plaintext_password BY 'aA0000000000'" 2>&1 | grep -qF \
+ "DB::Exception: Invalid password. The password should: contain at least 1 special character" && echo 'OK' || echo 'FAIL' ||:
-$CLICKHOUSE_CLIENT -q "DROP USER u_2474"
+# send TERM and save the error code to ensure that it is 0 (EXIT_SUCCESS)
+kill $server_pid
+wait $server_pid
+return_code=$?
+
+wait $client_pid
+
+trap '' EXIT
+if [ $return_code != 0 ]; then
+ cat clickhouse-server.log
+fi
+rm -f clickhouse-server.log
+
+exit $return_code