mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-20 08:40:50 +00:00
Merge pull request #15988 from traceon/ldap-cache-login
Allow caching of successful "bind" attempts to LDAP server for configurable period of time
This commit is contained in:
commit
a84887aff8
@ -324,6 +324,9 @@
|
||||
auth_dn_prefix, auth_dn_suffix - prefix and suffix used to construct the DN to bind to.
|
||||
Effectively, the resulting DN will be constructed as auth_dn_prefix + escape(user_name) + auth_dn_suffix string.
|
||||
Note, that this implies that auth_dn_suffix should usually have comma ',' as its first non-space character.
|
||||
verification_cooldown - a period of time, in seconds, after a successful bind attempt, during which a user will be assumed
|
||||
to be successfully authenticated for all consecutive requests without contacting the LDAP server.
|
||||
Specify 0 (the default) to disable caching and force contacting the LDAP server for each authentication request.
|
||||
enable_tls - flag to trigger use of secure connection to the LDAP server.
|
||||
Specify 'no' for plain text (ldap://) protocol (not recommended).
|
||||
Specify 'yes' for LDAP over SSL/TLS (ldaps://) protocol (recommended, the default).
|
||||
@ -343,6 +346,7 @@
|
||||
<port>636</port>
|
||||
<auth_dn_prefix>uid=</auth_dn_prefix>
|
||||
<auth_dn_suffix>,ou=users,dc=example,dc=com</auth_dn_suffix>
|
||||
<verification_cooldown>300</verification_cooldown>
|
||||
<enable_tls>yes</enable_tls>
|
||||
<tls_minimum_protocol_version>tls1.2</tls_minimum_protocol_version>
|
||||
<tls_require_cert>demand</tls_require_cert>
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include <Access/Authentication.h>
|
||||
#include <Access/ExternalAuthenticators.h>
|
||||
#include <Access/LDAPClient.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Poco/SHA1Engine.h>
|
||||
|
||||
@ -49,7 +48,7 @@ Authentication::Digest Authentication::getPasswordDoubleSHA1() const
|
||||
}
|
||||
|
||||
|
||||
bool Authentication::isCorrectPassword(const String & password_, const String & user_, const ExternalAuthenticators & external_authenticators) const
|
||||
bool Authentication::isCorrectPassword(const String & user_, const String & password_, const ExternalAuthenticators & external_authenticators) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
@ -81,14 +80,7 @@ bool Authentication::isCorrectPassword(const String & password_, const String &
|
||||
}
|
||||
|
||||
case LDAP_SERVER:
|
||||
{
|
||||
auto ldap_server_params = external_authenticators.getLDAPServerParams(server_name);
|
||||
ldap_server_params.user = user_;
|
||||
ldap_server_params.password = password_;
|
||||
|
||||
LDAPSimpleAuthClient ldap_client(ldap_server_params);
|
||||
return ldap_client.check();
|
||||
}
|
||||
return external_authenticators.checkLDAPCredentials(server_name, user_, password_);
|
||||
|
||||
case MAX_TYPE:
|
||||
break;
|
||||
|
@ -88,8 +88,8 @@ public:
|
||||
void setServerName(const String & server_name_);
|
||||
|
||||
/// Checks if the provided password is correct. Returns false if not.
|
||||
/// User name and external authenticators' info are used only by some specific authentication type (e.g., LDAP_SERVER).
|
||||
bool isCorrectPassword(const String & password_, const String & user_, const ExternalAuthenticators & external_authenticators) const;
|
||||
/// User name and external authenticators are used by the specific authentication types only (e.g., LDAP_SERVER).
|
||||
bool isCorrectPassword(const String & user_, const String & password_, const ExternalAuthenticators & external_authenticators) const;
|
||||
|
||||
friend bool operator ==(const Authentication & lhs, const Authentication & rhs) { return (lhs.type == rhs.type) && (lhs.password_hash == rhs.password_hash); }
|
||||
friend bool operator !=(const Authentication & lhs, const Authentication & rhs) { return !(lhs == rhs); }
|
||||
|
@ -1,9 +1,13 @@
|
||||
#include <Access/ExternalAuthenticators.h>
|
||||
#include <Access/LDAPClient.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/quoteString.h>
|
||||
#include <Poco/Util/AbstractConfiguration.h>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -29,6 +33,7 @@ auto parseLDAPServer(const Poco::Util::AbstractConfiguration & config, const Str
|
||||
const bool has_port = config.has(ldap_server_config + ".port");
|
||||
const bool has_auth_dn_prefix = config.has(ldap_server_config + ".auth_dn_prefix");
|
||||
const bool has_auth_dn_suffix = config.has(ldap_server_config + ".auth_dn_suffix");
|
||||
const bool has_verification_cooldown = config.has(ldap_server_config + ".verification_cooldown");
|
||||
const bool has_enable_tls = config.has(ldap_server_config + ".enable_tls");
|
||||
const bool has_tls_minimum_protocol_version = config.has(ldap_server_config + ".tls_minimum_protocol_version");
|
||||
const bool has_tls_require_cert = config.has(ldap_server_config + ".tls_require_cert");
|
||||
@ -52,6 +57,9 @@ auto parseLDAPServer(const Poco::Util::AbstractConfiguration & config, const Str
|
||||
if (has_auth_dn_suffix)
|
||||
params.auth_dn_suffix = config.getString(ldap_server_config + ".auth_dn_suffix");
|
||||
|
||||
if (has_verification_cooldown)
|
||||
params.verification_cooldown = std::chrono::seconds{config.getUInt64(ldap_server_config + ".verification_cooldown")};
|
||||
|
||||
if (has_enable_tls)
|
||||
{
|
||||
String enable_tls_lc_str = config.getString(ldap_server_config + ".enable_tls");
|
||||
@ -130,16 +138,28 @@ auto parseLDAPServer(const Poco::Util::AbstractConfiguration & config, const Str
|
||||
return params;
|
||||
}
|
||||
|
||||
void parseAndAddLDAPServers(ExternalAuthenticators & external_authenticators, const Poco::Util::AbstractConfiguration & config, Poco::Logger * log)
|
||||
}
|
||||
|
||||
void ExternalAuthenticators::reset()
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
ldap_server_params.clear();
|
||||
ldap_server_caches.clear();
|
||||
}
|
||||
|
||||
void ExternalAuthenticators::setConfiguration(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log)
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
|
||||
reset();
|
||||
|
||||
Poco::Util::AbstractConfiguration::Keys ldap_server_names;
|
||||
config.keys("ldap_servers", ldap_server_names);
|
||||
|
||||
for (const auto & ldap_server_name : ldap_server_names)
|
||||
{
|
||||
try
|
||||
{
|
||||
external_authenticators.setLDAPServerParams(ldap_server_name, parseLDAPServer(config, ldap_server_name));
|
||||
ldap_server_params.insert_or_assign(ldap_server_name, parseLDAPServer(config, ldap_server_name));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -148,35 +168,100 @@ void parseAndAddLDAPServers(ExternalAuthenticators & external_authenticators, co
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ExternalAuthenticators::reset()
|
||||
bool ExternalAuthenticators::checkLDAPCredentials(const String & server, const String & user_name, const String & password) const
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
ldap_server_params.clear();
|
||||
}
|
||||
std::optional<LDAPServerParams> params;
|
||||
std::size_t params_hash = 0;
|
||||
|
||||
void ExternalAuthenticators::setConfiguration(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log)
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
reset();
|
||||
parseAndAddLDAPServers(*this, config, log);
|
||||
}
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
|
||||
void ExternalAuthenticators::setLDAPServerParams(const String & server, const LDAPServerParams & params)
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
ldap_server_params.erase(server);
|
||||
ldap_server_params[server] = params;
|
||||
}
|
||||
// Retrieve the server parameters.
|
||||
const auto pit = ldap_server_params.find(server);
|
||||
if (pit == ldap_server_params.end())
|
||||
throw Exception("LDAP server '" + server + "' is not configured", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
LDAPServerParams ExternalAuthenticators::getLDAPServerParams(const String & server) const
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
auto it = ldap_server_params.find(server);
|
||||
if (it == ldap_server_params.end())
|
||||
throw Exception("LDAP server '" + server + "' is not configured", ErrorCodes::BAD_ARGUMENTS);
|
||||
return it->second;
|
||||
params = pit->second;
|
||||
params->user = user_name;
|
||||
params->password = password;
|
||||
params_hash = params->getCoreHash();
|
||||
|
||||
// Check the cache, but only if the caching is enabled at all.
|
||||
if (params->verification_cooldown > std::chrono::seconds{0})
|
||||
{
|
||||
const auto cit = ldap_server_caches.find(server);
|
||||
if (cit != ldap_server_caches.end())
|
||||
{
|
||||
auto & cache = cit->second;
|
||||
|
||||
const auto eit = cache.find(user_name);
|
||||
if (eit != cache.end())
|
||||
{
|
||||
const auto & entry = eit->second;
|
||||
const auto last_check_period = std::chrono::steady_clock::now() - entry.last_successful_authentication_timestamp;
|
||||
|
||||
if (
|
||||
// Forbid the initial values explicitly.
|
||||
entry.last_successful_params_hash != 0 &&
|
||||
entry.last_successful_authentication_timestamp != std::chrono::steady_clock::time_point{} &&
|
||||
|
||||
// Check if we can safely "reuse" the result of the previous successful password verification.
|
||||
entry.last_successful_params_hash == params_hash &&
|
||||
last_check_period >= std::chrono::seconds{0} &&
|
||||
last_check_period <= params->verification_cooldown
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Erase the entry, if expired.
|
||||
if (last_check_period > params->verification_cooldown)
|
||||
cache.erase(eit);
|
||||
}
|
||||
|
||||
// Erase the cache, if empty.
|
||||
if (cache.empty())
|
||||
ldap_server_caches.erase(cit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LDAPSimpleAuthClient client(params.value());
|
||||
const auto result = client.check();
|
||||
const auto current_check_timestamp = std::chrono::steady_clock::now();
|
||||
|
||||
// Update the cache, but only if this is the latest check and the server is still configured in a compatible way.
|
||||
if (result)
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
|
||||
// If the server was removed from the config while we were checking the password, we discard the current result.
|
||||
const auto pit = ldap_server_params.find(server);
|
||||
if (pit == ldap_server_params.end())
|
||||
return false;
|
||||
|
||||
auto new_params = pit->second;
|
||||
new_params.user = user_name;
|
||||
new_params.password = password;
|
||||
|
||||
// If the critical server params have changed while we were checking the password, we discard the current result.
|
||||
if (params_hash != new_params.getCoreHash())
|
||||
return false;
|
||||
|
||||
auto & entry = ldap_server_caches[server][user_name];
|
||||
if (entry.last_successful_authentication_timestamp < current_check_timestamp)
|
||||
{
|
||||
entry.last_successful_params_hash = params_hash;
|
||||
entry.last_successful_authentication_timestamp = current_check_timestamp;
|
||||
}
|
||||
else if (entry.last_successful_params_hash != params_hash)
|
||||
{
|
||||
// Somehow a newer check with different params/password succeeded, so the current result is obsolete and we discard it.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,9 +3,10 @@
|
||||
#include <Access/LDAPParams.h>
|
||||
#include <common/types.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace Poco
|
||||
@ -27,13 +28,23 @@ class ExternalAuthenticators
|
||||
public:
|
||||
void reset();
|
||||
void setConfiguration(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log);
|
||||
bool checkLDAPCredentials(const String & server, const String & user_name, const String & password) const;
|
||||
|
||||
void setLDAPServerParams(const String & server, const LDAPServerParams & params);
|
||||
LDAPServerParams getLDAPServerParams(const String & server) const;
|
||||
private:
|
||||
struct LDAPCacheEntry
|
||||
{
|
||||
std::size_t last_successful_params_hash = 0;
|
||||
std::chrono::steady_clock::time_point last_successful_authentication_timestamp;
|
||||
};
|
||||
|
||||
using LDAPServerCache = std::unordered_map<String, LDAPCacheEntry>; // user name -> cache entry
|
||||
using LDAPServerCaches = std::map<String, LDAPServerCache>; // server name -> cache
|
||||
using LDAPServersParams = std::map<String, LDAPServerParams>; // server name -> params
|
||||
|
||||
private:
|
||||
mutable std::recursive_mutex mutex;
|
||||
std::map<String, LDAPServerParams> ldap_server_params;
|
||||
LDAPServersParams ldap_server_params;
|
||||
mutable LDAPServerCaches ldap_server_caches;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -463,7 +463,7 @@ UUID IAccessStorage::loginImpl(
|
||||
|
||||
bool IAccessStorage::isPasswordCorrectImpl(const User & user, const String & password, const ExternalAuthenticators & external_authenticators) const
|
||||
{
|
||||
return user.authentication.isCorrectPassword(password, user.getName(), external_authenticators);
|
||||
return user.authentication.isCorrectPassword(user.getName(), password, external_authenticators);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include <common/types.h>
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
|
||||
@ -68,10 +70,26 @@ struct LDAPServerParams
|
||||
String user;
|
||||
String password;
|
||||
|
||||
std::chrono::seconds verification_cooldown{0};
|
||||
|
||||
std::chrono::seconds operation_timeout{40};
|
||||
std::chrono::seconds network_timeout{30};
|
||||
std::chrono::seconds search_timeout{20};
|
||||
std::uint32_t search_limit = 100;
|
||||
|
||||
std::size_t getCoreHash() const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
|
||||
boost::hash_combine(seed, host);
|
||||
boost::hash_combine(seed, port);
|
||||
boost::hash_combine(seed, auth_dn_prefix);
|
||||
boost::hash_combine(seed, auth_dn_suffix);
|
||||
boost::hash_combine(seed, user);
|
||||
boost::hash_combine(seed, password);
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -37,6 +37,23 @@ class Node(object):
|
||||
|
||||
self.cluster.command(None, f'{self.cluster.docker_compose} restart {self.name}', timeout=timeout)
|
||||
|
||||
def start(self, timeout=300, safe=True):
|
||||
"""Start node.
|
||||
"""
|
||||
self.cluster.command(None, f'{self.cluster.docker_compose} start {self.name}', timeout=timeout)
|
||||
|
||||
|
||||
def stop(self, timeout=300, safe=True):
|
||||
"""Stop node.
|
||||
"""
|
||||
with self.cluster.lock:
|
||||
for key in list(self.cluster._bash.keys()):
|
||||
if key.endswith(f"-{self.name}"):
|
||||
shell = self.cluster._bash.pop(key)
|
||||
shell.__exit__(None, None, None)
|
||||
|
||||
self.cluster.command(None, f'{self.cluster.docker_compose} stop {self.name}', timeout=timeout)
|
||||
|
||||
def command(self, *args, **kwargs):
|
||||
return self.cluster.command(self.name, *args, **kwargs)
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
# SRS-007 ClickHouse Authentication of Users via LDAP
|
||||
# Software Requirements Specification
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@ -57,22 +58,28 @@
|
||||
* 4.2.25 [RQ.SRS-007.LDAP.Configuration.Server.TLSCACertDir](#rqsrs-007ldapconfigurationservertlscacertdir)
|
||||
* 4.2.26 [RQ.SRS-007.LDAP.Configuration.Server.TLSCACertFile](#rqsrs-007ldapconfigurationservertlscacertfile)
|
||||
* 4.2.27 [RQ.SRS-007.LDAP.Configuration.Server.TLSCipherSuite](#rqsrs-007ldapconfigurationservertlsciphersuite)
|
||||
* 4.2.28 [RQ.SRS-007.LDAP.Configuration.Server.Syntax](#rqsrs-007ldapconfigurationserversyntax)
|
||||
* 4.2.29 [RQ.SRS-007.LDAP.Configuration.User.RBAC](#rqsrs-007ldapconfigurationuserrbac)
|
||||
* 4.2.30 [RQ.SRS-007.LDAP.Configuration.User.Syntax](#rqsrs-007ldapconfigurationusersyntax)
|
||||
* 4.2.31 [RQ.SRS-007.LDAP.Configuration.User.Name.Empty](#rqsrs-007ldapconfigurationusernameempty)
|
||||
* 4.2.32 [RQ.SRS-007.LDAP.Configuration.User.BothPasswordAndLDAP](#rqsrs-007ldapconfigurationuserbothpasswordandldap)
|
||||
* 4.2.33 [RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.NotDefined](#rqsrs-007ldapconfigurationuserldapinvalidservernamenotdefined)
|
||||
* 4.2.34 [RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.Empty](#rqsrs-007ldapconfigurationuserldapinvalidservernameempty)
|
||||
* 4.2.35 [RQ.SRS-007.LDAP.Configuration.User.OnlyOneServer](#rqsrs-007ldapconfigurationuseronlyoneserver)
|
||||
* 4.2.36 [RQ.SRS-007.LDAP.Configuration.User.Name.Long](#rqsrs-007ldapconfigurationusernamelong)
|
||||
* 4.2.37 [RQ.SRS-007.LDAP.Configuration.User.Name.UTF8](#rqsrs-007ldapconfigurationusernameutf8)
|
||||
* 4.2.38 [RQ.SRS-007.LDAP.Authentication.Username.Empty](#rqsrs-007ldapauthenticationusernameempty)
|
||||
* 4.2.39 [RQ.SRS-007.LDAP.Authentication.Username.Long](#rqsrs-007ldapauthenticationusernamelong)
|
||||
* 4.2.40 [RQ.SRS-007.LDAP.Authentication.Username.UTF8](#rqsrs-007ldapauthenticationusernameutf8)
|
||||
* 4.2.41 [RQ.SRS-007.LDAP.Authentication.Password.Empty](#rqsrs-007ldapauthenticationpasswordempty)
|
||||
* 4.2.42 [RQ.SRS-007.LDAP.Authentication.Password.Long](#rqsrs-007ldapauthenticationpasswordlong)
|
||||
* 4.2.43 [RQ.SRS-007.LDAP.Authentication.Password.UTF8](#rqsrs-007ldapauthenticationpasswordutf8)
|
||||
* 4.2.28 [RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown](#rqsrs-007ldapconfigurationserververificationcooldown)
|
||||
* 4.2.29 [RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown.Default](#rqsrs-007ldapconfigurationserververificationcooldowndefault)
|
||||
* 4.2.30 [RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown.Invalid](#rqsrs-007ldapconfigurationserververificationcooldowninvalid)
|
||||
* 4.2.31 [RQ.SRS-007.LDAP.Configuration.Server.Syntax](#rqsrs-007ldapconfigurationserversyntax)
|
||||
* 4.2.32 [RQ.SRS-007.LDAP.Configuration.User.RBAC](#rqsrs-007ldapconfigurationuserrbac)
|
||||
* 4.2.33 [RQ.SRS-007.LDAP.Configuration.User.Syntax](#rqsrs-007ldapconfigurationusersyntax)
|
||||
* 4.2.34 [RQ.SRS-007.LDAP.Configuration.User.Name.Empty](#rqsrs-007ldapconfigurationusernameempty)
|
||||
* 4.2.35 [RQ.SRS-007.LDAP.Configuration.User.BothPasswordAndLDAP](#rqsrs-007ldapconfigurationuserbothpasswordandldap)
|
||||
* 4.2.36 [RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.NotDefined](#rqsrs-007ldapconfigurationuserldapinvalidservernamenotdefined)
|
||||
* 4.2.37 [RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.Empty](#rqsrs-007ldapconfigurationuserldapinvalidservernameempty)
|
||||
* 4.2.38 [RQ.SRS-007.LDAP.Configuration.User.OnlyOneServer](#rqsrs-007ldapconfigurationuseronlyoneserver)
|
||||
* 4.2.39 [RQ.SRS-007.LDAP.Configuration.User.Name.Long](#rqsrs-007ldapconfigurationusernamelong)
|
||||
* 4.2.40 [RQ.SRS-007.LDAP.Configuration.User.Name.UTF8](#rqsrs-007ldapconfigurationusernameutf8)
|
||||
* 4.2.41 [RQ.SRS-007.LDAP.Authentication.Username.Empty](#rqsrs-007ldapauthenticationusernameempty)
|
||||
* 4.2.42 [RQ.SRS-007.LDAP.Authentication.Username.Long](#rqsrs-007ldapauthenticationusernamelong)
|
||||
* 4.2.43 [RQ.SRS-007.LDAP.Authentication.Username.UTF8](#rqsrs-007ldapauthenticationusernameutf8)
|
||||
* 4.2.44 [RQ.SRS-007.LDAP.Authentication.Password.Empty](#rqsrs-007ldapauthenticationpasswordempty)
|
||||
* 4.2.45 [RQ.SRS-007.LDAP.Authentication.Password.Long](#rqsrs-007ldapauthenticationpasswordlong)
|
||||
* 4.2.46 [RQ.SRS-007.LDAP.Authentication.Password.UTF8](#rqsrs-007ldapauthenticationpasswordutf8)
|
||||
* 4.2.47 [RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Performance](#rqsrs-007ldapauthenticationverificationcooldownperformance)
|
||||
* 4.2.48 [RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Reset.ChangeInCoreServerParameters](#rqsrs-007ldapauthenticationverificationcooldownresetchangeincoreserverparameters)
|
||||
* 4.2.49 [RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Reset.InvalidPassword](#rqsrs-007ldapauthenticationverificationcooldownresetinvalidpassword)
|
||||
* 5 [References](#references)
|
||||
|
||||
## Revision History
|
||||
@ -393,9 +400,44 @@ For example,
|
||||
The available suites SHALL depend on the [OpenSSL] library version and variant used to build
|
||||
[ClickHouse] and therefore might change.
|
||||
|
||||
#### RQ.SRS-007.LDAP.Configuration.Server.Syntax
|
||||
#### RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL support `verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
that SHALL define a period of time, in seconds, after a successful bind attempt, during which a user SHALL be assumed
|
||||
to be successfully authenticated for all consecutive requests without contacting the [LDAP] server.
|
||||
After period of time since the last successful attempt expires then on the authentication attempt
|
||||
SHALL result in contacting the [LDAP] server to verify the username and password.
|
||||
|
||||
#### RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown.Default
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] `verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
SHALL have a default value of `0` that disables caching and forces contacting
|
||||
the [LDAP] server for each authentication request.
|
||||
|
||||
#### RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown.Invalid
|
||||
version: 1.0
|
||||
|
||||
[Clickhouse] SHALL return an error if the value provided for the `verification_cooldown` parameter is not a valid positive integer.
|
||||
|
||||
For example:
|
||||
|
||||
* negative integer
|
||||
* string
|
||||
* empty value
|
||||
* extremely large positive value (overflow)
|
||||
* extremely large negative value (overflow)
|
||||
|
||||
The error SHALL appear in the log and SHALL be similar to the following:
|
||||
|
||||
```bash
|
||||
<Error> Access(user directories): Could not parse LDAP server `openldap1`: Poco::Exception. Code: 1000, e.code() = 0, e.displayText() = Syntax error: Not a valid unsigned integer: *input value*
|
||||
```
|
||||
|
||||
#### RQ.SRS-007.LDAP.Configuration.Server.Syntax
|
||||
version: 2.0
|
||||
|
||||
[ClickHouse] SHALL support the following example syntax to create an entry for an [LDAP] server inside the `config.xml`
|
||||
configuration file or of any configuration file inside the `config.d` directory.
|
||||
|
||||
@ -406,6 +448,7 @@ configuration file or of any configuration file inside the `config.d` directory.
|
||||
<port>636</port>
|
||||
<auth_dn_prefix>cn=</auth_dn_prefix>
|
||||
<auth_dn_suffix>, ou=users, dc=example, dc=com</auth_dn_suffix>
|
||||
<verification_cooldown>0</verification_cooldown>
|
||||
<enable_tls>yes</enable_tls>
|
||||
<tls_minimum_protocol_version>tls1.2</tls_minimum_protocol_version>
|
||||
<tls_require_cert>demand</tls_require_cert>
|
||||
@ -521,6 +564,33 @@ version: 1.0
|
||||
[ClickHouse] SHALL support [UTF-8] characters in passwords
|
||||
used to authenticate users using an [LDAP] server.
|
||||
|
||||
#### RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Performance
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL provide better login performance of [LDAP] authenticated users
|
||||
when `verification_cooldown` parameter is set to a positive value when comparing
|
||||
to the the case when `verification_cooldown` is turned off either for a single user or multiple users
|
||||
making a large number of repeated requests.
|
||||
|
||||
#### RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Reset.ChangeInCoreServerParameters
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL reset any currently cached [LDAP] authentication bind requests enabled by the
|
||||
`verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
if either `host`, `port`, `auth_dn_prefix`, or `auth_dn_suffix` parameter values
|
||||
change in the configuration file. The reset SHALL cause any subsequent authentication attempts for any user
|
||||
to result in contacting the [LDAP] server to verify user's username and password.
|
||||
|
||||
#### RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Reset.InvalidPassword
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL reset current cached [LDAP] authentication bind request enabled by the
|
||||
`verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
for the user if the password provided in the current authentication attempt does not match
|
||||
the valid password provided during the first successful authentication request that was cached
|
||||
for this exact user. The reset SHALL cause the next authentication attempt for this user
|
||||
to result in contacting the [LDAP] server to verify user's username and password.
|
||||
|
||||
## References
|
||||
|
||||
* **ClickHouse:** https://clickhouse.tech
|
||||
|
@ -790,9 +790,74 @@ RQ_SRS_007_LDAP_Configuration_Server_TLSCipherSuite = Requirement(
|
||||
level=3,
|
||||
num='4.2.27')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_VerificationCooldown = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `verification_cooldown` parameter in the [LDAP] server configuration section\n'
|
||||
'that SHALL define a period of time, in seconds, after a successful bind attempt, during which a user SHALL be assumed\n'
|
||||
'to be successfully authenticated for all consecutive requests without contacting the [LDAP] server.\n'
|
||||
'After period of time since the last successful attempt expires then on the authentication attempt\n'
|
||||
'SHALL result in contacting the [LDAP] server to verify the username and password. \n'
|
||||
'\n'
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.28')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_VerificationCooldown_Default = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown.Default',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] `verification_cooldown` parameter in the [LDAP] server configuration section\n'
|
||||
'SHALL have a default value of `0` that disables caching and forces contacting\n'
|
||||
'the [LDAP] server for each authentication request.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.29')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_VerificationCooldown_Invalid = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown.Invalid',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[Clickhouse] SHALL return an error if the value provided for the `verification_cooldown` parameter is not a valid positive integer.\n'
|
||||
'\n'
|
||||
'For example:\n'
|
||||
'\n'
|
||||
'* negative integer\n'
|
||||
'* string\n'
|
||||
'* empty value\n'
|
||||
'* extremely large positive value (overflow)\n'
|
||||
'* extremely large negative value (overflow)\n'
|
||||
'\n'
|
||||
'The error SHALL appear in the log and SHALL be similar to the following:\n'
|
||||
'\n'
|
||||
'```bash\n'
|
||||
'<Error> Access(user directories): Could not parse LDAP server `openldap1`: Poco::Exception. Code: 1000, e.code() = 0, e.displayText() = Syntax error: Not a valid unsigned integer: *input value*\n'
|
||||
'```\n'
|
||||
'\n'
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.30')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Syntax = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.Syntax',
|
||||
version='1.0',
|
||||
version='2.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
@ -808,6 +873,7 @@ RQ_SRS_007_LDAP_Configuration_Server_Syntax = Requirement(
|
||||
' <port>636</port>\n'
|
||||
' <auth_dn_prefix>cn=</auth_dn_prefix>\n'
|
||||
' <auth_dn_suffix>, ou=users, dc=example, dc=com</auth_dn_suffix>\n'
|
||||
' <verification_cooldown>0</verification_cooldown>\n'
|
||||
' <enable_tls>yes</enable_tls>\n'
|
||||
' <tls_minimum_protocol_version>tls1.2</tls_minimum_protocol_version>\n'
|
||||
' <tls_require_cert>demand</tls_require_cert>\n'
|
||||
@ -823,7 +889,7 @@ RQ_SRS_007_LDAP_Configuration_Server_Syntax = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.28')
|
||||
num='4.2.31')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_RBAC = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.RBAC',
|
||||
@ -843,7 +909,7 @@ RQ_SRS_007_LDAP_Configuration_User_RBAC = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.29')
|
||||
num='4.2.32')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_Syntax = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.Syntax',
|
||||
@ -871,7 +937,7 @@ RQ_SRS_007_LDAP_Configuration_User_Syntax = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.30')
|
||||
num='4.2.33')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_Name_Empty = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.Name.Empty',
|
||||
@ -886,7 +952,7 @@ RQ_SRS_007_LDAP_Configuration_User_Name_Empty = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.31')
|
||||
num='4.2.34')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_BothPasswordAndLDAP = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.BothPasswordAndLDAP',
|
||||
@ -902,7 +968,7 @@ RQ_SRS_007_LDAP_Configuration_User_BothPasswordAndLDAP = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.32')
|
||||
num='4.2.35')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_LDAP_InvalidServerName_NotDefined = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.NotDefined',
|
||||
@ -919,7 +985,7 @@ RQ_SRS_007_LDAP_Configuration_User_LDAP_InvalidServerName_NotDefined = Requireme
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.33')
|
||||
num='4.2.36')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_LDAP_InvalidServerName_Empty = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.Empty',
|
||||
@ -936,7 +1002,7 @@ RQ_SRS_007_LDAP_Configuration_User_LDAP_InvalidServerName_Empty = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.34')
|
||||
num='4.2.37')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_OnlyOneServer = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.OnlyOneServer',
|
||||
@ -951,7 +1017,7 @@ RQ_SRS_007_LDAP_Configuration_User_OnlyOneServer = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.35')
|
||||
num='4.2.38')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_Name_Long = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.Name.Long',
|
||||
@ -967,7 +1033,7 @@ RQ_SRS_007_LDAP_Configuration_User_Name_Long = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.36')
|
||||
num='4.2.39')
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_Name_UTF8 = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.Name.UTF8',
|
||||
@ -982,7 +1048,7 @@ RQ_SRS_007_LDAP_Configuration_User_Name_UTF8 = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.37')
|
||||
num='4.2.40')
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Username_Empty = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Username.Empty',
|
||||
@ -997,7 +1063,7 @@ RQ_SRS_007_LDAP_Authentication_Username_Empty = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.38')
|
||||
num='4.2.41')
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Username_Long = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Username.Long',
|
||||
@ -1012,7 +1078,7 @@ RQ_SRS_007_LDAP_Authentication_Username_Long = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.39')
|
||||
num='4.2.42')
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Username_UTF8 = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Username.UTF8',
|
||||
@ -1027,7 +1093,7 @@ RQ_SRS_007_LDAP_Authentication_Username_UTF8 = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.40')
|
||||
num='4.2.43')
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Password_Empty = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Password.Empty',
|
||||
@ -1044,7 +1110,7 @@ RQ_SRS_007_LDAP_Authentication_Password_Empty = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.41')
|
||||
num='4.2.44')
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Password_Long = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Password.Long',
|
||||
@ -1060,7 +1126,7 @@ RQ_SRS_007_LDAP_Authentication_Password_Long = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.42')
|
||||
num='4.2.45')
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Password_UTF8 = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Password.UTF8',
|
||||
@ -1076,7 +1142,64 @@ RQ_SRS_007_LDAP_Authentication_Password_UTF8 = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.43')
|
||||
num='4.2.46')
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Performance = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Performance',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL provide better login performance of [LDAP] authenticated users\n'
|
||||
'when `verification_cooldown` parameter is set to a positive value when comparing\n'
|
||||
'to the the case when `verification_cooldown` is turned off either for a single user or multiple users\n'
|
||||
'making a large number of repeated requests.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.47')
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Reset_ChangeInCoreServerParameters = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Reset.ChangeInCoreServerParameters',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL reset any currently cached [LDAP] authentication bind requests enabled by the\n'
|
||||
'`verification_cooldown` parameter in the [LDAP] server configuration section\n'
|
||||
'if either `host`, `port`, `auth_dn_prefix`, or `auth_dn_suffix` parameter values\n'
|
||||
'change in the configuration file. The reset SHALL cause any subsequent authentication attempts for any user\n'
|
||||
"to result in contacting the [LDAP] server to verify user's username and password.\n"
|
||||
'\n'
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.48')
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Reset_InvalidPassword = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Reset.InvalidPassword',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL reset current cached [LDAP] authentication bind request enabled by the\n'
|
||||
'`verification_cooldown` parameter in the [LDAP] server configuration section\n'
|
||||
'for the user if the password provided in the current authentication attempt does not match\n'
|
||||
'the valid password provided during the first successful authentication request that was cached\n'
|
||||
'for this exact user. The reset SHALL cause the next authentication attempt for this user\n'
|
||||
"to result in contacting the [LDAP] server to verify user's username and password.\n"
|
||||
'\n'
|
||||
),
|
||||
link=None,
|
||||
level=3,
|
||||
num='4.2.49')
|
||||
|
||||
SRS_007_ClickHouse_Authentication_of_Users_via_LDAP = Specification(
|
||||
name='SRS-007 ClickHouse Authentication of Users via LDAP',
|
||||
@ -1150,22 +1273,28 @@ SRS_007_ClickHouse_Authentication_of_Users_via_LDAP = Specification(
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.Server.TLSCACertDir', level=3, num='4.2.25'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.Server.TLSCACertFile', level=3, num='4.2.26'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.Server.TLSCipherSuite', level=3, num='4.2.27'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.Server.Syntax', level=3, num='4.2.28'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.RBAC', level=3, num='4.2.29'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.Syntax', level=3, num='4.2.30'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.Name.Empty', level=3, num='4.2.31'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.BothPasswordAndLDAP', level=3, num='4.2.32'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.NotDefined', level=3, num='4.2.33'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.Empty', level=3, num='4.2.34'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.OnlyOneServer', level=3, num='4.2.35'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.Name.Long', level=3, num='4.2.36'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.Name.UTF8', level=3, num='4.2.37'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.Username.Empty', level=3, num='4.2.38'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.Username.Long', level=3, num='4.2.39'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.Username.UTF8', level=3, num='4.2.40'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.Password.Empty', level=3, num='4.2.41'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.Password.Long', level=3, num='4.2.42'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.Password.UTF8', level=3, num='4.2.43'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown', level=3, num='4.2.28'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown.Default', level=3, num='4.2.29'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown.Invalid', level=3, num='4.2.30'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.Server.Syntax', level=3, num='4.2.31'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.RBAC', level=3, num='4.2.32'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.Syntax', level=3, num='4.2.33'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.Name.Empty', level=3, num='4.2.34'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.BothPasswordAndLDAP', level=3, num='4.2.35'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.NotDefined', level=3, num='4.2.36'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.Empty', level=3, num='4.2.37'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.OnlyOneServer', level=3, num='4.2.38'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.Name.Long', level=3, num='4.2.39'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Configuration.User.Name.UTF8', level=3, num='4.2.40'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.Username.Empty', level=3, num='4.2.41'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.Username.Long', level=3, num='4.2.42'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.Username.UTF8', level=3, num='4.2.43'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.Password.Empty', level=3, num='4.2.44'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.Password.Long', level=3, num='4.2.45'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.Password.UTF8', level=3, num='4.2.46'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Performance', level=3, num='4.2.47'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Reset.ChangeInCoreServerParameters', level=3, num='4.2.48'),
|
||||
Heading(name='RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Reset.InvalidPassword', level=3, num='4.2.49'),
|
||||
Heading(name='References', level=1, num='5'),
|
||||
),
|
||||
requirements=(
|
||||
@ -1218,6 +1347,9 @@ SRS_007_ClickHouse_Authentication_of_Users_via_LDAP = Specification(
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSCACertDir,
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSCACertFile,
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSCipherSuite,
|
||||
RQ_SRS_007_LDAP_Configuration_Server_VerificationCooldown,
|
||||
RQ_SRS_007_LDAP_Configuration_Server_VerificationCooldown_Default,
|
||||
RQ_SRS_007_LDAP_Configuration_Server_VerificationCooldown_Invalid,
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Syntax,
|
||||
RQ_SRS_007_LDAP_Configuration_User_RBAC,
|
||||
RQ_SRS_007_LDAP_Configuration_User_Syntax,
|
||||
@ -1234,9 +1366,13 @@ SRS_007_ClickHouse_Authentication_of_Users_via_LDAP = Specification(
|
||||
RQ_SRS_007_LDAP_Authentication_Password_Empty,
|
||||
RQ_SRS_007_LDAP_Authentication_Password_Long,
|
||||
RQ_SRS_007_LDAP_Authentication_Password_UTF8,
|
||||
RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Performance,
|
||||
RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Reset_ChangeInCoreServerParameters,
|
||||
RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Reset_InvalidPassword,
|
||||
),
|
||||
content='''
|
||||
# SRS-007 ClickHouse Authentication of Users via LDAP
|
||||
# Software Requirements Specification
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@ -1295,22 +1431,28 @@ SRS_007_ClickHouse_Authentication_of_Users_via_LDAP = Specification(
|
||||
* 4.2.25 [RQ.SRS-007.LDAP.Configuration.Server.TLSCACertDir](#rqsrs-007ldapconfigurationservertlscacertdir)
|
||||
* 4.2.26 [RQ.SRS-007.LDAP.Configuration.Server.TLSCACertFile](#rqsrs-007ldapconfigurationservertlscacertfile)
|
||||
* 4.2.27 [RQ.SRS-007.LDAP.Configuration.Server.TLSCipherSuite](#rqsrs-007ldapconfigurationservertlsciphersuite)
|
||||
* 4.2.28 [RQ.SRS-007.LDAP.Configuration.Server.Syntax](#rqsrs-007ldapconfigurationserversyntax)
|
||||
* 4.2.29 [RQ.SRS-007.LDAP.Configuration.User.RBAC](#rqsrs-007ldapconfigurationuserrbac)
|
||||
* 4.2.30 [RQ.SRS-007.LDAP.Configuration.User.Syntax](#rqsrs-007ldapconfigurationusersyntax)
|
||||
* 4.2.31 [RQ.SRS-007.LDAP.Configuration.User.Name.Empty](#rqsrs-007ldapconfigurationusernameempty)
|
||||
* 4.2.32 [RQ.SRS-007.LDAP.Configuration.User.BothPasswordAndLDAP](#rqsrs-007ldapconfigurationuserbothpasswordandldap)
|
||||
* 4.2.33 [RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.NotDefined](#rqsrs-007ldapconfigurationuserldapinvalidservernamenotdefined)
|
||||
* 4.2.34 [RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.Empty](#rqsrs-007ldapconfigurationuserldapinvalidservernameempty)
|
||||
* 4.2.35 [RQ.SRS-007.LDAP.Configuration.User.OnlyOneServer](#rqsrs-007ldapconfigurationuseronlyoneserver)
|
||||
* 4.2.36 [RQ.SRS-007.LDAP.Configuration.User.Name.Long](#rqsrs-007ldapconfigurationusernamelong)
|
||||
* 4.2.37 [RQ.SRS-007.LDAP.Configuration.User.Name.UTF8](#rqsrs-007ldapconfigurationusernameutf8)
|
||||
* 4.2.38 [RQ.SRS-007.LDAP.Authentication.Username.Empty](#rqsrs-007ldapauthenticationusernameempty)
|
||||
* 4.2.39 [RQ.SRS-007.LDAP.Authentication.Username.Long](#rqsrs-007ldapauthenticationusernamelong)
|
||||
* 4.2.40 [RQ.SRS-007.LDAP.Authentication.Username.UTF8](#rqsrs-007ldapauthenticationusernameutf8)
|
||||
* 4.2.41 [RQ.SRS-007.LDAP.Authentication.Password.Empty](#rqsrs-007ldapauthenticationpasswordempty)
|
||||
* 4.2.42 [RQ.SRS-007.LDAP.Authentication.Password.Long](#rqsrs-007ldapauthenticationpasswordlong)
|
||||
* 4.2.43 [RQ.SRS-007.LDAP.Authentication.Password.UTF8](#rqsrs-007ldapauthenticationpasswordutf8)
|
||||
* 4.2.28 [RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown](#rqsrs-007ldapconfigurationserververificationcooldown)
|
||||
* 4.2.29 [RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown.Default](#rqsrs-007ldapconfigurationserververificationcooldowndefault)
|
||||
* 4.2.30 [RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown.Invalid](#rqsrs-007ldapconfigurationserververificationcooldowninvalid)
|
||||
* 4.2.31 [RQ.SRS-007.LDAP.Configuration.Server.Syntax](#rqsrs-007ldapconfigurationserversyntax)
|
||||
* 4.2.32 [RQ.SRS-007.LDAP.Configuration.User.RBAC](#rqsrs-007ldapconfigurationuserrbac)
|
||||
* 4.2.33 [RQ.SRS-007.LDAP.Configuration.User.Syntax](#rqsrs-007ldapconfigurationusersyntax)
|
||||
* 4.2.34 [RQ.SRS-007.LDAP.Configuration.User.Name.Empty](#rqsrs-007ldapconfigurationusernameempty)
|
||||
* 4.2.35 [RQ.SRS-007.LDAP.Configuration.User.BothPasswordAndLDAP](#rqsrs-007ldapconfigurationuserbothpasswordandldap)
|
||||
* 4.2.36 [RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.NotDefined](#rqsrs-007ldapconfigurationuserldapinvalidservernamenotdefined)
|
||||
* 4.2.37 [RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.Empty](#rqsrs-007ldapconfigurationuserldapinvalidservernameempty)
|
||||
* 4.2.38 [RQ.SRS-007.LDAP.Configuration.User.OnlyOneServer](#rqsrs-007ldapconfigurationuseronlyoneserver)
|
||||
* 4.2.39 [RQ.SRS-007.LDAP.Configuration.User.Name.Long](#rqsrs-007ldapconfigurationusernamelong)
|
||||
* 4.2.40 [RQ.SRS-007.LDAP.Configuration.User.Name.UTF8](#rqsrs-007ldapconfigurationusernameutf8)
|
||||
* 4.2.41 [RQ.SRS-007.LDAP.Authentication.Username.Empty](#rqsrs-007ldapauthenticationusernameempty)
|
||||
* 4.2.42 [RQ.SRS-007.LDAP.Authentication.Username.Long](#rqsrs-007ldapauthenticationusernamelong)
|
||||
* 4.2.43 [RQ.SRS-007.LDAP.Authentication.Username.UTF8](#rqsrs-007ldapauthenticationusernameutf8)
|
||||
* 4.2.44 [RQ.SRS-007.LDAP.Authentication.Password.Empty](#rqsrs-007ldapauthenticationpasswordempty)
|
||||
* 4.2.45 [RQ.SRS-007.LDAP.Authentication.Password.Long](#rqsrs-007ldapauthenticationpasswordlong)
|
||||
* 4.2.46 [RQ.SRS-007.LDAP.Authentication.Password.UTF8](#rqsrs-007ldapauthenticationpasswordutf8)
|
||||
* 4.2.47 [RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Performance](#rqsrs-007ldapauthenticationverificationcooldownperformance)
|
||||
* 4.2.48 [RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Reset.ChangeInCoreServerParameters](#rqsrs-007ldapauthenticationverificationcooldownresetchangeincoreserverparameters)
|
||||
* 4.2.49 [RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Reset.InvalidPassword](#rqsrs-007ldapauthenticationverificationcooldownresetinvalidpassword)
|
||||
* 5 [References](#references)
|
||||
|
||||
## Revision History
|
||||
@ -1631,9 +1773,44 @@ For example,
|
||||
The available suites SHALL depend on the [OpenSSL] library version and variant used to build
|
||||
[ClickHouse] and therefore might change.
|
||||
|
||||
#### RQ.SRS-007.LDAP.Configuration.Server.Syntax
|
||||
#### RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL support `verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
that SHALL define a period of time, in seconds, after a successful bind attempt, during which a user SHALL be assumed
|
||||
to be successfully authenticated for all consecutive requests without contacting the [LDAP] server.
|
||||
After period of time since the last successful attempt expires then on the authentication attempt
|
||||
SHALL result in contacting the [LDAP] server to verify the username and password.
|
||||
|
||||
#### RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown.Default
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] `verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
SHALL have a default value of `0` that disables caching and forces contacting
|
||||
the [LDAP] server for each authentication request.
|
||||
|
||||
#### RQ.SRS-007.LDAP.Configuration.Server.VerificationCooldown.Invalid
|
||||
version: 1.0
|
||||
|
||||
[Clickhouse] SHALL return an error if the value provided for the `verification_cooldown` parameter is not a valid positive integer.
|
||||
|
||||
For example:
|
||||
|
||||
* negative integer
|
||||
* string
|
||||
* empty value
|
||||
* extremely large positive value (overflow)
|
||||
* extremely large negative value (overflow)
|
||||
|
||||
The error SHALL appear in the log and SHALL be similar to the following:
|
||||
|
||||
```bash
|
||||
<Error> Access(user directories): Could not parse LDAP server `openldap1`: Poco::Exception. Code: 1000, e.code() = 0, e.displayText() = Syntax error: Not a valid unsigned integer: *input value*
|
||||
```
|
||||
|
||||
#### RQ.SRS-007.LDAP.Configuration.Server.Syntax
|
||||
version: 2.0
|
||||
|
||||
[ClickHouse] SHALL support the following example syntax to create an entry for an [LDAP] server inside the `config.xml`
|
||||
configuration file or of any configuration file inside the `config.d` directory.
|
||||
|
||||
@ -1644,6 +1821,7 @@ configuration file or of any configuration file inside the `config.d` directory.
|
||||
<port>636</port>
|
||||
<auth_dn_prefix>cn=</auth_dn_prefix>
|
||||
<auth_dn_suffix>, ou=users, dc=example, dc=com</auth_dn_suffix>
|
||||
<verification_cooldown>0</verification_cooldown>
|
||||
<enable_tls>yes</enable_tls>
|
||||
<tls_minimum_protocol_version>tls1.2</tls_minimum_protocol_version>
|
||||
<tls_require_cert>demand</tls_require_cert>
|
||||
@ -1759,6 +1937,33 @@ version: 1.0
|
||||
[ClickHouse] SHALL support [UTF-8] characters in passwords
|
||||
used to authenticate users using an [LDAP] server.
|
||||
|
||||
#### RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Performance
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL provide better login performance of [LDAP] authenticated users
|
||||
when `verification_cooldown` parameter is set to a positive value when comparing
|
||||
to the the case when `verification_cooldown` is turned off either for a single user or multiple users
|
||||
making a large number of repeated requests.
|
||||
|
||||
#### RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Reset.ChangeInCoreServerParameters
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL reset any currently cached [LDAP] authentication bind requests enabled by the
|
||||
`verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
if either `host`, `port`, `auth_dn_prefix`, or `auth_dn_suffix` parameter values
|
||||
change in the configuration file. The reset SHALL cause any subsequent authentication attempts for any user
|
||||
to result in contacting the [LDAP] server to verify user's username and password.
|
||||
|
||||
#### RQ.SRS-007.LDAP.Authentication.VerificationCooldown.Reset.InvalidPassword
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL reset current cached [LDAP] authentication bind request enabled by the
|
||||
`verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
for the user if the password provided in the current authentication attempt does not match
|
||||
the valid password provided during the first successful authentication request that was cached
|
||||
for this exact user. The reset SHALL cause the next authentication attempt for this user
|
||||
to result in contacting the [LDAP] server to verify user's username and password.
|
||||
|
||||
## References
|
||||
|
||||
* **ClickHouse:** https://clickhouse.tech
|
||||
|
@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import random
|
||||
import time
|
||||
|
||||
from multiprocessing.dummy import Pool
|
||||
from testflows.core import *
|
||||
@ -27,6 +28,7 @@ servers = {
|
||||
|
||||
@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.
|
||||
"""
|
||||
@ -129,7 +131,7 @@ def login_after_user_is_deleted_from_ldap(self, server, rbac=False):
|
||||
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):
|
||||
restart=True, rbac=rbac):
|
||||
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||||
|
||||
with When("I delete this user from LDAP"):
|
||||
@ -200,7 +202,7 @@ def login_after_user_cn_changed_in_ldap(self, server, rbac=False):
|
||||
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):
|
||||
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"):
|
||||
@ -474,6 +476,470 @@ def empty_username_and_empty_password(self, server=None, rbac=False):
|
||||
"""
|
||||
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(
|
||||
@ -493,5 +959,11 @@ def feature(self, rbac, servers=None, node="clickhouse1"):
|
||||
servers = globals()["servers"]
|
||||
|
||||
with ldap_servers(servers):
|
||||
for scenario in loads(current_module(), Scenario):
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -78,7 +78,7 @@ def restart(node=None, safe=False, timeout=60):
|
||||
f"ConfigReloader: Loaded config '/etc/clickhouse-server/config.xml', performed update on configuration",
|
||||
timeout=timeout)
|
||||
|
||||
def add_config(config, timeout=60, restart=False):
|
||||
def add_config(config, timeout=60, restart=False, modify=False):
|
||||
"""Add dynamic configuration file to ClickHouse.
|
||||
|
||||
:param node: node
|
||||
@ -165,19 +165,20 @@ def add_config(config, timeout=60, restart=False):
|
||||
wait_for_config_to_be_loaded()
|
||||
yield
|
||||
finally:
|
||||
with Finally(f"I remove {config.name}"):
|
||||
with node.cluster.shell(node.name) as bash:
|
||||
bash.expect(bash.prompt)
|
||||
bash.send("tail -n 0 -f /var/log/clickhouse-server/clickhouse-server.log")
|
||||
if not modify:
|
||||
with Finally(f"I remove {config.name}"):
|
||||
with node.cluster.shell(node.name) as bash:
|
||||
bash.expect(bash.prompt)
|
||||
bash.send("tail -n 0 -f /var/log/clickhouse-server/clickhouse-server.log")
|
||||
|
||||
with By("removing the config file", description=config.path):
|
||||
node.command(f"rm -rf {config.path}", exitcode=0)
|
||||
with By("removing the config file", description=config.path):
|
||||
node.command(f"rm -rf {config.path}", exitcode=0)
|
||||
|
||||
with Then(f"{config.preprocessed_name} should be updated", description=f"timeout {timeout}"):
|
||||
check_preprocessed_config_is_updated(after_removal=True)
|
||||
with Then(f"{config.preprocessed_name} should be updated", description=f"timeout {timeout}"):
|
||||
check_preprocessed_config_is_updated(after_removal=True)
|
||||
|
||||
with And("I wait for config to be reloaded"):
|
||||
wait_for_config_to_be_loaded()
|
||||
with And("I wait for config to be reloaded"):
|
||||
wait_for_config_to_be_loaded()
|
||||
|
||||
def create_ldap_servers_config_content(servers, config_d_dir="/etc/clickhouse-server/config.d", config_file="ldap_servers.xml"):
|
||||
"""Create LDAP servers configuration content.
|
||||
@ -201,12 +202,19 @@ def create_ldap_servers_config_content(servers, config_d_dir="/etc/clickhouse-se
|
||||
|
||||
return Config(content, path, name, uid, "config.xml")
|
||||
|
||||
@contextmanager
|
||||
def modify_config(config, restart=False):
|
||||
"""Apply updated configuration file.
|
||||
"""
|
||||
return add_config(config, restart=restart, modify=True)
|
||||
|
||||
@contextmanager
|
||||
def ldap_servers(servers, config_d_dir="/etc/clickhouse-server/config.d", config_file="ldap_servers.xml",
|
||||
timeout=60, restart=False):
|
||||
timeout=60, restart=False, config=None):
|
||||
"""Add LDAP servers configuration.
|
||||
"""
|
||||
config = create_ldap_servers_config_content(servers, config_d_dir, config_file)
|
||||
if config is None:
|
||||
config = create_ldap_servers_config_content(servers, config_d_dir, config_file)
|
||||
return add_config(config, restart=restart)
|
||||
|
||||
def create_ldap_users_config_content(*users, config_d_dir="/etc/clickhouse-server/users.d", config_file="ldap_users.xml"):
|
||||
|
@ -217,9 +217,39 @@ def auth_dn_value(self):
|
||||
|
||||
login(servers, user)
|
||||
|
||||
@TestOutline(Scenario)
|
||||
@Examples("invalid_value", [
|
||||
("-1", Name("negative int")),
|
||||
("foo", Name("string")),
|
||||
("", Name("empty string")),
|
||||
("36893488147419103232", Name("overflow with extremely large int value")),
|
||||
("-36893488147419103232", Name("overflow with extremely large negative int value")),
|
||||
("@#", Name("special characters"))
|
||||
])
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Configuration_Server_VerificationCooldown_Invalid("1.0")
|
||||
)
|
||||
def invalid_verification_cooldown_value(self, invalid_value, timeout=20):
|
||||
"""Check that server returns an error when LDAP server
|
||||
verification cooldown parameter is invalid.
|
||||
"""
|
||||
|
||||
error_message = ("<Error> Access(user directories): Could not parse LDAP server"
|
||||
" \\`openldap1\\`: Poco::Exception. Code: 1000, e.code() = 0,"
|
||||
f" e.displayText() = Syntax error: Not a valid unsigned integer{': ' + invalid_value if invalid_value else invalid_value}")
|
||||
|
||||
with Given("LDAP server configuration that uses a negative integer for the verification_cooldown parameter"):
|
||||
servers = {"openldap1": {"host": "openldap1", "port": "389", "enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||||
"verification_cooldown": f"{invalid_value}"
|
||||
}}
|
||||
|
||||
with When("I try to use this configuration then it should not work"):
|
||||
invalid_server_config(servers, message=error_message, tail=17, timeout=timeout)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Syntax("1.0")
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Syntax("2.0")
|
||||
)
|
||||
def syntax(self):
|
||||
"""Check that server configuration with valid syntax can be loaded.
|
||||
@ -230,6 +260,7 @@ def syntax(self):
|
||||
<port>636</port>
|
||||
<auth_dn_prefix>cn=</auth_dn_prefix>
|
||||
<auth_dn_suffix>, ou=users, dc=example, dc=com</auth_dn_suffix>
|
||||
<verification_cooldown>0</verification_cooldown>
|
||||
<enable_tls>yes</enable_tls>
|
||||
<tls_minimum_protocol_version>tls1.2</tls_minimum_protocol_version>
|
||||
<tls_require_cert>demand</tls_require_cert>
|
||||
@ -248,6 +279,7 @@ def syntax(self):
|
||||
"port": "389",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||||
"verification_cooldown": "0",
|
||||
"enable_tls": "yes",
|
||||
"tls_minimum_protocol_version": "tls1.2" ,
|
||||
"tls_require_cert": "demand",
|
||||
|
@ -80,20 +80,23 @@
|
||||
* 4.2.3.26 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.TLSCACertDir](#rqsrs-009ldapexternaluserdirectoryconfigurationservertlscacertdir)
|
||||
* 4.2.3.27 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.TLSCACertFile](#rqsrs-009ldapexternaluserdirectoryconfigurationservertlscacertfile)
|
||||
* 4.2.3.28 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.TLSCipherSuite](#rqsrs-009ldapexternaluserdirectoryconfigurationservertlsciphersuite)
|
||||
* 4.2.3.29 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.Syntax](#rqsrs-009ldapexternaluserdirectoryconfigurationserversyntax)
|
||||
* 4.2.3.30 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory](#rqsrs-009ldapexternaluserdirectoryconfigurationusersldapuserdirectory)
|
||||
* 4.2.3.31 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory.MoreThanOne](#rqsrs-009ldapexternaluserdirectoryconfigurationusersldapuserdirectorymorethanone)
|
||||
* 4.2.3.32 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Syntax](#rqsrs-009ldapexternaluserdirectoryconfigurationuserssyntax)
|
||||
* 4.2.3.33 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersserver)
|
||||
* 4.2.3.34 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Empty](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersserverempty)
|
||||
* 4.2.3.35 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Missing](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersservermissing)
|
||||
* 4.2.3.36 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.MoreThanOne](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersservermorethanone)
|
||||
* 4.2.3.37 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Invalid](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersserverinvalid)
|
||||
* 4.2.3.38 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersroles)
|
||||
* 4.2.3.39 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.MoreThanOne](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesmorethanone)
|
||||
* 4.2.3.40 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Invalid](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesinvalid)
|
||||
* 4.2.3.41 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Empty](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesempty)
|
||||
* 4.2.3.42 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Missing](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesmissing)
|
||||
* 4.2.3.29 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown](#rqsrs-009ldapexternaluserdirectoryconfigurationserververificationcooldown)
|
||||
* 4.2.3.30 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown.Default](#rqsrs-009ldapexternaluserdirectoryconfigurationserververificationcooldowndefault)
|
||||
* 4.2.3.31 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown.Invalid](#rqsrs-009ldapexternaluserdirectoryconfigurationserververificationcooldowninvalid)
|
||||
* 4.2.3.32 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.Syntax](#rqsrs-009ldapexternaluserdirectoryconfigurationserversyntax)
|
||||
* 4.2.3.33 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory](#rqsrs-009ldapexternaluserdirectoryconfigurationusersldapuserdirectory)
|
||||
* 4.2.3.34 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory.MoreThanOne](#rqsrs-009ldapexternaluserdirectoryconfigurationusersldapuserdirectorymorethanone)
|
||||
* 4.2.3.35 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Syntax](#rqsrs-009ldapexternaluserdirectoryconfigurationuserssyntax)
|
||||
* 4.2.3.36 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersserver)
|
||||
* 4.2.3.37 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Empty](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersserverempty)
|
||||
* 4.2.3.38 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Missing](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersservermissing)
|
||||
* 4.2.3.39 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.MoreThanOne](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersservermorethanone)
|
||||
* 4.2.3.40 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Invalid](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersserverinvalid)
|
||||
* 4.2.3.41 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersroles)
|
||||
* 4.2.3.42 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.MoreThanOne](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesmorethanone)
|
||||
* 4.2.3.43 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Invalid](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesinvalid)
|
||||
* 4.2.3.44 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Empty](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesempty)
|
||||
* 4.2.3.45 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Missing](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesmissing)
|
||||
* 4.2.4 [Authentication](#authentication)
|
||||
* 4.2.4.1 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Username.Empty](#rqsrs-009ldapexternaluserdirectoryauthenticationusernameempty)
|
||||
* 4.2.4.2 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Username.Long](#rqsrs-009ldapexternaluserdirectoryauthenticationusernamelong)
|
||||
@ -101,6 +104,9 @@
|
||||
* 4.2.4.4 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Password.Empty](#rqsrs-009ldapexternaluserdirectoryauthenticationpasswordempty)
|
||||
* 4.2.4.5 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Password.Long](#rqsrs-009ldapexternaluserdirectoryauthenticationpasswordlong)
|
||||
* 4.2.4.6 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Password.UTF8](#rqsrs-009ldapexternaluserdirectoryauthenticationpasswordutf8)
|
||||
* 4.2.4.7 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Performance](#rqsrs-009ldapexternaluserdirectoryauthenticationverificationcooldownperformance)
|
||||
* 4.2.4.8 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Reset.ChangeInCoreServerParameters](#rqsrs-009ldapexternaluserdirectoryauthenticationverificationcooldownresetchangeincoreserverparameters)
|
||||
* 4.2.4.9 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Reset.InvalidPassword](#rqsrs-009ldapexternaluserdirectoryauthenticationverificationcooldownresetinvalidpassword)
|
||||
* 5 [References](#references)
|
||||
|
||||
## Revision History
|
||||
@ -556,9 +562,44 @@ For example,
|
||||
The available suites SHALL depend on the [OpenSSL] library version and variant used to build
|
||||
[ClickHouse] and therefore might change.
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.Syntax
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL support `verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
that SHALL define a period of time, in seconds, after a successful bind attempt, during which a user SHALL be assumed
|
||||
to be successfully authenticated for all consecutive requests without contacting the [LDAP] server.
|
||||
After period of time since the last successful attempt expires then on the authentication attempt
|
||||
SHALL result in contacting the [LDAP] server to verify the username and password.
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown.Default
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] `verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
SHALL have a default value of `0` that disables caching and forces contacting
|
||||
the [LDAP] server for each authentication request.
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown.Invalid
|
||||
version: 1.0
|
||||
|
||||
[Clickhouse] SHALL return an error if the value provided for the `verification_cooldown` parameter is not a valid positive integer.
|
||||
|
||||
For example:
|
||||
|
||||
* negative integer
|
||||
* string
|
||||
* empty value
|
||||
* extremely large positive value (overflow)
|
||||
* extremely large negative value (overflow)
|
||||
|
||||
The error SHALL appear in the log and SHALL be similar to the following:
|
||||
|
||||
```bash
|
||||
<Error> Access(user directories): Could not parse LDAP server `openldap1`: Poco::Exception. Code: 1000, e.code() = 0, e.displayText() = Syntax error: Not a valid unsigned integer: *input value*
|
||||
```
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.Syntax
|
||||
version: 2.0
|
||||
|
||||
[ClickHouse] SHALL support the following example syntax to create an entry for an [LDAP] server inside the `config.xml`
|
||||
configuration file or of any configuration file inside the `config.d` directory.
|
||||
|
||||
@ -569,6 +610,7 @@ configuration file or of any configuration file inside the `config.d` directory.
|
||||
<port>636</port>
|
||||
<auth_dn_prefix>cn=</auth_dn_prefix>
|
||||
<auth_dn_suffix>, ou=users, dc=example, dc=com</auth_dn_suffix>
|
||||
<verification_cooldown>0</verification_cooldown>
|
||||
<enable_tls>yes</enable_tls>
|
||||
<tls_minimum_protocol_version>tls1.2</tls_minimum_protocol_version>
|
||||
<tls_require_cert>demand</tls_require_cert>
|
||||
@ -717,6 +759,33 @@ version: 1.0
|
||||
[ClickHouse] SHALL support [UTF-8] characters in passwords
|
||||
used to authenticate users when using [LDAP] external user directory.
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Performance
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL provide better login performance of users authenticated using [LDAP] external user directory
|
||||
when `verification_cooldown` parameter is set to a positive value when comparing
|
||||
to the the case when `verification_cooldown` is turned off either for a single user or multiple users
|
||||
making a large number of repeated requests.
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Reset.ChangeInCoreServerParameters
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL reset any currently cached [LDAP] authentication bind requests enabled by the
|
||||
`verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
if either `host`, `port`, `auth_dn_prefix`, or `auth_dn_suffix` parameter values
|
||||
change in the configuration file. The reset SHALL cause any subsequent authentication attempts for any user
|
||||
to result in contacting the [LDAP] server to verify user's username and password.
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Reset.InvalidPassword
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL reset current cached [LDAP] authentication bind request enabled by the
|
||||
`verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
for the user if the password provided in the current authentication attempt does not match
|
||||
the valid password provided during the first successful authentication request that was cached
|
||||
for this exact user. The reset SHALL cause the next authentication attempt for this user
|
||||
to result in contacting the [LDAP] server to verify user's username and password.
|
||||
|
||||
## References
|
||||
|
||||
* **Access Control and Account Management**: https://clickhouse.tech/docs/en/operations/access-rights/
|
||||
|
@ -1073,9 +1073,74 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_TLSCipherSuite = Requ
|
||||
level=4,
|
||||
num='4.2.3.28')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_VerificationCooldown = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `verification_cooldown` parameter in the [LDAP] server configuration section\n'
|
||||
'that SHALL define a period of time, in seconds, after a successful bind attempt, during which a user SHALL be assumed\n'
|
||||
'to be successfully authenticated for all consecutive requests without contacting the [LDAP] server.\n'
|
||||
'After period of time since the last successful attempt expires then on the authentication attempt\n'
|
||||
'SHALL result in contacting the [LDAP] server to verify the username and password.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.29')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_VerificationCooldown_Default = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown.Default',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] `verification_cooldown` parameter in the [LDAP] server configuration section\n'
|
||||
'SHALL have a default value of `0` that disables caching and forces contacting\n'
|
||||
'the [LDAP] server for each authentication request.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.30')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_VerificationCooldown_Invalid = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown.Invalid',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[Clickhouse] SHALL return an error if the value provided for the `verification_cooldown` parameter is not a valid positive integer.\n'
|
||||
'\n'
|
||||
'For example:\n'
|
||||
'\n'
|
||||
'* negative integer\n'
|
||||
'* string\n'
|
||||
'* empty value\n'
|
||||
'* extremely large positive value (overflow)\n'
|
||||
'* extremely large negative value (overflow)\n'
|
||||
'\n'
|
||||
'The error SHALL appear in the log and SHALL be similar to the following:\n'
|
||||
'\n'
|
||||
'```bash\n'
|
||||
'<Error> Access(user directories): Could not parse LDAP server `openldap1`: Poco::Exception. Code: 1000, e.code() = 0, e.displayText() = Syntax error: Not a valid unsigned integer: *input value*\n'
|
||||
'```\n'
|
||||
'\n'
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.31')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_Syntax = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.Syntax',
|
||||
version='1.0',
|
||||
version='2.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
@ -1091,6 +1156,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_Syntax = Requirement(
|
||||
' <port>636</port>\n'
|
||||
' <auth_dn_prefix>cn=</auth_dn_prefix>\n'
|
||||
' <auth_dn_suffix>, ou=users, dc=example, dc=com</auth_dn_suffix>\n'
|
||||
' <verification_cooldown>0</verification_cooldown>\n'
|
||||
' <enable_tls>yes</enable_tls>\n'
|
||||
' <tls_minimum_protocol_version>tls1.2</tls_minimum_protocol_version>\n'
|
||||
' <tls_require_cert>demand</tls_require_cert>\n'
|
||||
@ -1106,7 +1172,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_Syntax = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.29')
|
||||
num='4.2.3.32')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_LDAPUserDirectory = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory',
|
||||
@ -1122,7 +1188,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_LDAPUserDirectory = Re
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.30')
|
||||
num='4.2.3.33')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_LDAPUserDirectory_MoreThanOne = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory.MoreThanOne',
|
||||
@ -1139,7 +1205,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_LDAPUserDirectory_More
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.31')
|
||||
num='4.2.3.34')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Syntax = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Syntax',
|
||||
@ -1168,7 +1234,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Syntax = Requirement(
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.32')
|
||||
num='4.2.3.35')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Server = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server',
|
||||
@ -1185,7 +1251,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Server = Re
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.33')
|
||||
num='4.2.3.36')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Server_Empty = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Empty',
|
||||
@ -1201,7 +1267,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Server_Empt
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.34')
|
||||
num='4.2.3.37')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Server_Missing = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Missing',
|
||||
@ -1217,7 +1283,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Server_Miss
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.35')
|
||||
num='4.2.3.38')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Server_MoreThanOne = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.MoreThanOne',
|
||||
@ -1233,7 +1299,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Server_More
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.36')
|
||||
num='4.2.3.39')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Server_Invalid = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Invalid',
|
||||
@ -1249,7 +1315,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Server_Inva
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.37')
|
||||
num='4.2.3.40')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Roles = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles',
|
||||
@ -1266,7 +1332,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Roles = Req
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.38')
|
||||
num='4.2.3.41')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Roles_MoreThanOne = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.MoreThanOne',
|
||||
@ -1283,7 +1349,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Roles_MoreT
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.39')
|
||||
num='4.2.3.42')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Roles_Invalid = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Invalid',
|
||||
@ -1299,7 +1365,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Roles_Inval
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.40')
|
||||
num='4.2.3.43')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Roles_Empty = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Empty',
|
||||
@ -1316,7 +1382,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Roles_Empty
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.41')
|
||||
num='4.2.3.44')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Roles_Missing = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Missing',
|
||||
@ -1333,7 +1399,7 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_Parameters_Roles_Missi
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.3.42')
|
||||
num='4.2.3.45')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Username_Empty = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Username.Empty',
|
||||
@ -1432,6 +1498,63 @@ RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_UTF8 = Requirement
|
||||
level=4,
|
||||
num='4.2.4.6')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_VerificationCooldown_Performance = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Performance',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL provide better login performance of users authenticated using [LDAP] external user directory\n'
|
||||
'when `verification_cooldown` parameter is set to a positive value when comparing\n'
|
||||
'to the the case when `verification_cooldown` is turned off either for a single user or multiple users\n'
|
||||
'making a large number of repeated requests.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.4.7')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_VerificationCooldown_Reset_ChangeInCoreServerParameters = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Reset.ChangeInCoreServerParameters',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL reset any currently cached [LDAP] authentication bind requests enabled by the\n'
|
||||
'`verification_cooldown` parameter in the [LDAP] server configuration section\n'
|
||||
'if either `host`, `port`, `auth_dn_prefix`, or `auth_dn_suffix` parameter values\n'
|
||||
'change in the configuration file. The reset SHALL cause any subsequent authentication attempts for any user\n'
|
||||
"to result in contacting the [LDAP] server to verify user's username and password.\n"
|
||||
'\n'
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.4.8')
|
||||
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_VerificationCooldown_Reset_InvalidPassword = Requirement(
|
||||
name='RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Reset.InvalidPassword',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL reset current cached [LDAP] authentication bind request enabled by the\n'
|
||||
'`verification_cooldown` parameter in the [LDAP] server configuration section\n'
|
||||
'for the user if the password provided in the current authentication attempt does not match\n'
|
||||
'the valid password provided during the first successful authentication request that was cached\n'
|
||||
'for this exact user. The reset SHALL cause the next authentication attempt for this user\n'
|
||||
"to result in contacting the [LDAP] server to verify user's username and password.\n"
|
||||
'\n'
|
||||
),
|
||||
link=None,
|
||||
level=4,
|
||||
num='4.2.4.9')
|
||||
|
||||
SRS_009_ClickHouse_LDAP_External_User_Directory = Specification(
|
||||
name='SRS-009 ClickHouse LDAP External User Directory',
|
||||
description=None,
|
||||
@ -1526,20 +1649,23 @@ SRS_009_ClickHouse_LDAP_External_User_Directory = Specification(
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.TLSCACertDir', level=4, num='4.2.3.26'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.TLSCACertFile', level=4, num='4.2.3.27'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.TLSCipherSuite', level=4, num='4.2.3.28'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.Syntax', level=4, num='4.2.3.29'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory', level=4, num='4.2.3.30'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory.MoreThanOne', level=4, num='4.2.3.31'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Syntax', level=4, num='4.2.3.32'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server', level=4, num='4.2.3.33'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Empty', level=4, num='4.2.3.34'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Missing', level=4, num='4.2.3.35'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.MoreThanOne', level=4, num='4.2.3.36'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Invalid', level=4, num='4.2.3.37'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles', level=4, num='4.2.3.38'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.MoreThanOne', level=4, num='4.2.3.39'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Invalid', level=4, num='4.2.3.40'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Empty', level=4, num='4.2.3.41'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Missing', level=4, num='4.2.3.42'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown', level=4, num='4.2.3.29'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown.Default', level=4, num='4.2.3.30'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown.Invalid', level=4, num='4.2.3.31'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.Syntax', level=4, num='4.2.3.32'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory', level=4, num='4.2.3.33'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory.MoreThanOne', level=4, num='4.2.3.34'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Syntax', level=4, num='4.2.3.35'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server', level=4, num='4.2.3.36'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Empty', level=4, num='4.2.3.37'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Missing', level=4, num='4.2.3.38'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.MoreThanOne', level=4, num='4.2.3.39'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Invalid', level=4, num='4.2.3.40'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles', level=4, num='4.2.3.41'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.MoreThanOne', level=4, num='4.2.3.42'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Invalid', level=4, num='4.2.3.43'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Empty', level=4, num='4.2.3.44'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Missing', level=4, num='4.2.3.45'),
|
||||
Heading(name='Authentication', level=3, num='4.2.4'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Username.Empty', level=4, num='4.2.4.1'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Username.Long', level=4, num='4.2.4.2'),
|
||||
@ -1547,6 +1673,9 @@ SRS_009_ClickHouse_LDAP_External_User_Directory = Specification(
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Password.Empty', level=4, num='4.2.4.4'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Password.Long', level=4, num='4.2.4.5'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Password.UTF8', level=4, num='4.2.4.6'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Performance', level=4, num='4.2.4.7'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Reset.ChangeInCoreServerParameters', level=4, num='4.2.4.8'),
|
||||
Heading(name='RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Reset.InvalidPassword', level=4, num='4.2.4.9'),
|
||||
Heading(name='References', level=1, num='5'),
|
||||
),
|
||||
requirements=(
|
||||
@ -1615,6 +1744,9 @@ SRS_009_ClickHouse_LDAP_External_User_Directory = Specification(
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_TLSCACertDir,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_TLSCACertFile,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_TLSCipherSuite,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_VerificationCooldown,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_VerificationCooldown_Default,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_VerificationCooldown_Invalid,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_Syntax,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_LDAPUserDirectory,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Users_LDAPUserDirectory_MoreThanOne,
|
||||
@ -1635,6 +1767,9 @@ SRS_009_ClickHouse_LDAP_External_User_Directory = Specification(
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_Empty,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_Long,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_Password_UTF8,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_VerificationCooldown_Performance,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_VerificationCooldown_Reset_ChangeInCoreServerParameters,
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication_VerificationCooldown_Reset_InvalidPassword,
|
||||
),
|
||||
content='''
|
||||
# SRS-009 ClickHouse LDAP External User Directory
|
||||
@ -1719,20 +1854,23 @@ SRS_009_ClickHouse_LDAP_External_User_Directory = Specification(
|
||||
* 4.2.3.26 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.TLSCACertDir](#rqsrs-009ldapexternaluserdirectoryconfigurationservertlscacertdir)
|
||||
* 4.2.3.27 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.TLSCACertFile](#rqsrs-009ldapexternaluserdirectoryconfigurationservertlscacertfile)
|
||||
* 4.2.3.28 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.TLSCipherSuite](#rqsrs-009ldapexternaluserdirectoryconfigurationservertlsciphersuite)
|
||||
* 4.2.3.29 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.Syntax](#rqsrs-009ldapexternaluserdirectoryconfigurationserversyntax)
|
||||
* 4.2.3.30 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory](#rqsrs-009ldapexternaluserdirectoryconfigurationusersldapuserdirectory)
|
||||
* 4.2.3.31 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory.MoreThanOne](#rqsrs-009ldapexternaluserdirectoryconfigurationusersldapuserdirectorymorethanone)
|
||||
* 4.2.3.32 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Syntax](#rqsrs-009ldapexternaluserdirectoryconfigurationuserssyntax)
|
||||
* 4.2.3.33 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersserver)
|
||||
* 4.2.3.34 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Empty](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersserverempty)
|
||||
* 4.2.3.35 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Missing](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersservermissing)
|
||||
* 4.2.3.36 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.MoreThanOne](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersservermorethanone)
|
||||
* 4.2.3.37 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Invalid](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersserverinvalid)
|
||||
* 4.2.3.38 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersroles)
|
||||
* 4.2.3.39 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.MoreThanOne](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesmorethanone)
|
||||
* 4.2.3.40 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Invalid](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesinvalid)
|
||||
* 4.2.3.41 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Empty](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesempty)
|
||||
* 4.2.3.42 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Missing](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesmissing)
|
||||
* 4.2.3.29 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown](#rqsrs-009ldapexternaluserdirectoryconfigurationserververificationcooldown)
|
||||
* 4.2.3.30 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown.Default](#rqsrs-009ldapexternaluserdirectoryconfigurationserververificationcooldowndefault)
|
||||
* 4.2.3.31 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown.Invalid](#rqsrs-009ldapexternaluserdirectoryconfigurationserververificationcooldowninvalid)
|
||||
* 4.2.3.32 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.Syntax](#rqsrs-009ldapexternaluserdirectoryconfigurationserversyntax)
|
||||
* 4.2.3.33 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory](#rqsrs-009ldapexternaluserdirectoryconfigurationusersldapuserdirectory)
|
||||
* 4.2.3.34 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory.MoreThanOne](#rqsrs-009ldapexternaluserdirectoryconfigurationusersldapuserdirectorymorethanone)
|
||||
* 4.2.3.35 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Syntax](#rqsrs-009ldapexternaluserdirectoryconfigurationuserssyntax)
|
||||
* 4.2.3.36 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersserver)
|
||||
* 4.2.3.37 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Empty](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersserverempty)
|
||||
* 4.2.3.38 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Missing](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersservermissing)
|
||||
* 4.2.3.39 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.MoreThanOne](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersservermorethanone)
|
||||
* 4.2.3.40 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server.Invalid](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersserverinvalid)
|
||||
* 4.2.3.41 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersroles)
|
||||
* 4.2.3.42 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.MoreThanOne](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesmorethanone)
|
||||
* 4.2.3.43 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Invalid](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesinvalid)
|
||||
* 4.2.3.44 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Empty](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesempty)
|
||||
* 4.2.3.45 [RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Roles.Missing](#rqsrs-009ldapexternaluserdirectoryconfigurationusersparametersrolesmissing)
|
||||
* 4.2.4 [Authentication](#authentication)
|
||||
* 4.2.4.1 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Username.Empty](#rqsrs-009ldapexternaluserdirectoryauthenticationusernameempty)
|
||||
* 4.2.4.2 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Username.Long](#rqsrs-009ldapexternaluserdirectoryauthenticationusernamelong)
|
||||
@ -1740,6 +1878,9 @@ SRS_009_ClickHouse_LDAP_External_User_Directory = Specification(
|
||||
* 4.2.4.4 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Password.Empty](#rqsrs-009ldapexternaluserdirectoryauthenticationpasswordempty)
|
||||
* 4.2.4.5 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Password.Long](#rqsrs-009ldapexternaluserdirectoryauthenticationpasswordlong)
|
||||
* 4.2.4.6 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.Password.UTF8](#rqsrs-009ldapexternaluserdirectoryauthenticationpasswordutf8)
|
||||
* 4.2.4.7 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Performance](#rqsrs-009ldapexternaluserdirectoryauthenticationverificationcooldownperformance)
|
||||
* 4.2.4.8 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Reset.ChangeInCoreServerParameters](#rqsrs-009ldapexternaluserdirectoryauthenticationverificationcooldownresetchangeincoreserverparameters)
|
||||
* 4.2.4.9 [RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Reset.InvalidPassword](#rqsrs-009ldapexternaluserdirectoryauthenticationverificationcooldownresetinvalidpassword)
|
||||
* 5 [References](#references)
|
||||
|
||||
## Revision History
|
||||
@ -2195,9 +2336,44 @@ For example,
|
||||
The available suites SHALL depend on the [OpenSSL] library version and variant used to build
|
||||
[ClickHouse] and therefore might change.
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.Syntax
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL support `verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
that SHALL define a period of time, in seconds, after a successful bind attempt, during which a user SHALL be assumed
|
||||
to be successfully authenticated for all consecutive requests without contacting the [LDAP] server.
|
||||
After period of time since the last successful attempt expires then on the authentication attempt
|
||||
SHALL result in contacting the [LDAP] server to verify the username and password.
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown.Default
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] `verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
SHALL have a default value of `0` that disables caching and forces contacting
|
||||
the [LDAP] server for each authentication request.
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.VerificationCooldown.Invalid
|
||||
version: 1.0
|
||||
|
||||
[Clickhouse] SHALL return an error if the value provided for the `verification_cooldown` parameter is not a valid positive integer.
|
||||
|
||||
For example:
|
||||
|
||||
* negative integer
|
||||
* string
|
||||
* empty value
|
||||
* extremely large positive value (overflow)
|
||||
* extremely large negative value (overflow)
|
||||
|
||||
The error SHALL appear in the log and SHALL be similar to the following:
|
||||
|
||||
```bash
|
||||
<Error> Access(user directories): Could not parse LDAP server `openldap1`: Poco::Exception. Code: 1000, e.code() = 0, e.displayText() = Syntax error: Not a valid unsigned integer: *input value*
|
||||
```
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Server.Syntax
|
||||
version: 2.0
|
||||
|
||||
[ClickHouse] SHALL support the following example syntax to create an entry for an [LDAP] server inside the `config.xml`
|
||||
configuration file or of any configuration file inside the `config.d` directory.
|
||||
|
||||
@ -2208,6 +2384,7 @@ configuration file or of any configuration file inside the `config.d` directory.
|
||||
<port>636</port>
|
||||
<auth_dn_prefix>cn=</auth_dn_prefix>
|
||||
<auth_dn_suffix>, ou=users, dc=example, dc=com</auth_dn_suffix>
|
||||
<verification_cooldown>0</verification_cooldown>
|
||||
<enable_tls>yes</enable_tls>
|
||||
<tls_minimum_protocol_version>tls1.2</tls_minimum_protocol_version>
|
||||
<tls_require_cert>demand</tls_require_cert>
|
||||
@ -2356,6 +2533,33 @@ version: 1.0
|
||||
[ClickHouse] SHALL support [UTF-8] characters in passwords
|
||||
used to authenticate users when using [LDAP] external user directory.
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Performance
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL provide better login performance of users authenticated using [LDAP] external user directory
|
||||
when `verification_cooldown` parameter is set to a positive value when comparing
|
||||
to the the case when `verification_cooldown` is turned off either for a single user or multiple users
|
||||
making a large number of repeated requests.
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Reset.ChangeInCoreServerParameters
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL reset any currently cached [LDAP] authentication bind requests enabled by the
|
||||
`verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
if either `host`, `port`, `auth_dn_prefix`, or `auth_dn_suffix` parameter values
|
||||
change in the configuration file. The reset SHALL cause any subsequent authentication attempts for any user
|
||||
to result in contacting the [LDAP] server to verify user's username and password.
|
||||
|
||||
##### RQ.SRS-009.LDAP.ExternalUserDirectory.Authentication.VerificationCooldown.Reset.InvalidPassword
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL reset current cached [LDAP] authentication bind request enabled by the
|
||||
`verification_cooldown` parameter in the [LDAP] server configuration section
|
||||
for the user if the password provided in the current authentication attempt does not match
|
||||
the valid password provided during the first successful authentication request that was cached
|
||||
for this exact user. The reset SHALL cause the next authentication attempt for this user
|
||||
to result in contacting the [LDAP] server to verify user's username and password.
|
||||
|
||||
## References
|
||||
|
||||
* **Access Control and Account Management**: https://clickhouse.tech/docs/en/operations/access-rights/
|
||||
|
@ -698,6 +698,460 @@ def empty_username_and_empty_password(self, server=None):
|
||||
"""
|
||||
login_and_execute_query(username="", password="")
|
||||
|
||||
@TestScenario
|
||||
@Tags("verification_cooldown")
|
||||
@Requirements(
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_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 rbac_roles("ldap_role") as roles:
|
||||
with ldap_external_user_directory(server=server, roles=roles, restart=True):
|
||||
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_009_LDAP_ExternalUserDirectory_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 rbac_roles("ldap_role") as roles:
|
||||
with ldap_external_user_directory(server=server, roles=roles, restart=True):
|
||||
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_009_LDAP_ExternalUserDirectory_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 rbac_roles("ldap_role") as roles:
|
||||
with ldap_external_user_directory(server=server, roles=roles, restart=True):
|
||||
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_009_LDAP_ExternalUserDirectory_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 rbac_roles("ldap_role") as roles:
|
||||
with ldap_external_user_directory(server=server, roles=roles, restart=True):
|
||||
|
||||
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 rbac_roles("ldap_role") as roles:
|
||||
with ldap_external_user_directory(server=server, roles=roles, restart=True):
|
||||
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_009_LDAP_ExternalUserDirectory_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=None, restart=False, config=config):
|
||||
with rbac_roles("ldap_role") as roles:
|
||||
with ldap_external_user_directory(server=server, roles=roles, restart=True):
|
||||
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_009_LDAP_ExternalUserDirectory_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_009_LDAP_ExternalUserDirectory_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_009_LDAP_ExternalUserDirectory_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_009_LDAP_ExternalUserDirectory_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_009_LDAP_ExternalUserDirectory_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 rbac_roles("ldap_role") as roles:
|
||||
with ldap_external_user_directory(server=server, roles=roles, restart=True):
|
||||
|
||||
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)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Users_Lookup_Priority("2.0")
|
||||
@ -734,7 +1188,6 @@ def user_lookup_priority(self, server):
|
||||
with When("I try to login as 'ldap' user defined only in LDAP it should work"):
|
||||
login_and_execute_query(**users["ldap"])
|
||||
|
||||
|
||||
@TestOutline(Feature)
|
||||
@Name("user authentications")
|
||||
@Requirements(
|
||||
@ -755,8 +1208,11 @@ def feature(self, servers=None, server=None, node="clickhouse1"):
|
||||
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, filter=~has.tag("custom config")):
|
||||
Scenario(test=scenario, flags=TE)(server=server)
|
||||
for scenario in loads(current_module(), Scenario, filter=~has.tag("custom config") & ~has.tag("verification_cooldown")):
|
||||
Scenario(test=scenario)(server=server)
|
||||
|
||||
for scenario in loads(current_module(), Scenario, filter=has.tag("custom config")):
|
||||
Scenario(test=scenario, flags=TE)(server=server)
|
||||
Scenario(test=scenario)(server=server)
|
||||
|
||||
for scenario in loads(current_module(), Scenario, filter=has.tag("verification_cooldown")):
|
||||
Scenario(test=scenario)(server=server)
|
||||
|
@ -5,10 +5,11 @@ from contextlib import contextmanager
|
||||
import testflows.settings as settings
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
from ldap.authentication.tests.common import getuid, Config, ldap_servers, add_config, restart
|
||||
from ldap.authentication.tests.common import getuid, Config, ldap_servers, add_config, modify_config, restart
|
||||
from ldap.authentication.tests.common import xmltree, xml_indent, xml_append, xml_with_utf8
|
||||
from ldap.authentication.tests.common import ldap_user, ldap_users, add_user_to_ldap, delete_user_from_ldap
|
||||
from ldap.authentication.tests.common import change_user_password_in_ldap, change_user_cn_in_ldap
|
||||
from ldap.authentication.tests.common import create_ldap_servers_config_content
|
||||
from ldap.authentication.tests.common import randomword
|
||||
|
||||
def join(tasks, timeout):
|
||||
|
@ -99,7 +99,6 @@ def invalid_port(self):
|
||||
}]
|
||||
login(servers, "openldap1", *users)
|
||||
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_Invalid("1.0"),
|
||||
@ -232,9 +231,39 @@ def auth_dn_value(self):
|
||||
|
||||
login(servers, "openldap1", user)
|
||||
|
||||
@TestOutline(Scenario)
|
||||
@Examples("invalid_value", [
|
||||
("-1", Name("negative int")),
|
||||
("foo", Name("string")),
|
||||
("", Name("empty string")),
|
||||
("36893488147419103232", Name("overflow with extremely large int value")),
|
||||
("-36893488147419103232", Name("overflow with extremely large negative int value")),
|
||||
("@#", Name("special characters"))
|
||||
])
|
||||
@Requirements(
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_VerificationCooldown_Invalid("1.0")
|
||||
)
|
||||
def invalid_verification_cooldown_value(self, invalid_value, timeout=20):
|
||||
"""Check that server returns an error when LDAP server
|
||||
verification cooldown parameter is invalid.
|
||||
"""
|
||||
|
||||
error_message = ("<Error> Access(user directories): Could not parse LDAP server"
|
||||
" \\`openldap1\\`: Poco::Exception. Code: 1000, e.code() = 0,"
|
||||
f" e.displayText() = Syntax error: Not a valid unsigned integer{': ' + invalid_value if invalid_value else invalid_value}")
|
||||
|
||||
with Given("LDAP server configuration that uses a negative integer for the verification_cooldown parameter"):
|
||||
servers = {"openldap1": {"host": "openldap1", "port": "389", "enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||||
"verification_cooldown": f"{invalid_value}"
|
||||
}}
|
||||
|
||||
with When("I try to use this configuration then it should not work"):
|
||||
invalid_server_config(servers, message=error_message, tail=17, timeout=timeout)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_Syntax("1.0")
|
||||
RQ_SRS_009_LDAP_ExternalUserDirectory_Configuration_Server_Syntax("2.0")
|
||||
)
|
||||
def syntax(self):
|
||||
"""Check that server configuration with valid syntax can be loaded.
|
||||
@ -245,6 +274,7 @@ def syntax(self):
|
||||
<port>636</port>
|
||||
<auth_dn_prefix>cn=</auth_dn_prefix>
|
||||
<auth_dn_suffix>, ou=users, dc=example, dc=com</auth_dn_suffix>
|
||||
<verification_cooldown>0</verification_cooldown>
|
||||
<enable_tls>yes</enable_tls>
|
||||
<tls_minimum_protocol_version>tls1.2</tls_minimum_protocol_version>
|
||||
<tls_require_cert>demand</tls_require_cert>
|
||||
@ -263,6 +293,7 @@ def syntax(self):
|
||||
"port": "389",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||||
"verification_cooldown": "0",
|
||||
"enable_tls": "yes",
|
||||
"tls_minimum_protocol_version": "tls1.2" ,
|
||||
"tls_require_cert": "demand",
|
||||
|
Loading…
Reference in New Issue
Block a user