mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-07 16:14:52 +00:00
970 lines
41 KiB
Python
970 lines
41 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
import random
|
||
|
import time
|
||
|
|
||
|
from multiprocessing.dummy import Pool
|
||
|
from testflows.core import *
|
||
|
from testflows.asserts import error
|
||
|
from ldap.authentication.tests.common import *
|
||
|
from ldap.authentication.requirements import *
|
||
|
|
||
|
servers = {
|
||
|
"openldap1": {
|
||
|
"host": "openldap1",
|
||
|
"port": "389",
|
||
|
"enable_tls": "no",
|
||
|
"auth_dn_prefix": "cn=",
|
||
|
"auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||
|
},
|
||
|
"openldap2": {
|
||
|
"host": "openldap2",
|
||
|
"port": "636",
|
||
|
"enable_tls": "yes",
|
||
|
"auth_dn_prefix": "cn=",
|
||
|
"auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||
|
"tls_require_cert": "never",
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@TestStep(When)
|
||
|
@Name("I login as {username} and execute query")
|
||
|
@Args(format_name=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",
|
||
|
settings=[("user", username), ("password", password)],
|
||
|
exitcode=exitcode or 0,
|
||
|
message=message, steps=steps)
|
||
|
|
||
|
@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.
|
||
|
"""
|
||
|
self.context.ldap_node = self.context.cluster.node(server)
|
||
|
|
||
|
if ch_user is None:
|
||
|
ch_user = {}
|
||
|
if login is None:
|
||
|
login = {}
|
||
|
if user is None:
|
||
|
user = {"cn": "myuser", "userpassword": "myuser"}
|
||
|
|
||
|
with ldap_user(**user) as user:
|
||
|
ch_user["username"] = ch_user.get("username", user["cn"])
|
||
|
ch_user["server"] = ch_user.get("server", user["_server"])
|
||
|
|
||
|
with ldap_authenticated_users(ch_user, config_file=f"ldap_users_{getuid()}.xml", restart=True, rbac=rbac):
|
||
|
username = login.get("username", user["cn"])
|
||
|
password = login.get("password", user["userpassword"])
|
||
|
login_and_execute_query(username=username, password=password, exitcode=exitcode, message=message)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Parallel("1.0"),
|
||
|
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.
|
||
|
"""
|
||
|
self.context.ldap_node = self.context.cluster.node(server)
|
||
|
user = None
|
||
|
|
||
|
users = [{"cn": f"parallel_user{i}", "userpassword": randomword(20)} for i in range(user_count)]
|
||
|
|
||
|
with ldap_users(*users):
|
||
|
with ldap_authenticated_users(*[{"username": user["cn"], "server": server} for user in users], rbac=rbac):
|
||
|
|
||
|
def login_with_valid_username_and_password(users, i, iterations=10):
|
||
|
with When(f"valid users try to login #{i}"):
|
||
|
for i in range(iterations):
|
||
|
random_user = users[random.randint(0, len(users)-1)]
|
||
|
login_and_execute_query(username=random_user["cn"], password=random_user["userpassword"], steps=False)
|
||
|
|
||
|
def login_with_valid_username_and_invalid_password(users, i, iterations=10):
|
||
|
with When(f"users try to login with valid username and invalid password #{i}"):
|
||
|
for i in range(iterations):
|
||
|
random_user = users[random.randint(0, len(users)-1)]
|
||
|
login_and_execute_query(username=random_user["cn"],
|
||
|
password=(random_user["userpassword"] + randomword(1)),
|
||
|
exitcode=4,
|
||
|
message=f"DB::Exception: {random_user['cn']}: Authentication failed: password is incorrect or there is no user with such name",
|
||
|
steps=False)
|
||
|
|
||
|
def login_with_invalid_username_and_valid_password(users, i, iterations=10):
|
||
|
with When(f"users try to login with invalid username and valid password #{i}"):
|
||
|
for i in range(iterations):
|
||
|
random_user = dict(users[random.randint(0, len(users)-1)])
|
||
|
random_user["cn"] += randomword(1)
|
||
|
login_and_execute_query(username=random_user["cn"],
|
||
|
password=random_user["userpassword"],
|
||
|
exitcode=4,
|
||
|
message=f"DB::Exception: {random_user['cn']}: Authentication failed: password is incorrect or there is no user with such name",
|
||
|
steps=False)
|
||
|
|
||
|
with When("I login in parallel"):
|
||
|
p = Pool(15)
|
||
|
tasks = []
|
||
|
for i in range(5):
|
||
|
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 Then("it should work"):
|
||
|
for task in tasks:
|
||
|
task.get(timeout=timeout)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Invalid("1.0"),
|
||
|
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.
|
||
|
"""
|
||
|
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 ldap_authenticated_users({"username": user["cn"], "server": server}, config_file=f"ldap_users_{getuid()}.xml",
|
||
|
restart=True, rbac=rbac):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
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)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Invalid("1.0"),
|
||
|
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.
|
||
|
"""
|
||
|
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 ldap_authenticated_users({"username": user["cn"], "server": server}, config_file=f"ldap_users_{getuid()}.xml",
|
||
|
restart=True, rbac=rbac):
|
||
|
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 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")
|
||
|
|
||
|
finally:
|
||
|
with Finally("I make sure LDAP user is deleted"):
|
||
|
if user is not None:
|
||
|
delete_user_from_ldap(user, exitcode=None)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Invalid("1.0"),
|
||
|
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.
|
||
|
"""
|
||
|
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 ldap_authenticated_users({"username": user["cn"], "server": server},
|
||
|
config_file=f"ldap_users_{getuid()}.xml", restart=True, rbac=rbac):
|
||
|
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 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(
|
||
|
RQ_SRS_007_LDAP_Authentication_Valid("1.0"),
|
||
|
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.
|
||
|
"""
|
||
|
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 ldap_authenticated_users({"username": user["cn"], "server": server}, rbac=rbac):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
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)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Valid("1.0"),
|
||
|
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.
|
||
|
"""
|
||
|
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 ldap_authenticated_users({"username": user["cn"], "server": server}, rbac=rbac):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
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)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Invalid("1.0"),
|
||
|
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.
|
||
|
"""
|
||
|
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, rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Invalid("1.0"),
|
||
|
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.
|
||
|
"""
|
||
|
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"
|
||
|
|
||
|
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server, rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
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.
|
||
|
"""
|
||
|
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, rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Invalid("1.0")
|
||
|
)
|
||
|
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.
|
||
|
"""
|
||
|
self.context.ldap_node = self.context.cluster.node("openldap1")
|
||
|
|
||
|
user = {"username": "user2", "userpassword": "user2", "server": "openldap1"}
|
||
|
|
||
|
exitcode = 4
|
||
|
message = f"DB::Exception: user2: Authentication failed: password is incorrect or there is no user with such name"
|
||
|
|
||
|
with ldap_authenticated_users(user, config_file=f"ldap_users_{getuid()}.xml", restart=True, rbac=rbac):
|
||
|
login_and_execute_query(username="user2", password="user2", exitcode=exitcode, message=message)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Valid("1.0"),
|
||
|
RQ_SRS_007_LDAP_Authentication_Username_Long("1.0"),
|
||
|
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.
|
||
|
"""
|
||
|
username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
||
|
user = {"cn": username, "userpassword": "long_username"}
|
||
|
|
||
|
add_user_to_ldap_and_login(user=user, server=server, rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
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.
|
||
|
"""
|
||
|
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"
|
||
|
|
||
|
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server, rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Valid("1.0"),
|
||
|
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.
|
||
|
"""
|
||
|
username = "long_password"
|
||
|
user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"}
|
||
|
add_user_to_ldap_and_login(user=user, server=server, rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
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.
|
||
|
"""
|
||
|
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"
|
||
|
|
||
|
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server, rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
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.
|
||
|
"""
|
||
|
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"
|
||
|
|
||
|
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server, rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
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.
|
||
|
"""
|
||
|
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"
|
||
|
|
||
|
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server, rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Valid("1.0"),
|
||
|
RQ_SRS_007_LDAP_Authentication_Username_UTF8("1.0"),
|
||
|
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.
|
||
|
"""
|
||
|
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, rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Valid("1.0"),
|
||
|
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.
|
||
|
"""
|
||
|
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, rbac=rbac)
|
||
|
|
||
|
@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.
|
||
|
"""
|
||
|
login_and_execute_query(username="", password="")
|
||
|
|
||
|
@TestScenario
|
||
|
@Tags("verification_cooldown")
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Configuration_Server_VerificationCooldown_Default("1.0")
|
||
|
)
|
||
|
def default_verification_cooldown_value(self, server, rbac=False, timeout=20):
|
||
|
"""Check that the default value (0) for the verification cooldown parameter
|
||
|
disables caching and forces contacting the LDAP server for each
|
||
|
authentication request.
|
||
|
"""
|
||
|
|
||
|
error_message = "DB::Exception: testVCD: Authentication failed: password is incorrect or there is no user with such name"
|
||
|
error_exitcode = 4
|
||
|
user = None
|
||
|
|
||
|
with Given("I have an LDAP configuration that uses the default verification_cooldown value (0)"):
|
||
|
servers = {"openldap1": {"host": "openldap1", "port": "389", "enable_tls": "no",
|
||
|
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||
|
}}
|
||
|
|
||
|
self.context.ldap_node = self.context.cluster.node(server)
|
||
|
|
||
|
try:
|
||
|
with Given("I add user to LDAP"):
|
||
|
user = {"cn": "testVCD", "userpassword": "testVCD"}
|
||
|
user = add_user_to_ldap(**user)
|
||
|
|
||
|
with ldap_servers(servers):
|
||
|
with ldap_authenticated_users({"username": user["cn"], "server": server}, config_file=f"ldap_users_{getuid()}.xml"):
|
||
|
with When("I login and execute a query"):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
with And("I change user password in LDAP"):
|
||
|
change_user_password_in_ldap(user, "newpassword")
|
||
|
|
||
|
with Then("when I try to login immediately with the old user password it should fail"):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"],
|
||
|
exitcode=error_exitcode, message=error_message)
|
||
|
|
||
|
finally:
|
||
|
with Finally("I make sure LDAP user is deleted"):
|
||
|
if user is not None:
|
||
|
delete_user_from_ldap(user, exitcode=None)
|
||
|
|
||
|
@TestScenario
|
||
|
@Tags("verification_cooldown")
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Configuration_Server_VerificationCooldown("1.0")
|
||
|
)
|
||
|
def valid_verification_cooldown_value_cn_change(self, server, rbac=False, timeout=20):
|
||
|
"""Check that we can perform requests without contacting the LDAP server
|
||
|
after successful authentication when the verification_cooldown parameter
|
||
|
is set and the user cn is changed.
|
||
|
"""
|
||
|
|
||
|
error_message = "DB::Exception: testVCD: Authentication failed: password is incorrect or there is no user with such name"
|
||
|
error_exitcode = 4
|
||
|
user = None
|
||
|
new_user = None
|
||
|
|
||
|
with Given("I have an LDAP configuration that sets verification_cooldown parameter to 2 sec"):
|
||
|
servers = { "openldap1": {
|
||
|
"host": "openldap1",
|
||
|
"port": "389",
|
||
|
"enable_tls": "no",
|
||
|
"auth_dn_prefix": "cn=",
|
||
|
"auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||
|
"verification_cooldown": "2"
|
||
|
}}
|
||
|
|
||
|
self.context.ldap_node = self.context.cluster.node(server)
|
||
|
|
||
|
try:
|
||
|
with Given("I add user to LDAP"):
|
||
|
user = {"cn": "testVCD", "userpassword": "testVCD"}
|
||
|
user = add_user_to_ldap(**user)
|
||
|
|
||
|
with ldap_servers(servers):
|
||
|
with ldap_authenticated_users({"username": user["cn"], "server": server}, config_file=f"ldap_users_{getuid()}.xml"):
|
||
|
with When("I login and execute a query"):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
with And("I change user cn in LDAP"):
|
||
|
new_user = change_user_cn_in_ldap(user, "testVCD2")
|
||
|
|
||
|
with Then("when I try to login again with the old user cn it should work"):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
with And("when I sleep for 2 seconds and try to log in, it should fail"):
|
||
|
time.sleep(2)
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"],
|
||
|
exitcode=error_exitcode, message=error_message)
|
||
|
|
||
|
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
|
||
|
@Tags("verification_cooldown")
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Configuration_Server_VerificationCooldown("1.0")
|
||
|
)
|
||
|
def valid_verification_cooldown_value_password_change(self, server, rbac=False, timeout=20):
|
||
|
"""Check that we can perform requests without contacting the LDAP server
|
||
|
after successful authentication when the verification_cooldown parameter
|
||
|
is set and the user password is changed.
|
||
|
"""
|
||
|
|
||
|
error_message = "DB::Exception: testVCD: Authentication failed: password is incorrect or there is no user with such name"
|
||
|
error_exitcode = 4
|
||
|
user = None
|
||
|
|
||
|
with Given("I have an LDAP configuration that sets verification_cooldown parameter to 2 sec"):
|
||
|
servers = { "openldap1": {
|
||
|
"host": "openldap1",
|
||
|
"port": "389",
|
||
|
"enable_tls": "no",
|
||
|
"auth_dn_prefix": "cn=",
|
||
|
"auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||
|
"verification_cooldown": "2"
|
||
|
}}
|
||
|
|
||
|
self.context.ldap_node = self.context.cluster.node(server)
|
||
|
|
||
|
try:
|
||
|
with Given("I add user to LDAP"):
|
||
|
user = {"cn": "testVCD", "userpassword": "testVCD"}
|
||
|
user = add_user_to_ldap(**user)
|
||
|
|
||
|
with ldap_servers(servers):
|
||
|
with ldap_authenticated_users({"username": user["cn"], "server": server}, config_file=f"ldap_users_{getuid()}.xml"):
|
||
|
with When("I login and execute a query"):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
with And("I change user password in LDAP"):
|
||
|
change_user_password_in_ldap(user, "newpassword")
|
||
|
|
||
|
with Then("when I try to login again with the old password it should work"):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
with And("when I sleep for 2 seconds and try to log in, it should fail"):
|
||
|
time.sleep(2)
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"],
|
||
|
exitcode=error_exitcode, message=error_message)
|
||
|
|
||
|
finally:
|
||
|
with Finally("I make sure LDAP user is deleted"):
|
||
|
if user is not None:
|
||
|
delete_user_from_ldap(user, exitcode=None)
|
||
|
|
||
|
@TestScenario
|
||
|
@Tags("verification_cooldown")
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Configuration_Server_VerificationCooldown("1.0")
|
||
|
)
|
||
|
def valid_verification_cooldown_value_ldap_unavailable(self, server, rbac=False, timeout=20):
|
||
|
"""Check that we can perform requests without contacting the LDAP server
|
||
|
after successful authentication when the verification_cooldown parameter
|
||
|
is set, even when the LDAP server is offline.
|
||
|
"""
|
||
|
|
||
|
error_message = "DB::Exception: testVCD: Authentication failed: password is incorrect or there is no user with such name"
|
||
|
error_exitcode = 4
|
||
|
user = None
|
||
|
|
||
|
with Given("I have an LDAP configuration that sets verification_cooldown parameter to 2 sec"):
|
||
|
servers = { "openldap1": {
|
||
|
"host": "openldap1",
|
||
|
"port": "389",
|
||
|
"enable_tls": "no",
|
||
|
"auth_dn_prefix": "cn=",
|
||
|
"auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||
|
"verification_cooldown": "2"
|
||
|
}}
|
||
|
|
||
|
self.context.ldap_node = self.context.cluster.node(server)
|
||
|
|
||
|
try:
|
||
|
with Given("I add a new user to LDAP"):
|
||
|
user = {"cn": "testVCD", "userpassword": "testVCD"}
|
||
|
user = add_user_to_ldap(**user)
|
||
|
|
||
|
with ldap_servers(servers):
|
||
|
with ldap_authenticated_users({"username": user["cn"], "server": server},
|
||
|
config_file=f"ldap_users_{getuid()}.xml"):
|
||
|
|
||
|
with When("I login and execute a query"):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
try:
|
||
|
with And("then I stop the ldap server"):
|
||
|
self.context.ldap_node.stop()
|
||
|
|
||
|
with Then("when I try to login again with the server offline it should work"):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
with And("when I sleep for 2 seconds and try to log in, it should fail"):
|
||
|
time.sleep(2)
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"],
|
||
|
exitcode=error_exitcode, message=error_message)
|
||
|
|
||
|
finally:
|
||
|
with Finally("I start the ldap server back up"):
|
||
|
self.context.ldap_node.start()
|
||
|
|
||
|
finally:
|
||
|
with Finally("I make sure LDAP user is deleted"):
|
||
|
if user is not None:
|
||
|
delete_user_from_ldap(user, exitcode=None)
|
||
|
|
||
|
@TestOutline
|
||
|
def repeat_requests(self, server, iterations, vcd_value, rbac=False):
|
||
|
"""Run repeated requests from some user to the LDAP server.
|
||
|
"""
|
||
|
|
||
|
user = None
|
||
|
|
||
|
with Given(f"I have an LDAP configuration that sets verification_cooldown parameter to {vcd_value} sec"):
|
||
|
servers = { "openldap1": {
|
||
|
"host": "openldap1",
|
||
|
"port": "389",
|
||
|
"enable_tls": "no",
|
||
|
"auth_dn_prefix": "cn=",
|
||
|
"auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||
|
"verification_cooldown": vcd_value
|
||
|
}}
|
||
|
|
||
|
self.context.ldap_node = self.context.cluster.node(server)
|
||
|
|
||
|
try:
|
||
|
with And("I add a new user to LDAP"):
|
||
|
user = {"cn": "testVCD", "userpassword": "testVCD"}
|
||
|
user = add_user_to_ldap(**user)
|
||
|
|
||
|
with ldap_servers(servers):
|
||
|
with ldap_authenticated_users({"username": user["cn"], "server": server}, config_file=f"ldap_users_{getuid()}.xml"):
|
||
|
with When(f"I login and execute some query {iterations} times"):
|
||
|
start_time = time.time()
|
||
|
r = self.context.node.command(f"time for i in {{1..{iterations}}}; do clickhouse client -q \"SELECT 1\" --user {user['cn']} --password {user['userpassword']} > /dev/null; done")
|
||
|
end_time = time.time()
|
||
|
|
||
|
return end_time - start_time
|
||
|
|
||
|
finally:
|
||
|
with Finally("I make sure LDAP user is deleted"):
|
||
|
if user is not None:
|
||
|
delete_user_from_ldap(user, exitcode=None)
|
||
|
|
||
|
@TestScenario
|
||
|
@Tags("verification_cooldown")
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Performance("1.0")
|
||
|
)
|
||
|
def verification_cooldown_performance(self, server, rbac=False, iterations=5000):
|
||
|
"""Check that login performance is better when the verification cooldown
|
||
|
parameter is set to a positive value when comparing to the case when
|
||
|
the verification cooldown parameter is turned off.
|
||
|
"""
|
||
|
|
||
|
vcd_time = 0
|
||
|
no_vcd_time = 0
|
||
|
|
||
|
with Example(f"Repeated requests with verification cooldown parameter set to 600 seconds, {iterations} iterations"):
|
||
|
vcd_time = repeat_requests(server=server, iterations=iterations, vcd_value="600", rbac=rbac)
|
||
|
metric("login_with_vcd_value_600", units="seconds", value=vcd_time)
|
||
|
|
||
|
with Example(f"Repeated requests with verification cooldown parameter set to 0 seconds, {iterations} iterations"):
|
||
|
no_vcd_time = repeat_requests(server=server, iterations=iterations, vcd_value="0", rbac=rbac)
|
||
|
metric("login_with_vcd_value_0", units="seconds", value=no_vcd_time)
|
||
|
|
||
|
with Then("The performance with verification cooldown parameter set is better than the performance with no verification cooldown parameter."):
|
||
|
assert no_vcd_time > vcd_time, error()
|
||
|
|
||
|
with And("Log the performance improvement as a percentage."):
|
||
|
metric("percentage_improvement", units="%", value=100*(no_vcd_time - vcd_time)/vcd_time)
|
||
|
|
||
|
@TestOutline
|
||
|
def check_verification_cooldown_reset_on_core_server_parameter_change(self, server,
|
||
|
parameter_name, parameter_value, rbac=False):
|
||
|
"""Check that the LDAP login cache is reset for all the LDAP authentication users
|
||
|
when verification_cooldown parameter is set after one of the core server
|
||
|
parameters is changed in the LDAP server configuration.
|
||
|
"""
|
||
|
|
||
|
config_d_dir="/etc/clickhouse-server/config.d"
|
||
|
config_file="ldap_servers.xml"
|
||
|
error_message = "DB::Exception: {user}: Authentication failed: password is incorrect or there is no user with such name"
|
||
|
error_exitcode = 4
|
||
|
user = None
|
||
|
config=None
|
||
|
updated_config=None
|
||
|
|
||
|
with Given("I have an LDAP configuration that sets verification_cooldown parameter to 600 sec"):
|
||
|
servers = { "openldap1": {
|
||
|
"host": "openldap1",
|
||
|
"port": "389",
|
||
|
"enable_tls": "no",
|
||
|
"auth_dn_prefix": "cn=",
|
||
|
"auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||
|
"verification_cooldown": "600"
|
||
|
}}
|
||
|
|
||
|
self.context.ldap_node = self.context.cluster.node(server)
|
||
|
|
||
|
with And("LDAP authenticated user"):
|
||
|
users = [
|
||
|
{"cn": f"testVCD_0", "userpassword": "testVCD_0"},
|
||
|
{"cn": f"testVCD_1", "userpassword": "testVCD_1"}
|
||
|
]
|
||
|
|
||
|
with And("I create LDAP servers configuration file"):
|
||
|
config = create_ldap_servers_config_content(servers, config_d_dir, config_file)
|
||
|
|
||
|
with ldap_users(*users) as users:
|
||
|
with ldap_servers(servers, restart=True):
|
||
|
with ldap_authenticated_users(*[{"username": user["cn"], "server": server} for user in users]):
|
||
|
with When("I login and execute a query"):
|
||
|
for user in users:
|
||
|
with By(f"as user {user['cn']}"):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
with And("I change user password in LDAP"):
|
||
|
for user in users:
|
||
|
with By(f"for user {user['cn']}"):
|
||
|
change_user_password_in_ldap(user, "newpassword")
|
||
|
|
||
|
with And(f"I change the server {parameter_name} core parameter", description=f"{parameter_value}"):
|
||
|
servers["openldap1"][parameter_name] = parameter_value
|
||
|
|
||
|
with And("I create an updated the config file that has a different server host name"):
|
||
|
updated_config = create_ldap_servers_config_content(servers, config_d_dir, config_file)
|
||
|
|
||
|
with modify_config(updated_config, restart=False):
|
||
|
with Then("when I try to log in it should fail as cache should have been reset"):
|
||
|
for user in users:
|
||
|
with By(f"as user {user['cn']}"):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"],
|
||
|
exitcode=error_exitcode, message=error_message.format(user=user["cn"]))
|
||
|
|
||
|
@TestScenario
|
||
|
@Tags("verification_cooldown")
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Reset_ChangeInCoreServerParameters("1.0")
|
||
|
)
|
||
|
def verification_cooldown_reset_on_server_host_parameter_change(self, server, rbac=False):
|
||
|
"""Check that the LDAP login cache is reset for all the LDAP authentication users
|
||
|
when verification_cooldown parameter is set after server host name
|
||
|
is changed in the LDAP server configuration.
|
||
|
"""
|
||
|
|
||
|
check_verification_cooldown_reset_on_core_server_parameter_change(server=server,
|
||
|
parameter_name="host", parameter_value="openldap2", rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Tags("verification_cooldown")
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Reset_ChangeInCoreServerParameters("1.0")
|
||
|
)
|
||
|
def verification_cooldown_reset_on_server_port_parameter_change(self, server, rbac=False):
|
||
|
"""Check that the LDAP login cache is reset for all the LDAP authentication users
|
||
|
when verification_cooldown parameter is set after server port is changed in the
|
||
|
LDAP server configuration.
|
||
|
"""
|
||
|
|
||
|
check_verification_cooldown_reset_on_core_server_parameter_change(server=server,
|
||
|
parameter_name="port", parameter_value="9006", rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Tags("verification_cooldown")
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Reset_ChangeInCoreServerParameters("1.0")
|
||
|
)
|
||
|
def verification_cooldown_reset_on_server_auth_dn_prefix_parameter_change(self, server, rbac=False):
|
||
|
"""Check that the LDAP login cache is reset for all the LDAP authentication users
|
||
|
when verification_cooldown parameter is set after server auth_dn_prefix
|
||
|
is changed in the LDAP server configuration.
|
||
|
"""
|
||
|
|
||
|
check_verification_cooldown_reset_on_core_server_parameter_change(server=server,
|
||
|
parameter_name="auth_dn_prefix", parameter_value="cxx=", rbac=rbac)
|
||
|
|
||
|
@TestScenario
|
||
|
@Tags("verification_cooldown")
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Reset_ChangeInCoreServerParameters("1.0")
|
||
|
)
|
||
|
def verification_cooldown_reset_on_server_auth_dn_suffix_parameter_change(self, server, rbac=False):
|
||
|
"""Check that the LDAP login cache is reset for all the LDAP authentication users
|
||
|
when verification_cooldown parameter is set after server auth_dn_suffix
|
||
|
is changed in the LDAP server configuration.
|
||
|
"""
|
||
|
|
||
|
check_verification_cooldown_reset_on_core_server_parameter_change(server=server,
|
||
|
parameter_name="auth_dn_suffix",
|
||
|
parameter_value=",ou=company,dc=users,dc=com", rbac=rbac)
|
||
|
|
||
|
|
||
|
@TestScenario
|
||
|
@Name("verification cooldown reset when invalid password is provided")
|
||
|
@Tags("verification_cooldown")
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Reset_InvalidPassword("1.0")
|
||
|
)
|
||
|
def scenario(self, server, rbac=False):
|
||
|
"""Check that cached bind requests for the user are discarded when
|
||
|
the user provides invalid login credentials.
|
||
|
"""
|
||
|
|
||
|
user = None
|
||
|
error_exitcode = 4
|
||
|
error_message = "DB::Exception: testVCD: Authentication failed: password is incorrect or there is no user with such name"
|
||
|
|
||
|
with Given("I have an LDAP configuration that sets verification_cooldown parameter to 600 sec"):
|
||
|
servers = { "openldap1": {
|
||
|
"host": "openldap1",
|
||
|
"port": "389",
|
||
|
"enable_tls": "no",
|
||
|
"auth_dn_prefix": "cn=",
|
||
|
"auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||
|
"verification_cooldown": "600"
|
||
|
}}
|
||
|
|
||
|
self.context.ldap_node = self.context.cluster.node(server)
|
||
|
|
||
|
try:
|
||
|
with Given("I add a new user to LDAP"):
|
||
|
user = {"cn": "testVCD", "userpassword": "testVCD"}
|
||
|
user = add_user_to_ldap(**user)
|
||
|
|
||
|
with ldap_servers(servers):
|
||
|
with ldap_authenticated_users({"username": user["cn"], "server": server},
|
||
|
config_file=f"ldap_users_{getuid()}.xml"):
|
||
|
|
||
|
with When("I login and execute a query"):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
with And("I change user password in LDAP"):
|
||
|
change_user_password_in_ldap(user, "newpassword")
|
||
|
|
||
|
with Then("When I try to log in with the cached password it should work"):
|
||
|
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||
|
|
||
|
with And("When I try to log in with an incorrect password it should fail"):
|
||
|
login_and_execute_query(username=user["cn"], password="incorrect", exitcode=error_exitcode,
|
||
|
message=error_message)
|
||
|
|
||
|
with And("When I try to log in with the cached password it should fail"):
|
||
|
login_and_execute_query(username=user["cn"], password="incorrect", exitcode=error_exitcode,
|
||
|
message=error_message)
|
||
|
|
||
|
finally:
|
||
|
with Finally("I make sure LDAP user is deleted"):
|
||
|
if user is not None:
|
||
|
delete_user_from_ldap(user, exitcode=None)
|
||
|
|
||
|
@TestFeature
|
||
|
def verification_cooldown(self, rbac, servers=None, node="clickhouse1"):
|
||
|
"""Check verification cooldown parameter functionality.
|
||
|
"""
|
||
|
for scenario in loads(current_module(), Scenario, filter=has.tag("verification_cooldown")):
|
||
|
scenario(server="openldap1", rbac=rbac)
|
||
|
|
||
|
|
||
|
@TestOutline(Feature)
|
||
|
@Name("user authentications")
|
||
|
@Requirements(
|
||
|
RQ_SRS_007_LDAP_Authentication_Mechanism_NamePassword("1.0")
|
||
|
)
|
||
|
@Examples("rbac", [
|
||
|
(False,),
|
||
|
(True, Requirements(RQ_SRS_007_LDAP_Configuration_User_RBAC("1.0")))
|
||
|
])
|
||
|
def feature(self, rbac, servers=None, node="clickhouse1"):
|
||
|
"""Check that users can be authenticated using an LDAP server when
|
||
|
users are configured either using an XML configuration file or RBAC.
|
||
|
"""
|
||
|
self.context.node = self.context.cluster.node(node)
|
||
|
|
||
|
if servers is None:
|
||
|
servers = globals()["servers"]
|
||
|
|
||
|
with ldap_servers(servers):
|
||
|
for scenario in loads(current_module(), Scenario, filter=~has.tag("verification_cooldown")):
|
||
|
scenario(server="openldap1", rbac=rbac)
|
||
|
|
||
|
Feature(test=verification_cooldown)(rbac=rbac, servers=servers, node=node)
|
||
|
|
||
|
|
||
|
|
||
|
|