Merge pull request #16249 from traceon/access-storage-added-log

Add a log message after an access storage is added
This commit is contained in:
alexey-milovidov 2020-10-23 09:17:10 +03:00 committed by GitHub
commit 121ebfdc81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 400 additions and 258 deletions

View File

@ -35,7 +35,7 @@ RUN apt-get update \
ENV TZ=Europe/Moscow ENV TZ=Europe/Moscow
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN pip3 install urllib3 testflows==1.6.48 docker-compose docker dicttoxml kazoo tzlocal RUN pip3 install urllib3 testflows==1.6.57 docker-compose docker dicttoxml kazoo tzlocal
ENV DOCKER_CHANNEL stable ENV DOCKER_CHANNEL stable
ENV DOCKER_VERSION 17.09.1-ce ENV DOCKER_VERSION 17.09.1-ce
@ -72,5 +72,5 @@ RUN set -x \
VOLUME /var/lib/docker VOLUME /var/lib/docker
EXPOSE 2375 EXPOSE 2375
ENTRYPOINT ["dockerd-entrypoint.sh"] ENTRYPOINT ["dockerd-entrypoint.sh"]
CMD ["sh", "-c", "python3 regression.py --no-color --local --clickhouse-binary-path ${CLICKHOUSE_TESTS_SERVER_BIN_PATH} --log test.log ${TESTFLOWS_OPTS}; cat test.log | tfs report results --format json > results.json"] CMD ["sh", "-c", "python3 regression.py --no-color -o classic --local --clickhouse-binary-path ${CLICKHOUSE_TESTS_SERVER_BIN_PATH} --log test.log ${TESTFLOWS_OPTS}; cat test.log | tfs report results --format json > results.json"]

View File

@ -137,7 +137,6 @@ AccessControlManager::AccessControlManager()
AccessControlManager::~AccessControlManager() = default; AccessControlManager::~AccessControlManager() = default;
void AccessControlManager::setUsersConfig(const Poco::Util::AbstractConfiguration & users_config_) void AccessControlManager::setUsersConfig(const Poco::Util::AbstractConfiguration & users_config_)
{ {
auto storages = getStoragesPtr(); auto storages = getStoragesPtr();
@ -163,6 +162,7 @@ void AccessControlManager::addUsersConfigStorage(const String & storage_name_, c
auto new_storage = std::make_shared<UsersConfigAccessStorage>(storage_name_, check_setting_name_function); auto new_storage = std::make_shared<UsersConfigAccessStorage>(storage_name_, check_setting_name_function);
new_storage->setConfig(users_config_); new_storage->setConfig(users_config_);
addStorage(new_storage); addStorage(new_storage);
LOG_DEBUG(getLogger(), "Added {} access storage '{}', path: {}", String(new_storage->getStorageType()), new_storage->getStorageName(), new_storage->getPath());
} }
void AccessControlManager::addUsersConfigStorage( void AccessControlManager::addUsersConfigStorage(
@ -195,6 +195,7 @@ void AccessControlManager::addUsersConfigStorage(
auto new_storage = std::make_shared<UsersConfigAccessStorage>(storage_name_, check_setting_name_function); auto new_storage = std::make_shared<UsersConfigAccessStorage>(storage_name_, check_setting_name_function);
new_storage->load(users_config_path_, include_from_path_, preprocessed_dir_, get_zookeeper_function_); new_storage->load(users_config_path_, include_from_path_, preprocessed_dir_, get_zookeeper_function_);
addStorage(new_storage); addStorage(new_storage);
LOG_DEBUG(getLogger(), "Added {} access storage '{}', path: {}", String(new_storage->getStorageType()), new_storage->getStorageName(), new_storage->getPath());
} }
void AccessControlManager::reloadUsersConfigs() void AccessControlManager::reloadUsersConfigs()
@ -238,7 +239,9 @@ void AccessControlManager::addDiskStorage(const String & storage_name_, const St
} }
} }
} }
addStorage(std::make_shared<DiskAccessStorage>(storage_name_, directory_, readonly_)); auto new_storage = std::make_shared<DiskAccessStorage>(storage_name_, directory_, readonly_);
addStorage(new_storage);
LOG_DEBUG(getLogger(), "Added {} access storage '{}', path: {}", String(new_storage->getStorageType()), new_storage->getStorageName(), new_storage->getPath());
} }
@ -250,13 +253,17 @@ void AccessControlManager::addMemoryStorage(const String & storage_name_)
if (auto memory_storage = typeid_cast<std::shared_ptr<MemoryAccessStorage>>(storage)) if (auto memory_storage = typeid_cast<std::shared_ptr<MemoryAccessStorage>>(storage))
return; return;
} }
addStorage(std::make_shared<MemoryAccessStorage>(storage_name_)); auto new_storage = std::make_shared<MemoryAccessStorage>(storage_name_);
addStorage(new_storage);
LOG_DEBUG(getLogger(), "Added {} access storage '{}'", String(new_storage->getStorageType()), new_storage->getStorageName());
} }
void AccessControlManager::addLDAPStorage(const String & storage_name_, const Poco::Util::AbstractConfiguration & config_, const String & prefix_) void AccessControlManager::addLDAPStorage(const String & storage_name_, const Poco::Util::AbstractConfiguration & config_, const String & prefix_)
{ {
addStorage(std::make_shared<LDAPAccessStorage>(storage_name_, this, config_, prefix_)); auto new_storage = std::make_shared<LDAPAccessStorage>(storage_name_, this, config_, prefix_);
addStorage(new_storage);
LOG_DEBUG(getLogger(), "Added {} access storage '{}', LDAP server name: {}", String(new_storage->getStorageType()), new_storage->getStorageName(), new_storage->getLDAPServerName());
} }

View File

@ -29,6 +29,12 @@ LDAPAccessStorage::LDAPAccessStorage(const String & storage_name_, AccessControl
} }
String LDAPAccessStorage::getLDAPServerName() const
{
return ldap_server;
}
void LDAPAccessStorage::setConfiguration(AccessControlManager * access_control_manager_, const Poco::Util::AbstractConfiguration & config, const String & prefix) void LDAPAccessStorage::setConfiguration(AccessControlManager * access_control_manager_, const Poco::Util::AbstractConfiguration & config, const String & prefix)
{ {
std::scoped_lock lock(mutex); std::scoped_lock lock(mutex);

View File

@ -32,6 +32,8 @@ public:
explicit LDAPAccessStorage(const String & storage_name_, AccessControlManager * access_control_manager_, const Poco::Util::AbstractConfiguration & config, const String & prefix); explicit LDAPAccessStorage(const String & storage_name_, AccessControlManager * access_control_manager_, const Poco::Util::AbstractConfiguration & config, const String & prefix);
virtual ~LDAPAccessStorage() override = default; virtual ~LDAPAccessStorage() override = default;
String getLDAPServerName() const;
public: // IAccessStorage implementations. public: // IAccessStorage implementations.
virtual const char * getStorageType() const override; virtual const char * getStorageType() const override;
virtual String getStorageParamsJSON() const override; virtual String getStorageParamsJSON() const override;

View File

@ -28,6 +28,8 @@ servers = {
@TestStep(When) @TestStep(When)
@Name("I login as {username} and execute query") @Name("I login as {username} and execute query")
def login_and_execute_query(self, username, password, exitcode=None, message=None, steps=True): def login_and_execute_query(self, username, password, exitcode=None, message=None, steps=True):
"""Execute query as some user.
"""
self.context.node.query("SELECT 1", self.context.node.query("SELECT 1",
settings=[("user", username), ("password", password)], settings=[("user", username), ("password", password)],
exitcode=exitcode or 0, exitcode=exitcode or 0,
@ -35,7 +37,8 @@ def login_and_execute_query(self, username, password, exitcode=None, message=Non
@TestScenario @TestScenario
def add_user_to_ldap_and_login(self, server, user=None, ch_user=None, login=None, exitcode=None, message=None, rbac=False): def add_user_to_ldap_and_login(self, server, user=None, ch_user=None, login=None, exitcode=None, message=None, rbac=False):
"""Add user to LDAP and ClickHouse and then try to login.""" """Add user to LDAP and ClickHouse and then try to login.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
if ch_user is None: if ch_user is None:
@ -60,7 +63,8 @@ def add_user_to_ldap_and_login(self, server, user=None, ch_user=None, login=None
RQ_SRS_007_LDAP_Authentication_Parallel_ValidAndInvalid("1.0") RQ_SRS_007_LDAP_Authentication_Parallel_ValidAndInvalid("1.0")
) )
def parallel_login(self, server, user_count=10, timeout=200, rbac=False): def parallel_login(self, server, user_count=10, timeout=200, rbac=False):
"""Check that login of valid and invalid LDAP authenticated users works in parallel.""" """Check that login of valid and invalid LDAP authenticated users works in parallel.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
user = None user = None
@ -114,7 +118,8 @@ def parallel_login(self, server, user_count=10, timeout=200, rbac=False):
RQ_SRS_007_LDAP_Authentication_Invalid_DeletedUser("1.0") RQ_SRS_007_LDAP_Authentication_Invalid_DeletedUser("1.0")
) )
def login_after_user_is_deleted_from_ldap(self, server, rbac=False): def login_after_user_is_deleted_from_ldap(self, server, rbac=False):
"""Check that login fails after user is deleted from LDAP.""" """Check that login fails after user is deleted from LDAP.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
user = None user = None
@ -146,7 +151,8 @@ def login_after_user_is_deleted_from_ldap(self, server, rbac=False):
RQ_SRS_007_LDAP_Authentication_PasswordChanged("1.0") RQ_SRS_007_LDAP_Authentication_PasswordChanged("1.0")
) )
def login_after_user_password_changed_in_ldap(self, server, rbac=False): def login_after_user_password_changed_in_ldap(self, server, rbac=False):
"""Check that login fails after user password is changed in LDAP.""" """Check that login fails after user password is changed in LDAP.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
user = None user = None
@ -182,7 +188,8 @@ def login_after_user_password_changed_in_ldap(self, server, rbac=False):
RQ_SRS_007_LDAP_Authentication_UsernameChanged("1.0") RQ_SRS_007_LDAP_Authentication_UsernameChanged("1.0")
) )
def login_after_user_cn_changed_in_ldap(self, server, rbac=False): def login_after_user_cn_changed_in_ldap(self, server, rbac=False):
"""Check that login fails after user cn is changed in LDAP.""" """Check that login fails after user cn is changed in LDAP.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
user = None user = None
new_user = None new_user = None
@ -215,7 +222,8 @@ def login_after_user_cn_changed_in_ldap(self, server, rbac=False):
RQ_SRS_007_LDAP_Authentication_LDAPServerRestart("1.0") RQ_SRS_007_LDAP_Authentication_LDAPServerRestart("1.0")
) )
def login_after_ldap_server_is_restarted(self, server, timeout=60, rbac=False): def login_after_ldap_server_is_restarted(self, server, timeout=60, rbac=False):
"""Check that login succeeds after LDAP server is restarted.""" """Check that login succeeds after LDAP server is restarted.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
user = None user = None
@ -250,7 +258,8 @@ def login_after_ldap_server_is_restarted(self, server, timeout=60, rbac=False):
RQ_SRS_007_LDAP_Authentication_ClickHouseServerRestart("1.0") RQ_SRS_007_LDAP_Authentication_ClickHouseServerRestart("1.0")
) )
def login_after_clickhouse_server_is_restarted(self, server, timeout=60, rbac=False): def login_after_clickhouse_server_is_restarted(self, server, timeout=60, rbac=False):
"""Check that login succeeds after ClickHouse server is restarted.""" """Check that login succeeds after ClickHouse server is restarted.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
user = None user = None
@ -285,7 +294,8 @@ def login_after_clickhouse_server_is_restarted(self, server, timeout=60, rbac=Fa
RQ_SRS_007_LDAP_Authentication_Password_Empty("1.0") RQ_SRS_007_LDAP_Authentication_Password_Empty("1.0")
) )
def valid_username_with_valid_empty_password(self, server, rbac=False): def valid_username_with_valid_empty_password(self, server, rbac=False):
"""Check that we can't login using valid username that has empty password.""" """Check that we can't login using valid username that has empty password.
"""
user = {"cn": "empty_password", "userpassword": ""} user = {"cn": "empty_password", "userpassword": ""}
exitcode = 4 exitcode = 4
message = f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name" message = f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
@ -298,7 +308,8 @@ def valid_username_with_valid_empty_password(self, server, rbac=False):
RQ_SRS_007_LDAP_Authentication_Password_Empty("1.0") RQ_SRS_007_LDAP_Authentication_Password_Empty("1.0")
) )
def valid_username_and_invalid_empty_password(self, server, rbac=False): def valid_username_and_invalid_empty_password(self, server, rbac=False):
"""Check that we can't login using valid username but invalid empty password.""" """Check that we can't login using valid username but invalid empty password.
"""
username = "user_non_empty_password" username = "user_non_empty_password"
user = {"cn": username, "userpassword": username} user = {"cn": username, "userpassword": username}
login = {"password": ""} login = {"password": ""}
@ -313,7 +324,8 @@ def valid_username_and_invalid_empty_password(self, server, rbac=False):
RQ_SRS_007_LDAP_Authentication_Valid("1.0") RQ_SRS_007_LDAP_Authentication_Valid("1.0")
) )
def valid_username_and_password(self, server, rbac=False): def valid_username_and_password(self, server, rbac=False):
"""Check that we can login using valid username and password.""" """Check that we can login using valid username and password.
"""
username = "valid_username_and_password" username = "valid_username_and_password"
user = {"cn": username, "userpassword": username} user = {"cn": username, "userpassword": username}
@ -326,7 +338,8 @@ def valid_username_and_password(self, server, rbac=False):
) )
def valid_username_and_password_invalid_server(self, server=None, rbac=False): def valid_username_and_password_invalid_server(self, server=None, rbac=False):
"""Check that we can't login using valid username and valid """Check that we can't login using valid username and valid
password but for a different server.""" password but for a different server.
"""
self.context.ldap_node = self.context.cluster.node("openldap1") self.context.ldap_node = self.context.cluster.node("openldap1")
user = {"username": "user2", "userpassword": "user2", "server": "openldap1"} user = {"username": "user2", "userpassword": "user2", "server": "openldap1"}
@ -344,7 +357,8 @@ def valid_username_and_password_invalid_server(self, server=None, rbac=False):
RQ_SRS_007_LDAP_Configuration_User_Name_Long("1.0") RQ_SRS_007_LDAP_Configuration_User_Name_Long("1.0")
) )
def valid_long_username_and_short_password(self, server, rbac=False): def valid_long_username_and_short_password(self, server, rbac=False):
"""Check that we can login using valid very long username and short password.""" """Check that we can login using valid very long username and short password.
"""
username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890" username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890"
user = {"cn": username, "userpassword": "long_username"} user = {"cn": username, "userpassword": "long_username"}
@ -355,7 +369,8 @@ def valid_long_username_and_short_password(self, server, rbac=False):
RQ_SRS_007_LDAP_Authentication_Invalid("1.0") RQ_SRS_007_LDAP_Authentication_Invalid("1.0")
) )
def invalid_long_username_and_valid_short_password(self, server, rbac=False): def invalid_long_username_and_valid_short_password(self, server, rbac=False):
"""Check that we can't login using slightly invalid long username but valid password.""" """Check that we can't login using slightly invalid long username but valid password.
"""
username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890" username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890"
user = {"cn": username, "userpassword": "long_username"} user = {"cn": username, "userpassword": "long_username"}
login = {"username": f"{username}?"} login = {"username": f"{username}?"}
@ -371,7 +386,8 @@ def invalid_long_username_and_valid_short_password(self, server, rbac=False):
RQ_SRS_007_LDAP_Authentication_Password_Long("1.0") RQ_SRS_007_LDAP_Authentication_Password_Long("1.0")
) )
def valid_short_username_and_long_password(self, server, rbac=False): def valid_short_username_and_long_password(self, server, rbac=False):
"""Check that we can login using valid short username with very long password.""" """Check that we can login using valid short username with very long password.
"""
username = "long_password" username = "long_password"
user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"} user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"}
add_user_to_ldap_and_login(user=user, server=server, rbac=rbac) add_user_to_ldap_and_login(user=user, server=server, rbac=rbac)
@ -381,7 +397,8 @@ def valid_short_username_and_long_password(self, server, rbac=False):
RQ_SRS_007_LDAP_Authentication_Invalid("1.0") RQ_SRS_007_LDAP_Authentication_Invalid("1.0")
) )
def valid_short_username_and_invalid_long_password(self, server, rbac=False): def valid_short_username_and_invalid_long_password(self, server, rbac=False):
"""Check that we can't login using valid short username and invalid long password.""" """Check that we can't login using valid short username and invalid long password.
"""
username = "long_password" username = "long_password"
user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"} user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"}
login = {"password": user["userpassword"] + "1"} login = {"password": user["userpassword"] + "1"}
@ -396,7 +413,8 @@ def valid_short_username_and_invalid_long_password(self, server, rbac=False):
RQ_SRS_007_LDAP_Authentication_Invalid("1.0") RQ_SRS_007_LDAP_Authentication_Invalid("1.0")
) )
def valid_username_and_invalid_password(self, server, rbac=False): def valid_username_and_invalid_password(self, server, rbac=False):
"""Check that we can't login using valid username and invalid password.""" """Check that we can't login using valid username and invalid password.
"""
username = "valid_username_and_invalid_password" username = "valid_username_and_invalid_password"
user = {"cn": username, "userpassword": username} user = {"cn": username, "userpassword": username}
login = {"password": user["userpassword"] + "1"} login = {"password": user["userpassword"] + "1"}
@ -411,7 +429,8 @@ def valid_username_and_invalid_password(self, server, rbac=False):
RQ_SRS_007_LDAP_Authentication_Invalid("1.0") RQ_SRS_007_LDAP_Authentication_Invalid("1.0")
) )
def invalid_username_and_valid_password(self, server, rbac=False): def invalid_username_and_valid_password(self, server, rbac=False):
"""Check that we can't login using slightly invalid username but valid password.""" """Check that we can't login using slightly invalid username but valid password.
"""
username = "invalid_username_and_valid_password" username = "invalid_username_and_valid_password"
user = {"cn": username, "userpassword": username} user = {"cn": username, "userpassword": username}
login = {"username": user["cn"] + "1"} login = {"username": user["cn"] + "1"}
@ -428,7 +447,8 @@ def invalid_username_and_valid_password(self, server, rbac=False):
RQ_SRS_007_LDAP_Configuration_User_Name_UTF8("1.0") RQ_SRS_007_LDAP_Configuration_User_Name_UTF8("1.0")
) )
def valid_utf8_username_and_ascii_password(self, server, rbac=False): def valid_utf8_username_and_ascii_password(self, server, rbac=False):
"""Check that we can login using valid utf-8 username with ascii password.""" """Check that we can login using valid utf-8 username with ascii password.
"""
username = "utf8_username_Gãńdåłf_Thê_Gręât" username = "utf8_username_Gãńdåłf_Thê_Gręât"
user = {"cn": username, "userpassword": "utf8_username"} user = {"cn": username, "userpassword": "utf8_username"}
@ -440,7 +460,8 @@ def valid_utf8_username_and_ascii_password(self, server, rbac=False):
RQ_SRS_007_LDAP_Authentication_Password_UTF8("1.0") RQ_SRS_007_LDAP_Authentication_Password_UTF8("1.0")
) )
def valid_ascii_username_and_utf8_password(self, server, rbac=False): def valid_ascii_username_and_utf8_password(self, server, rbac=False):
"""Check that we can login using valid ascii username with utf-8 password.""" """Check that we can login using valid ascii username with utf-8 password.
"""
username = "utf8_password" username = "utf8_password"
user = {"cn": username, "userpassword": "utf8_password_Gãńdåłf_Thê_Gręât"} user = {"cn": username, "userpassword": "utf8_password_Gãńdåłf_Thê_Gręât"}
@ -449,7 +470,8 @@ def valid_ascii_username_and_utf8_password(self, server, rbac=False):
@TestScenario @TestScenario
def empty_username_and_empty_password(self, server=None, rbac=False): def empty_username_and_empty_password(self, server=None, rbac=False):
"""Check that we can login using empty username and empty password as """Check that we can login using empty username and empty password as
it will use the default user and that has an empty password.""" it will use the default user and that has an empty password.
"""
login_and_execute_query(username="", password="") login_and_execute_query(username="", password="")
@TestOutline(Feature) @TestOutline(Feature)

View File

@ -95,6 +95,8 @@ def add_config(config, timeout=20, restart=False):
if exitcode == 0: if exitcode == 0:
break break
time.sleep(1) time.sleep(1)
if settings.debug:
node.command(f"cat /var/lib/clickhouse/preprocessed_configs/{config.preprocessed_name}")
assert exitcode == 0, error() assert exitcode == 0, error()
def wait_for_config_to_be_loaded(): def wait_for_config_to_be_loaded():

View File

@ -98,7 +98,8 @@ def starttls_with_custom_port(self):
login(servers, *users) login(servers, *users)
def tls_connection(enable_tls, tls_require_cert): def tls_connection(enable_tls, tls_require_cert):
"""Try to login using LDAP user authentication over a TLS connection.""" """Try to login using LDAP user authentication over a TLS connection.
"""
servers = { servers = {
"openldap2": { "openldap2": {
"host": "openldap2", "host": "openldap2",
@ -152,7 +153,8 @@ def tls(self):
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS_Options_Default("1.0") RQ_SRS_007_LDAP_Configuration_Server_EnableTLS_Options_Default("1.0")
) )
def tls_enable_tls_default_yes(self): def tls_enable_tls_default_yes(self):
"""Check that the default value for the `enable_tls` is set to `yes`.""" """Check that the default value for the `enable_tls` is set to `yes`.
"""
servers = { servers = {
"openldap2": { "openldap2": {
"host": "openldap2", "host": "openldap2",
@ -171,7 +173,8 @@ def tls_enable_tls_default_yes(self):
RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert_Options_Default("1.0") RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert_Options_Default("1.0")
) )
def tls_require_cert_default_demand(self): def tls_require_cert_default_demand(self):
"""Check that the default value for the `tls_require_cert` is set to `demand`.""" """Check that the default value for the `tls_require_cert` is set to `demand`.
"""
servers = { servers = {
"openldap2": { "openldap2": {
"host": "openldap2", "host": "openldap2",
@ -210,7 +213,8 @@ def starttls(self):
RQ_SRS_007_LDAP_Configuration_Server_TLSCipherSuite("1.0") RQ_SRS_007_LDAP_Configuration_Server_TLSCipherSuite("1.0")
) )
def tls_cipher_suite(self): def tls_cipher_suite(self):
"""Check that `tls_cipher_suite` parameter can be used specify allowed cipher suites.""" """Check that `tls_cipher_suite` parameter can be used specify allowed cipher suites.
"""
servers = { servers = {
"openldap4": { "openldap4": {
"host": "openldap4", "host": "openldap4",
@ -241,7 +245,8 @@ def tls_cipher_suite(self):
]) ])
def tls_minimum_protocol_version(self, version, exitcode, message): def tls_minimum_protocol_version(self, version, exitcode, message):
"""Check that `tls_minimum_protocol_version` parameter can be used specify """Check that `tls_minimum_protocol_version` parameter can be used specify
to specify the minimum protocol version of SSL/TLS.""" to specify the minimum protocol version of SSL/TLS.
"""
servers = { servers = {
"openldap4": { "openldap4": {
@ -278,6 +283,8 @@ def tls_minimum_protocol_version(self, version, exitcode, message):
@TestFeature @TestFeature
@Name("connection protocols") @Name("connection protocols")
def feature(self, node="clickhouse1"): def feature(self, node="clickhouse1"):
"""Check different LDAP connection protocols.
"""
self.context.node = self.context.cluster.node(node) self.context.node = self.context.cluster.node(node)
for scenario in loads(current_module(), Scenario): for scenario in loads(current_module(), Scenario):

View File

@ -14,6 +14,7 @@ def scenario(self, node="clickhouse1"):
authenticate users. authenticate users.
""" """
self.context.node = self.context.cluster.node(node) self.context.node = self.context.cluster.node(node)
servers = { servers = {
"openldap1": { "openldap1": {
"host": "openldap1", "host": "openldap1",
@ -35,4 +36,6 @@ def scenario(self, node="clickhouse1"):
{"server": "openldap1", "username": "user1", "password": "user1", "login": True}, {"server": "openldap1", "username": "user1", "password": "user1", "login": True},
{"server": "openldap2", "username": "user2", "password": "user2", "login": True} {"server": "openldap2", "username": "user2", "password": "user2", "login": True}
] ]
login(servers, *users)
with When("I add multiple LDAP servers and users that use different servers and try to login"):
login(servers, *users)

View File

@ -267,5 +267,6 @@ def feature(self, node="clickhouse1"):
"""Check that LDAP server configuration. """Check that LDAP server configuration.
""" """
self.context.node = self.context.cluster.node(node) self.context.node = self.context.cluster.node(node)
for scenario in loads(current_module(), Scenario): for scenario in loads(current_module(), Scenario):
scenario() scenario()

View File

@ -28,7 +28,8 @@ servers = {
@TestOutline @TestOutline
def add_user_to_ldap_and_login(self, server, user=None, ch_user=None, login=None, exitcode=None, message=None): def add_user_to_ldap_and_login(self, server, user=None, ch_user=None, login=None, exitcode=None, message=None):
"""Add user to LDAP and ClickHouse and then try to login.""" """Add user to LDAP and ClickHouse and then try to login.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
if ch_user is None: if ch_user is None:
@ -91,23 +92,25 @@ def parallel_login(self, server, user_count=10, timeout=200):
with Given("a group of LDAP users"): with Given("a group of LDAP users"):
users = [{"cn": f"parallel_user{i}", "userpassword": randomword(20)} for i in range(user_count)] users = [{"cn": f"parallel_user{i}", "userpassword": randomword(20)} for i in range(user_count)]
with ldap_users(*users): with rbac_roles("ldap_role") as roles:
tasks = [] with ldap_external_user_directory(server=server, roles=roles, restart=True):
try: with ldap_users(*users):
with When("users try to login in parallel", description=""" tasks = []
* with valid username and password try:
* with invalid username and valid password with When("users try to login in parallel", description="""
* with valid username and invalid password * with valid username and password
"""): * with invalid username and valid password
p = Pool(15) * with valid username and invalid password
for i in range(25): """):
tasks.append(p.apply_async(login_with_valid_username_and_password, (users, i, 50,))) p = Pool(15)
tasks.append(p.apply_async(login_with_valid_username_and_invalid_password, (users, i, 50,))) for i in range(25):
tasks.append(p.apply_async(login_with_invalid_username_and_valid_password, (users, i, 50,))) tasks.append(p.apply_async(login_with_valid_username_and_password, (users, i, 50,)))
tasks.append(p.apply_async(login_with_valid_username_and_invalid_password, (users, i, 50,)))
tasks.append(p.apply_async(login_with_invalid_username_and_valid_password, (users, i, 50,)))
finally: finally:
with Then("it should work"): with Then("it should work"):
join(tasks, timeout) join(tasks, timeout)
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -124,34 +127,36 @@ def parallel_login_with_the_same_user(self, server, timeout=200):
with Given("only one LDAP user"): with Given("only one LDAP user"):
users = [{"cn": f"parallel_user1", "userpassword": randomword(20)}] users = [{"cn": f"parallel_user1", "userpassword": randomword(20)}]
with ldap_users(*users): with rbac_roles("ldap_role") as roles:
tasks = [] with ldap_external_user_directory(server=server, roles=roles, restart=True):
try: with ldap_users(*users):
with When("the same user tries to login in parallel", description=""" tasks = []
* with valid username and password try:
* with invalid username and valid password with When("the same user tries to login in parallel", description="""
* with valid username and invalid password * with valid username and password
"""): * with invalid username and valid password
p = Pool(15) * with valid username and invalid password
for i in range(25): """):
tasks.append(p.apply_async(login_with_valid_username_and_password, (users, i, 50,))) p = Pool(15)
tasks.append(p.apply_async(login_with_valid_username_and_invalid_password, (users, i, 50,))) for i in range(25):
tasks.append(p.apply_async(login_with_invalid_username_and_valid_password, (users, i, 50,))) tasks.append(p.apply_async(login_with_valid_username_and_password, (users, i, 50,)))
tasks.append(p.apply_async(login_with_valid_username_and_invalid_password, (users, i, 50,)))
tasks.append(p.apply_async(login_with_invalid_username_and_valid_password, (users, i, 50,)))
finally: finally:
with Then("it should work"): with Then("it should work"):
join(tasks, timeout) join(tasks, timeout)
@TestScenario @TestScenario
def login_after_ldap_external_user_directory_is_removed(self, server): def login_after_ldap_external_user_directory_is_removed(self, server):
"""Check that ClickHouse stops authenticating LDAP users """Check that ClickHouse stops authenticating LDAP users
after LDAP external user directory is removed. after LDAP external user directory is removed.
""" """
with When("I attempt to login after LDAP external user directory is added"): with When("I login after LDAP external user directory is added"):
with ldap_external_user_directory(server="openldap2", roles=[], restart=True): with ldap_external_user_directory(server="openldap2", roles=[], restart=True):
login_and_execute_query(username="user2", password="user2") login_and_execute_query(username="user2", password="user2")
with When("I attempt to login after LDAP external user directory is removed"): with And("I attempt to login after LDAP external user directory is removed"):
exitcode = 4 exitcode = 4
message = f"DB::Exception: user2: Authentication failed: password is incorrect or there is no user with such name" message = f"DB::Exception: user2: Authentication failed: password is incorrect or there is no user with such name"
login_and_execute_query(username="user2", password="user2", exitcode=exitcode, message=message) login_and_execute_query(username="user2", password="user2", exitcode=exitcode, message=message)
@ -318,29 +323,34 @@ def parallel_login_with_rbac_users(self, server, user_count=10, timeout=200):
users = [{"cn": f"parallel_user{i}", "userpassword": randomword(20)} for i in range(user_count)] users = [{"cn": f"parallel_user{i}", "userpassword": randomword(20)} for i in range(user_count)]
with rbac_users(*users): with rbac_roles("ldap_role") as roles:
tasks = [] with ldap_external_user_directory(server=server, roles=roles, restart=True):
try: with rbac_users(*users):
with When("I login in parallel"): tasks = []
p = Pool(15) try:
for i in range(25): with When("I login in parallel"):
tasks.append(p.apply_async(login_with_valid_username_and_password, (users, i, 50,))) p = Pool(15)
tasks.append(p.apply_async(login_with_valid_username_and_invalid_password, (users, i, 50,))) for i in range(25):
tasks.append(p.apply_async(login_with_invalid_username_and_valid_password, (users, i, 50,))) tasks.append(p.apply_async(login_with_valid_username_and_password, (users, i, 50,)))
finally: tasks.append(p.apply_async(login_with_valid_username_and_invalid_password, (users, i, 50,)))
with Then("it should work"): tasks.append(p.apply_async(login_with_invalid_username_and_valid_password, (users, i, 50,)))
join(tasks, timeout) finally:
with Then("it should work"):
join(tasks, timeout)
@TestScenario @TestScenario
@Requirements( @Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Users_Authentication_NewUsers("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Users_Authentication_NewUsers("1.0")
) )
def login_after_user_is_added_to_ldap(self, server): def login_after_user_is_added_to_ldap(self, server):
"""Check that user can login as soon as it is added to LDAP.""" """Check that user can login as soon as it is added to LDAP.
"""
user = {"cn": "myuser", "userpassword": "myuser"} user = {"cn": "myuser", "userpassword": "myuser"}
with When(f"I add user to LDAP and try to login"): with rbac_roles("ldap_role") as roles:
add_user_to_ldap_and_login(user=user, server=server) with ldap_external_user_directory(server=server, roles=roles, restart=True):
with When(f"I add user to LDAP and try to login"):
add_user_to_ldap_and_login(user=user, server=server)
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -348,29 +358,32 @@ def login_after_user_is_added_to_ldap(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_DeletedUsers("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_DeletedUsers("1.0")
) )
def login_after_user_is_deleted_from_ldap(self, server): def login_after_user_is_deleted_from_ldap(self, server):
"""Check that login fails after user is deleted from LDAP.""" """Check that login fails after user is deleted from LDAP.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
user = None user = None
try: with rbac_roles("ldap_role") as roles:
with Given(f"I add user to LDAP"): with ldap_external_user_directory(server=server, roles=roles, restart=True):
user = {"cn": "myuser", "userpassword": "myuser"} try:
user = add_user_to_ldap(**user) with Given(f"I add user to LDAP"):
user = {"cn": "myuser", "userpassword": "myuser"}
user = add_user_to_ldap(**user)
login_and_execute_query(username=user["cn"], password=user["userpassword"]) login_and_execute_query(username=user["cn"], password=user["userpassword"])
with When("I delete this user from LDAP"): with When("I delete this user from LDAP"):
delete_user_from_ldap(user) delete_user_from_ldap(user)
with Then("when I try to login again it should fail"): with Then("when I try to login again it should fail"):
login_and_execute_query(username=user["cn"], password=user["userpassword"], login_and_execute_query(username=user["cn"], password=user["userpassword"],
exitcode=4, exitcode=4,
message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name" message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
) )
finally: finally:
with Finally("I make sure LDAP user is deleted"): with Finally("I make sure LDAP user is deleted"):
if user is not None: if user is not None:
delete_user_from_ldap(user, exitcode=None) delete_user_from_ldap(user, exitcode=None)
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -378,33 +391,36 @@ def login_after_user_is_deleted_from_ldap(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_PasswordChanged("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_PasswordChanged("1.0")
) )
def login_after_user_password_changed_in_ldap(self, server): def login_after_user_password_changed_in_ldap(self, server):
"""Check that login fails after user password is changed in LDAP.""" """Check that login fails after user password is changed in LDAP.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
user = None user = None
try: with rbac_roles("ldap_role") as roles:
with Given(f"I add user to LDAP"): with ldap_external_user_directory(server=server, roles=roles, restart=True):
user = {"cn": "myuser", "userpassword": "myuser"} try:
user = add_user_to_ldap(**user) with Given(f"I add user to LDAP"):
user = {"cn": "myuser", "userpassword": "myuser"}
user = add_user_to_ldap(**user)
login_and_execute_query(username=user["cn"], password=user["userpassword"]) login_and_execute_query(username=user["cn"], password=user["userpassword"])
with When("I change user password in LDAP"): with When("I change user password in LDAP"):
change_user_password_in_ldap(user, "newpassword") change_user_password_in_ldap(user, "newpassword")
with Then("when I try to login again it should fail"): with Then("when I try to login again it should fail"):
login_and_execute_query(username=user["cn"], password=user["userpassword"], login_and_execute_query(username=user["cn"], password=user["userpassword"],
exitcode=4, exitcode=4,
message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name" message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
) )
with And("when I try to login with the new password it should work"): with And("when I try to login with the new password it should work"):
login_and_execute_query(username=user["cn"], password="newpassword") login_and_execute_query(username=user["cn"], password="newpassword")
finally: finally:
with Finally("I make sure LDAP user is deleted"): with Finally("I make sure LDAP user is deleted"):
if user is not None: if user is not None:
delete_user_from_ldap(user, exitcode=None) delete_user_from_ldap(user, exitcode=None)
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -412,30 +428,33 @@ def login_after_user_password_changed_in_ldap(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_UsernameChanged("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_UsernameChanged("1.0")
) )
def login_after_user_cn_changed_in_ldap(self, server): def login_after_user_cn_changed_in_ldap(self, server):
"""Check that login fails after user cn is changed in LDAP.""" """Check that login fails after user cn is changed in LDAP.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
user = None user = None
new_user = None new_user = None
try: with rbac_roles("ldap_role") as roles:
with Given(f"I add user to LDAP"): with ldap_external_user_directory(server=server, roles=roles, restart=True):
user = {"cn": "myuser", "userpassword": "myuser"} try:
user = add_user_to_ldap(**user) with Given(f"I add user to LDAP"):
user = {"cn": "myuser", "userpassword": "myuser"}
user = add_user_to_ldap(**user)
login_and_execute_query(username=user["cn"], password=user["userpassword"]) login_and_execute_query(username=user["cn"], password=user["userpassword"])
with When("I change user password in LDAP"): with When("I change user password in LDAP"):
new_user = change_user_cn_in_ldap(user, "myuser2") new_user = change_user_cn_in_ldap(user, "myuser2")
with Then("when I try to login again it should fail"): with Then("when I try to login again it should fail"):
login_and_execute_query(username=user["cn"], password=user["userpassword"], login_and_execute_query(username=user["cn"], password=user["userpassword"],
exitcode=4, exitcode=4,
message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name" message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
) )
finally: finally:
with Finally("I make sure LDAP user is deleted"): with Finally("I make sure LDAP user is deleted"):
if new_user is not None: if new_user is not None:
delete_user_from_ldap(new_user, exitcode=None) delete_user_from_ldap(new_user, exitcode=None)
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -443,33 +462,36 @@ def login_after_user_cn_changed_in_ldap(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_LDAPServerRestart("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_LDAPServerRestart("1.0")
) )
def login_after_ldap_server_is_restarted(self, server, timeout=60): def login_after_ldap_server_is_restarted(self, server, timeout=60):
"""Check that login succeeds after LDAP server is restarted.""" """Check that login succeeds after LDAP server is restarted.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
user = None user = None
try: with rbac_roles("ldap_role") as roles:
with Given(f"I add user to LDAP"): with ldap_external_user_directory(server=server, roles=roles, restart=True):
user = {"cn": "myuser", "userpassword": getuid()} try:
user = add_user_to_ldap(**user) with Given(f"I add user to LDAP"):
user = {"cn": "myuser", "userpassword": getuid()}
user = add_user_to_ldap(**user)
login_and_execute_query(username=user["cn"], password=user["userpassword"]) login_and_execute_query(username=user["cn"], password=user["userpassword"])
with When("I restart LDAP server"): with When("I restart LDAP server"):
self.context.ldap_node.restart() self.context.ldap_node.restart()
with Then("I try to login until it works", description=f"timeout {timeout} sec"): with Then("I try to login until it works", description=f"timeout {timeout} sec"):
started = time.time() started = time.time()
while True: while True:
r = self.context.node.query("SELECT 1", r = self.context.node.query("SELECT 1",
settings=[("user", user["cn"]), ("password", user["userpassword"])], settings=[("user", user["cn"]), ("password", user["userpassword"])],
no_checks=True) no_checks=True)
if r.exitcode == 0: if r.exitcode == 0:
break break
assert time.time() - started < timeout, error(r.output) assert time.time() - started < timeout, error(r.output)
finally: finally:
with Finally("I make sure LDAP user is deleted"): with Finally("I make sure LDAP user is deleted"):
if user is not None: if user is not None:
delete_user_from_ldap(user, exitcode=None) delete_user_from_ldap(user, exitcode=None)
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -477,33 +499,36 @@ def login_after_ldap_server_is_restarted(self, server, timeout=60):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_ClickHouseServerRestart("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_ClickHouseServerRestart("1.0")
) )
def login_after_clickhouse_server_is_restarted(self, server, timeout=60): def login_after_clickhouse_server_is_restarted(self, server, timeout=60):
"""Check that login succeeds after ClickHouse server is restarted.""" """Check that login succeeds after ClickHouse server is restarted.
"""
self.context.ldap_node = self.context.cluster.node(server) self.context.ldap_node = self.context.cluster.node(server)
user = None user = None
try: with rbac_roles("ldap_role") as roles:
with Given(f"I add user to LDAP"): with ldap_external_user_directory(server=server, roles=roles, restart=True):
user = {"cn": "myuser", "userpassword": getuid()} try:
user = add_user_to_ldap(**user) with Given(f"I add user to LDAP"):
user = {"cn": "myuser", "userpassword": getuid()}
user = add_user_to_ldap(**user)
login_and_execute_query(username=user["cn"], password=user["userpassword"]) login_and_execute_query(username=user["cn"], password=user["userpassword"])
with When("I restart ClickHouse server"): with When("I restart ClickHouse server"):
self.context.node.restart() self.context.node.restart()
with Then("I try to login until it works", description=f"timeout {timeout} sec"): with Then("I try to login until it works", description=f"timeout {timeout} sec"):
started = time.time() started = time.time()
while True: while True:
r = self.context.node.query("SELECT 1", r = self.context.node.query("SELECT 1",
settings=[("user", user["cn"]), ("password", user["userpassword"])], settings=[("user", user["cn"]), ("password", user["userpassword"])],
no_checks=True) no_checks=True)
if r.exitcode == 0: if r.exitcode == 0:
break break
assert time.time() - started < timeout, error(r.output) assert time.time() - started < timeout, error(r.output)
finally: finally:
with Finally("I make sure LDAP user is deleted"): with Finally("I make sure LDAP user is deleted"):
if user is not None: if user is not None:
delete_user_from_ldap(user, exitcode=None) delete_user_from_ldap(user, exitcode=None)
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -511,12 +536,15 @@ def login_after_clickhouse_server_is_restarted(self, server, timeout=60):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_Empty("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_Empty("1.0")
) )
def valid_username_with_valid_empty_password(self, server): def valid_username_with_valid_empty_password(self, server):
"""Check that we can't login using valid username that has empty password.""" """Check that we can't login using valid username that has empty password.
"""
user = {"cn": "empty_password", "userpassword": ""} user = {"cn": "empty_password", "userpassword": ""}
exitcode = 4 exitcode = 4
message = f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name" message = f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
add_user_to_ldap_and_login(user=user, exitcode=exitcode, message=message, server=server) with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
add_user_to_ldap_and_login(user=user, exitcode=exitcode, message=message, server=server)
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -524,41 +552,50 @@ def valid_username_with_valid_empty_password(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_Empty("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_Empty("1.0")
) )
def valid_username_and_invalid_empty_password(self, server): def valid_username_and_invalid_empty_password(self, server):
"""Check that we can't login using valid username but invalid empty password.""" """Check that we can't login using valid username but invalid empty password.
username = "user_non_empty_password" """
user = {"cn": username, "userpassword": username} username = "user_non_empty_password"
login = {"password": ""} user = {"cn": username, "userpassword": username}
login = {"password": ""}
exitcode = 4 exitcode = 4
message = f"DB::Exception: {username}: Authentication failed: password is incorrect or there is no user with such name" message = f"DB::Exception: {username}: Authentication failed: password is incorrect or there is no user with such name"
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server) with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server)
@TestScenario @TestScenario
@Requirements( @Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Valid("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Valid("1.0")
) )
def valid_username_and_password(self, server): def valid_username_and_password(self, server):
"""Check that we can login using valid username and password.""" """Check that we can login using valid username and password.
username = "valid_username_and_password" """
user = {"cn": username, "userpassword": username} username = "valid_username_and_password"
user = {"cn": username, "userpassword": username}
with When(f"I add user {username} to LDAP and try to login"): with rbac_roles("ldap_role") as roles:
add_user_to_ldap_and_login(user=user, server=server) with ldap_external_user_directory(server=server, roles=roles, restart=True):
with When(f"I add user {username} to LDAP and try to login"):
add_user_to_ldap_and_login(user=user, server=server)
@TestScenario @TestScenario
@Requirements( @Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0")
) )
def valid_username_and_password_invalid_server(self, server=None): def valid_username_and_password_invalid_server(self, server=None):
"""Check that we can't login using valid username and valid """Check that we can't login using valid username and valid
password but for a different server.""" password but for a different server.
self.context.ldap_node = self.context.cluster.node("openldap1") """
self.context.ldap_node = self.context.cluster.node("openldap1")
exitcode = 4 exitcode = 4
message = f"DB::Exception: user2: Authentication failed: password is incorrect or there is no user with such name" message = f"DB::Exception: user2: Authentication failed: password is incorrect or there is no user with such name"
login_and_execute_query(username="user2", password="user2", exitcode=exitcode, message=message) with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
login_and_execute_query(username="user2", password="user2", exitcode=exitcode, message=message)
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -566,26 +603,32 @@ def valid_username_and_password_invalid_server(self, server=None):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Username_Long("1.0"), RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Username_Long("1.0"),
) )
def valid_long_username_and_short_password(self, server): def valid_long_username_and_short_password(self, server):
"""Check that we can login using valid very long username and short password.""" """Check that we can login using valid very long username and short password.
username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890" """
user = {"cn": username, "userpassword": "long_username"} username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890"
user = {"cn": username, "userpassword": "long_username"}
add_user_to_ldap_and_login(user=user, server=server) with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
add_user_to_ldap_and_login(user=user, server=server)
@TestScenario @TestScenario
@Requirements( @Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0")
) )
def invalid_long_username_and_valid_short_password(self, server): def invalid_long_username_and_valid_short_password(self, server):
"""Check that we can't login using slightly invalid long username but valid password.""" """Check that we can't login using slightly invalid long username but valid password.
username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890" """
user = {"cn": username, "userpassword": "long_username"} username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890"
login = {"username": f"{username}?"} user = {"cn": username, "userpassword": "long_username"}
login = {"username": f"{username}?"}
exitcode = 4 exitcode = 4
message=f"DB::Exception: {login['username']}: Authentication failed: password is incorrect or there is no user with such name" message=f"DB::Exception: {login['username']}: Authentication failed: password is incorrect or there is no user with such name"
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server) with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server)
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -593,55 +636,68 @@ def invalid_long_username_and_valid_short_password(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_Long("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_Long("1.0")
) )
def valid_short_username_and_long_password(self, server): def valid_short_username_and_long_password(self, server):
"""Check that we can login using valid short username with very long password.""" """Check that we can login using valid short username with very long password.
username = "long_password" """
user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"} username = "long_password"
add_user_to_ldap_and_login(user=user, server=server) user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"}
with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
add_user_to_ldap_and_login(user=user, server=server)
@TestScenario @TestScenario
@Requirements( @Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0")
) )
def valid_short_username_and_invalid_long_password(self, server): def valid_short_username_and_invalid_long_password(self, server):
"""Check that we can't login using valid short username and invalid long password.""" """Check that we can't login using valid short username and invalid long password.
username = "long_password" """
user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"} username = "long_password"
login = {"password": user["userpassword"] + "1"} user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"}
login = {"password": user["userpassword"] + "1"}
exitcode = 4 exitcode = 4
message=f"DB::Exception: {username}: Authentication failed: password is incorrect or there is no user with such name" message=f"DB::Exception: {username}: Authentication failed: password is incorrect or there is no user with such name"
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server) with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server)
@TestScenario @TestScenario
@Requirements( @Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0")
) )
def valid_username_and_invalid_password(self, server): def valid_username_and_invalid_password(self, server):
"""Check that we can't login using valid username and invalid password.""" """Check that we can't login using valid username and invalid password.
username = "valid_username_and_invalid_password" """
user = {"cn": username, "userpassword": username} username = "valid_username_and_invalid_password"
login = {"password": user["userpassword"] + "1"} user = {"cn": username, "userpassword": username}
login = {"password": user["userpassword"] + "1"}
exitcode = 4 exitcode = 4
message=f"DB::Exception: {username}: Authentication failed: password is incorrect or there is no user with such name" message=f"DB::Exception: {username}: Authentication failed: password is incorrect or there is no user with such name"
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server) with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server)
@TestScenario @TestScenario
@Requirements( @Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0")
) )
def invalid_username_and_valid_password(self, server): def invalid_username_and_valid_password(self, server):
"""Check that we can't login using slightly invalid username but valid password.""" """Check that we can't login using slightly invalid username but valid password.
username = "invalid_username_and_valid_password" """
user = {"cn": username, "userpassword": username} username = "invalid_username_and_valid_password"
login = {"username": user["cn"] + "1"} user = {"cn": username, "userpassword": username}
login = {"username": user["cn"] + "1"}
exitcode = 4 exitcode = 4
message=f"DB::Exception: {login['username']}: Authentication failed: password is incorrect or there is no user with such name" message=f"DB::Exception: {login['username']}: Authentication failed: password is incorrect or there is no user with such name"
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server) with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server)
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -649,11 +705,14 @@ def invalid_username_and_valid_password(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Username_UTF8("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Username_UTF8("1.0")
) )
def valid_utf8_username_and_ascii_password(self, server): def valid_utf8_username_and_ascii_password(self, server):
"""Check that we can login using valid utf-8 username with ascii password.""" """Check that we can login using valid utf-8 username with ascii password.
username = "utf8_username_Gãńdåłf_Thê_Gręât" """
user = {"cn": username, "userpassword": "utf8_username"} username = "utf8_username_Gãńdåłf_Thê_Gręât"
user = {"cn": username, "userpassword": "utf8_username"}
add_user_to_ldap_and_login(user=user, server=server) with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
add_user_to_ldap_and_login(user=user, server=server)
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -661,17 +720,23 @@ def valid_utf8_username_and_ascii_password(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_UTF8("1.0") RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_UTF8("1.0")
) )
def valid_ascii_username_and_utf8_password(self, server): def valid_ascii_username_and_utf8_password(self, server):
"""Check that we can login using valid ascii username with utf-8 password.""" """Check that we can login using valid ascii username with utf-8 password.
"""
username = "utf8_password" username = "utf8_password"
user = {"cn": username, "userpassword": "utf8_password_Gãńdåłf_Thê_Gręât"} user = {"cn": username, "userpassword": "utf8_password_Gãńdåłf_Thê_Gręât"}
add_user_to_ldap_and_login(user=user, server=server) with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
add_user_to_ldap_and_login(user=user, server=server)
@TestScenario @TestScenario
def empty_username_and_empty_password(self, server=None): def empty_username_and_empty_password(self, server=None):
"""Check that we can login using empty username and empty password as """Check that we can login using empty username and empty password as
it will use the default user and that has an empty password.""" it will use the default user and that has an empty password.
login_and_execute_query(username="", password="") """
with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
login_and_execute_query(username="", password="")
@TestScenario @TestScenario
@Requirements( @Requirements(
@ -698,16 +763,18 @@ def user_lookup_priority(self, server):
"ldap": {"username": "ldap", "password": "userldap"} "ldap": {"username": "ldap", "password": "userldap"}
} }
with ldap_users(*[{"cn": user["username"], "userpassword": user["password"]} for user in users.values()]): with rbac_roles("ldap_role") as roles:
with rbac_users({"cn": "local", "userpassword": "local"}): with ldap_external_user_directory(server=server, roles=roles, restart=True):
with When("I try to login as 'default' user which is also defined in users.xml it should fail"): with ldap_users(*[{"cn": user["username"], "userpassword": user["password"]} for user in users.values()]):
login_and_execute_query(**users["default"], exitcode=exitcode, message=message.format(username="default")) with rbac_users({"cn": "local", "userpassword": "local"}):
with When("I try to login as 'default' user which is also defined in users.xml it should fail"):
login_and_execute_query(**users["default"], exitcode=exitcode, message=message.format(username="default"))
with When("I try to login as 'local' user which is also defined in local storage it should fail"): with When("I try to login as 'local' user which is also defined in local storage it should fail"):
login_and_execute_query(**users["local"], exitcode=exitcode, message=message.format(username="local")) login_and_execute_query(**users["local"], exitcode=exitcode, message=message.format(username="local"))
with When("I try to login as 'ldap' user defined only in LDAP it should work"): with When("I try to login as 'ldap' user defined only in LDAP it should work"):
login_and_execute_query(**users["ldap"]) login_and_execute_query(**users["ldap"])
@TestOutline(Feature) @TestOutline(Feature)
@ -728,7 +795,5 @@ def feature(self, servers=None, server=None, node="clickhouse1"):
server = "openldap1" server = "openldap1"
with ldap_servers(servers): with ldap_servers(servers):
with rbac_roles("ldap_role") as roles: for scenario in loads(current_module(), Scenario):
with ldap_external_user_directory(server=server, roles=roles, restart=True): Scenario(test=scenario, flags=TE)(server=server)
for scenario in loads(current_module(), Scenario):
Scenario(test=scenario, flags=TE)(server=server)

View File

@ -70,6 +70,15 @@ def rbac_roles(*roles):
with By(f"dropping role {role}", flags=TE): with By(f"dropping role {role}", flags=TE):
node.query(f"DROP ROLE IF EXISTS {role}") node.query(f"DROP ROLE IF EXISTS {role}")
def verify_ldap_user_exists(server, username, password):
"""Check that LDAP user is defined on the LDAP server.
"""
with By("searching LDAP database"):
ldap_node = current().context.cluster.node(server)
r = ldap_node.command(
f"ldapwhoami -H ldap://localhost -D 'cn={username},ou=users,dc=company,dc=com' -w {password}")
assert r.exitcode == 0, error()
def create_ldap_external_user_directory_config_content(server=None, roles=None, **kwargs): def create_ldap_external_user_directory_config_content(server=None, roles=None, **kwargs):
"""Create LDAP external user directory configuration file content. """Create LDAP external user directory configuration file content.
""" """
@ -197,8 +206,26 @@ def login(servers, directory_server, *users, config=None):
@TestStep(When) @TestStep(When)
@Name("I login as {username} and execute query") @Name("I login as {username} and execute query")
def login_and_execute_query(self, username, password, exitcode=None, message=None, steps=True, timeout=60): def login_and_execute_query(self, username, password, exitcode=None, message=None, steps=True, timeout=60, poll=False):
self.context.node.query("SELECT 1", if poll:
settings=[("user", username), ("password", password)], start_time = time.time()
exitcode=exitcode or 0, attempt = 0
message=message, steps=steps, timeout=timeout)
with By("repeatedly trying to login until successful or timeout"):
while True:
with When(f"attempt #{attempt}"):
r = self.context.node.query("SELECT 1", settings=[("user", username), ("password", password)],
no_checks=True, steps=False, timeout=timeout)
if r.exitcode == (0 if exitcode is None else exitcode) and (message in r.output if message is not None else True):
break
if time.time() - start_time > timeout:
fail(f"timeout {timeout} trying to login")
attempt += 1
else:
self.context.node.query("SELECT 1",
settings=[("user", username), ("password", password)],
exitcode=(0 if exitcode is None else exitcode),
message=message, steps=steps, timeout=timeout)