* Fixing tests in the tests/testflows/ldap/external_user_directory/tests/authentications.py suite

* Moving TestFlows runs to use classic output format for stdout
* Moving to TestFlows 1.6.57
* Updating LDAP test code styling
This commit is contained in:
Vitaliy Zakaznikov 2020-10-22 19:14:58 -04:00
parent a6439aba44
commit e743d2fd26
8 changed files with 381 additions and 254 deletions

View File

@ -35,7 +35,7 @@ RUN apt-get update \
ENV TZ=Europe/Moscow
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_VERSION 17.09.1-ce
@ -72,5 +72,5 @@ RUN set -x \
VOLUME /var/lib/docker
EXPOSE 2375
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

@ -28,6 +28,8 @@ servers = {
@TestStep(When)
@Name("I login as {username} and execute query")
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",
settings=[("user", username), ("password", password)],
exitcode=exitcode or 0,
@ -35,7 +37,8 @@ def login_and_execute_query(self, username, password, exitcode=None, message=Non
@TestScenario
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)
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")
)
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)
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")
)
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)
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")
)
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)
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")
)
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)
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")
)
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)
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")
)
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)
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")
)
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": ""}
exitcode = 4
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")
)
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"
user = {"cn": username, "userpassword": username}
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")
)
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"
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):
"""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")
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")
)
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"
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")
)
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"
user = {"cn": username, "userpassword": "long_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")
)
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"
user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"}
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")
)
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"
user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"}
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")
)
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"
user = {"cn": username, "userpassword": username}
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")
)
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"
user = {"cn": username, "userpassword": username}
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")
)
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"
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")
)
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"
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
def empty_username_and_empty_password(self, server=None, rbac=False):
"""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="")
@TestOutline(Feature)

View File

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

View File

@ -98,7 +98,8 @@ def starttls_with_custom_port(self):
login(servers, *users)
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 = {
"openldap2": {
"host": "openldap2",
@ -152,7 +153,8 @@ def tls(self):
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS_Options_Default("1.0")
)
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 = {
"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")
)
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 = {
"openldap2": {
"host": "openldap2",
@ -210,7 +213,8 @@ def starttls(self):
RQ_SRS_007_LDAP_Configuration_Server_TLSCipherSuite("1.0")
)
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 = {
"openldap4": {
"host": "openldap4",
@ -241,7 +245,8 @@ def tls_cipher_suite(self):
])
def tls_minimum_protocol_version(self, version, exitcode, message):
"""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 = {
"openldap4": {
@ -278,6 +283,8 @@ def tls_minimum_protocol_version(self, version, exitcode, message):
@TestFeature
@Name("connection protocols")
def feature(self, node="clickhouse1"):
"""Check different LDAP connection protocols.
"""
self.context.node = self.context.cluster.node(node)
for scenario in loads(current_module(), Scenario):

View File

@ -14,6 +14,7 @@ def scenario(self, node="clickhouse1"):
authenticate users.
"""
self.context.node = self.context.cluster.node(node)
servers = {
"openldap1": {
"host": "openldap1",
@ -35,4 +36,6 @@ def scenario(self, node="clickhouse1"):
{"server": "openldap1", "username": "user1", "password": "user1", "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.
"""
self.context.node = self.context.cluster.node(node)
for scenario in loads(current_module(), Scenario):
scenario()

View File

@ -28,7 +28,8 @@ servers = {
@TestOutline
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)
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"):
users = [{"cn": f"parallel_user{i}", "userpassword": randomword(20)} for i in range(user_count)]
with ldap_users(*users):
tasks = []
try:
with When("users try to login in parallel", description="""
* with valid username and password
* with invalid username and valid password
* with valid username and invalid password
"""):
p = Pool(15)
for i in range(25):
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,)))
with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
with ldap_users(*users):
tasks = []
try:
with When("users try to login in parallel", description="""
* with valid username and password
* with invalid username and valid password
* with valid username and invalid password
"""):
p = Pool(15)
for i in range(25):
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:
with Then("it should work"):
join(tasks, timeout)
finally:
with Then("it should work"):
join(tasks, timeout)
@TestScenario
@Requirements(
@ -124,34 +127,36 @@ def parallel_login_with_the_same_user(self, server, timeout=200):
with Given("only one LDAP user"):
users = [{"cn": f"parallel_user1", "userpassword": randomword(20)}]
with ldap_users(*users):
tasks = []
try:
with When("the same user tries to login in parallel", description="""
* with valid username and password
* with invalid username and valid password
* with valid username and invalid password
"""):
p = Pool(15)
for i in range(25):
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,)))
with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
with ldap_users(*users):
tasks = []
try:
with When("the same user tries to login in parallel", description="""
* with valid username and password
* with invalid username and valid password
* with valid username and invalid password
"""):
p = Pool(15)
for i in range(25):
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:
with Then("it should work"):
join(tasks, timeout)
finally:
with Then("it should work"):
join(tasks, timeout)
@TestScenario
def login_after_ldap_external_user_directory_is_removed(self, server):
"""Check that ClickHouse stops authenticating LDAP users
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):
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
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)
@ -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)]
with rbac_users(*users):
tasks = []
try:
with When("I login in parallel"):
p = Pool(15)
for i in range(25):
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:
with Then("it should work"):
join(tasks, timeout)
with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
with rbac_users(*users):
tasks = []
try:
with When("I login in parallel"):
p = Pool(15)
for i in range(25):
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:
with Then("it should work"):
join(tasks, timeout)
@TestScenario
@Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Users_Authentication_NewUsers("1.0")
)
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"}
with When(f"I add user to LDAP and try to login"):
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):
with When(f"I add user to LDAP and try to login"):
add_user_to_ldap_and_login(user=user, server=server)
@TestScenario
@Requirements(
@ -348,29 +358,32 @@ def login_after_user_is_added_to_ldap(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_DeletedUsers("1.0")
)
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)
user = None
try:
with Given(f"I add user to LDAP"):
user = {"cn": "myuser", "userpassword": "myuser"}
user = add_user_to_ldap(**user)
with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
try:
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"):
delete_user_from_ldap(user)
with When("I delete this user from LDAP"):
delete_user_from_ldap(user)
with Then("when I try to login again it should fail"):
login_and_execute_query(username=user["cn"], password=user["userpassword"],
exitcode=4,
message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
)
finally:
with Finally("I make sure LDAP user is deleted"):
if user is not None:
delete_user_from_ldap(user, exitcode=None)
with Then("when I try to login again it should fail"):
login_and_execute_query(username=user["cn"], password=user["userpassword"],
exitcode=4,
message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
)
finally:
with Finally("I make sure LDAP user is deleted"):
if user is not None:
delete_user_from_ldap(user, exitcode=None)
@TestScenario
@Requirements(
@ -378,33 +391,36 @@ def login_after_user_is_deleted_from_ldap(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_PasswordChanged("1.0")
)
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)
user = None
try:
with Given(f"I add user to LDAP"):
user = {"cn": "myuser", "userpassword": "myuser"}
user = add_user_to_ldap(**user)
with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
try:
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"):
change_user_password_in_ldap(user, "newpassword")
with When("I change user password in LDAP"):
change_user_password_in_ldap(user, "newpassword")
with Then("when I try to login again it should fail"):
login_and_execute_query(username=user["cn"], password=user["userpassword"],
exitcode=4,
message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
)
with Then("when I try to login again it should fail"):
login_and_execute_query(username=user["cn"], password=user["userpassword"],
exitcode=4,
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"):
login_and_execute_query(username=user["cn"], password="newpassword")
with And("when I try to login with the new password it should work"):
login_and_execute_query(username=user["cn"], password="newpassword")
finally:
with Finally("I make sure LDAP user is deleted"):
if user is not None:
delete_user_from_ldap(user, exitcode=None)
finally:
with Finally("I make sure LDAP user is deleted"):
if user is not None:
delete_user_from_ldap(user, exitcode=None)
@TestScenario
@Requirements(
@ -412,30 +428,33 @@ def login_after_user_password_changed_in_ldap(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_UsernameChanged("1.0")
)
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)
user = None
new_user = None
try:
with Given(f"I add user to LDAP"):
user = {"cn": "myuser", "userpassword": "myuser"}
user = add_user_to_ldap(**user)
with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
try:
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"):
new_user = change_user_cn_in_ldap(user, "myuser2")
with When("I change user password in LDAP"):
new_user = change_user_cn_in_ldap(user, "myuser2")
with Then("when I try to login again it should fail"):
login_and_execute_query(username=user["cn"], password=user["userpassword"],
exitcode=4,
message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
)
finally:
with Finally("I make sure LDAP user is deleted"):
if new_user is not None:
delete_user_from_ldap(new_user, exitcode=None)
with Then("when I try to login again it should fail"):
login_and_execute_query(username=user["cn"], password=user["userpassword"],
exitcode=4,
message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
)
finally:
with Finally("I make sure LDAP user is deleted"):
if new_user is not None:
delete_user_from_ldap(new_user, exitcode=None)
@TestScenario
@Requirements(
@ -443,33 +462,36 @@ def login_after_user_cn_changed_in_ldap(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_LDAPServerRestart("1.0")
)
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)
user = None
try:
with Given(f"I add user to LDAP"):
user = {"cn": "myuser", "userpassword": getuid()}
user = add_user_to_ldap(**user)
with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
try:
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"):
self.context.ldap_node.restart()
with When("I restart LDAP server"):
self.context.ldap_node.restart()
with Then("I try to login until it works", description=f"timeout {timeout} sec"):
started = time.time()
while True:
r = self.context.node.query("SELECT 1",
settings=[("user", user["cn"]), ("password", user["userpassword"])],
no_checks=True)
if r.exitcode == 0:
break
assert time.time() - started < timeout, error(r.output)
finally:
with Finally("I make sure LDAP user is deleted"):
if user is not None:
delete_user_from_ldap(user, exitcode=None)
with Then("I try to login until it works", description=f"timeout {timeout} sec"):
started = time.time()
while True:
r = self.context.node.query("SELECT 1",
settings=[("user", user["cn"]), ("password", user["userpassword"])],
no_checks=True)
if r.exitcode == 0:
break
assert time.time() - started < timeout, error(r.output)
finally:
with Finally("I make sure LDAP user is deleted"):
if user is not None:
delete_user_from_ldap(user, exitcode=None)
@TestScenario
@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")
)
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)
user = None
try:
with Given(f"I add user to LDAP"):
user = {"cn": "myuser", "userpassword": getuid()}
user = add_user_to_ldap(**user)
with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
try:
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"):
self.context.node.restart()
with When("I restart ClickHouse server"):
self.context.node.restart()
with Then("I try to login until it works", description=f"timeout {timeout} sec"):
started = time.time()
while True:
r = self.context.node.query("SELECT 1",
settings=[("user", user["cn"]), ("password", user["userpassword"])],
no_checks=True)
if r.exitcode == 0:
break
assert time.time() - started < timeout, error(r.output)
finally:
with Finally("I make sure LDAP user is deleted"):
if user is not None:
delete_user_from_ldap(user, exitcode=None)
with Then("I try to login until it works", description=f"timeout {timeout} sec"):
started = time.time()
while True:
r = self.context.node.query("SELECT 1",
settings=[("user", user["cn"]), ("password", user["userpassword"])],
no_checks=True)
if r.exitcode == 0:
break
assert time.time() - started < timeout, error(r.output)
finally:
with Finally("I make sure LDAP user is deleted"):
if user is not None:
delete_user_from_ldap(user, exitcode=None)
@TestScenario
@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")
)
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": ""}
exitcode = 4
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
@Requirements(
@ -524,41 +552,50 @@ def valid_username_with_valid_empty_password(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_Empty("1.0")
)
def valid_username_and_invalid_empty_password(self, server):
"""Check that we can't login using valid username but invalid empty password."""
username = "user_non_empty_password"
user = {"cn": username, "userpassword": username}
login = {"password": ""}
"""Check that we can't login using valid username but invalid empty password.
"""
username = "user_non_empty_password"
user = {"cn": username, "userpassword": username}
login = {"password": ""}
exitcode = 4
message = f"DB::Exception: {username}: Authentication failed: password is incorrect or there is no user with such name"
exitcode = 4
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
@Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Valid("1.0")
)
def valid_username_and_password(self, server):
"""Check that we can login using valid username and password."""
username = "valid_username_and_password"
user = {"cn": username, "userpassword": username}
"""Check that we can login using valid username and password.
"""
username = "valid_username_and_password"
user = {"cn": username, "userpassword": username}
with When(f"I add user {username} to LDAP and try to login"):
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):
with When(f"I add user {username} to LDAP and try to login"):
add_user_to_ldap_and_login(user=user, server=server)
@TestScenario
@Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0")
)
def valid_username_and_password_invalid_server(self, server=None):
"""Check that we can't login using valid username and valid
password but for a different server."""
self.context.ldap_node = self.context.cluster.node("openldap1")
"""Check that we can't login using valid username and valid
password but for a different server.
"""
self.context.ldap_node = self.context.cluster.node("openldap1")
exitcode = 4
message = f"DB::Exception: user2: Authentication failed: password is incorrect or there is no user with such name"
exitcode = 4
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
@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"),
)
def valid_long_username_and_short_password(self, server):
"""Check that we can login using valid very long username and short password."""
username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890"
user = {"cn": username, "userpassword": "long_username"}
"""Check that we can login using valid very long username and short password.
"""
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
@Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0")
)
def invalid_long_username_and_valid_short_password(self, server):
"""Check that we can't login using slightly invalid long username but valid password."""
username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890"
user = {"cn": username, "userpassword": "long_username"}
login = {"username": f"{username}?"}
"""Check that we can't login using slightly invalid long username but valid password.
"""
username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890"
user = {"cn": username, "userpassword": "long_username"}
login = {"username": f"{username}?"}
exitcode = 4
message=f"DB::Exception: {login['username']}: Authentication failed: password is incorrect or there is no user with such name"
exitcode = 4
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
@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")
)
def valid_short_username_and_long_password(self, server):
"""Check that we can login using valid short username with very long password."""
username = "long_password"
user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"}
add_user_to_ldap_and_login(user=user, server=server)
"""Check that we can login using valid short username with very long password.
"""
username = "long_password"
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
@Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0")
)
def valid_short_username_and_invalid_long_password(self, server):
"""Check that we can't login using valid short username and invalid long password."""
username = "long_password"
user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"}
login = {"password": user["userpassword"] + "1"}
"""Check that we can't login using valid short username and invalid long password.
"""
username = "long_password"
user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"}
login = {"password": user["userpassword"] + "1"}
exitcode = 4
message=f"DB::Exception: {username}: Authentication failed: password is incorrect or there is no user with such name"
exitcode = 4
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
@Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0")
)
def valid_username_and_invalid_password(self, server):
"""Check that we can't login using valid username and invalid password."""
username = "valid_username_and_invalid_password"
user = {"cn": username, "userpassword": username}
login = {"password": user["userpassword"] + "1"}
"""Check that we can't login using valid username and invalid password.
"""
username = "valid_username_and_invalid_password"
user = {"cn": username, "userpassword": username}
login = {"password": user["userpassword"] + "1"}
exitcode = 4
message=f"DB::Exception: {username}: Authentication failed: password is incorrect or there is no user with such name"
exitcode = 4
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
@Requirements(
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Invalid("1.0")
)
def invalid_username_and_valid_password(self, server):
"""Check that we can't login using slightly invalid username but valid password."""
username = "invalid_username_and_valid_password"
user = {"cn": username, "userpassword": username}
login = {"username": user["cn"] + "1"}
"""Check that we can't login using slightly invalid username but valid password.
"""
username = "invalid_username_and_valid_password"
user = {"cn": username, "userpassword": username}
login = {"username": user["cn"] + "1"}
exitcode = 4
message=f"DB::Exception: {login['username']}: Authentication failed: password is incorrect or there is no user with such name"
exitcode = 4
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
@Requirements(
@ -649,11 +705,14 @@ def invalid_username_and_valid_password(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Username_UTF8("1.0")
)
def valid_utf8_username_and_ascii_password(self, server):
"""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"}
"""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"}
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
@Requirements(
@ -661,17 +720,23 @@ def valid_utf8_username_and_ascii_password(self, server):
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_UTF8("1.0")
)
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"
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
def empty_username_and_empty_password(self, server=None):
"""Check that we can login using empty username and empty password as
it will use the default user and that has an empty password."""
login_and_execute_query(username="", password="")
it will use the default user and that has an empty 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
@Requirements(
@ -698,16 +763,18 @@ def user_lookup_priority(self, server):
"ldap": {"username": "ldap", "password": "userldap"}
}
with ldap_users(*[{"cn": user["username"], "userpassword": user["password"]} for user in users.values()]):
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 rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
with ldap_users(*[{"cn": user["username"], "userpassword": user["password"]} for user in users.values()]):
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"):
login_and_execute_query(**users["local"], exitcode=exitcode, message=message.format(username="local"))
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"))
with When("I try to login as 'ldap' user defined only in LDAP it should work"):
login_and_execute_query(**users["ldap"])
with When("I try to login as 'ldap' user defined only in LDAP it should work"):
login_and_execute_query(**users["ldap"])
@TestOutline(Feature)
@ -728,7 +795,5 @@ def feature(self, servers=None, server=None, node="clickhouse1"):
server = "openldap1"
with ldap_servers(servers):
with rbac_roles("ldap_role") as roles:
with ldap_external_user_directory(server=server, roles=roles, restart=True):
for scenario in loads(current_module(), Scenario):
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):
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):
"""Create LDAP external user directory configuration file content.
"""
@ -197,8 +206,26 @@ def login(servers, directory_server, *users, config=None):
@TestStep(When)
@Name("I login as {username} and execute query")
def login_and_execute_query(self, username, password, exitcode=None, message=None, steps=True, timeout=60):
self.context.node.query("SELECT 1",
settings=[("user", username), ("password", password)],
exitcode=exitcode or 0,
message=message, steps=steps, timeout=timeout)
def login_and_execute_query(self, username, password, exitcode=None, message=None, steps=True, timeout=60, poll=False):
if poll:
start_time = time.time()
attempt = 0
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)