diff --git a/src/Access/UsersConfigAccessStorage.cpp b/src/Access/UsersConfigAccessStorage.cpp index 882563d1a337..1093611f8a25 100644 --- a/src/Access/UsersConfigAccessStorage.cpp +++ b/src/Access/UsersConfigAccessStorage.cpp @@ -115,7 +115,7 @@ namespace UserPtr parseUser( const Poco::Util::AbstractConfiguration & config, - const String & user_name, + String user_name, const std::unordered_set & allowed_profile_ids, const std::unordered_set & allowed_role_ids, bool allow_no_password, @@ -123,8 +123,12 @@ namespace { const bool validate = true; auto user = std::make_shared(); - user->setName(user_name); String user_config = "users." + user_name; + + /// If the user name contains a dot, it is escaped with a backslash when parsed from the config file. + /// We need to remove the backslash to get the correct user name. + Poco::replaceInPlace(user_name, "\\.", "."); + user->setName(user_name); bool has_no_password = config.has(user_config + ".no_password"); bool has_password_plaintext = config.has(user_config + ".password"); bool has_password_sha256_hex = config.has(user_config + ".password_sha256_hex"); diff --git a/tests/integration/test_dot_in_user_name/__init__.py b/tests/integration/test_dot_in_user_name/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/integration/test_dot_in_user_name/configs/users.xml b/tests/integration/test_dot_in_user_name/configs/users.xml new file mode 100644 index 000000000000..144d44594bf4 --- /dev/null +++ b/tests/integration/test_dot_in_user_name/configs/users.xml @@ -0,0 +1,23 @@ + + + + + + ::/0 + + default + default + + + + + ::/0 + + default + default + + + + + + diff --git a/tests/integration/test_dot_in_user_name/test.py b/tests/integration/test_dot_in_user_name/test.py new file mode 100644 index 000000000000..1719c38d1092 --- /dev/null +++ b/tests/integration/test_dot_in_user_name/test.py @@ -0,0 +1,35 @@ +import pytest + +from helpers.cluster import ClickHouseCluster + +cluster = ClickHouseCluster(__file__) +node = cluster.add_instance( + "node", + user_configs=[ + "configs/users.xml", + ], +) + + +@pytest.fixture(scope="module", autouse=True) +def started_cluster(): + try: + cluster.start() + yield cluster + finally: + cluster.shutdown() + + +def test_user_with_dot_in_name(): + assert node.query("SELECT count()>0 FROM system.users where name = 'user.name'") == "1\n" + assert node.query("SELECT count()>0 FROM system.users where name = 'user\\.name'") == "0\n" + + node.query("DROP USER IF EXISTS 'foo.bar'") + node.query("CREATE USER 'foo.bar'") + assert node.query("SELECT count()>0 FROM system.users where name = 'foo.bar'") == "1\n" + assert node.query("SELECT count()>0 FROM system.users where name = 'foo\\.bar'") == "0\n" + + node.query("ALTER USER 'foo.bar' RENAME TO 'foo\\.bar'") + assert node.query("SELECT count()>0 FROM system.users where name = 'foo.bar'") == "0\n" + assert node.query("SELECT count()>0 FROM system.users where name = 'foo\\.bar'") == "1\n" + node.query("DROP USER 'foo\\.bar'")