mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
Merge pull request #11234 from traceon/ldap-per-user-authentication
Add LDAP authentication support
This commit is contained in:
commit
000b197ad1
@ -7,7 +7,7 @@
|
||||
#
|
||||
# Sets values of:
|
||||
# OPENLDAP_FOUND - TRUE if found
|
||||
# OPENLDAP_INCLUDE_DIR - path to the include directory
|
||||
# OPENLDAP_INCLUDE_DIRS - paths to the include directories
|
||||
# OPENLDAP_LIBRARIES - paths to the libldap and liblber libraries
|
||||
# OPENLDAP_LDAP_LIBRARY - paths to the libldap library
|
||||
# OPENLDAP_LBER_LIBRARY - paths to the liblber library
|
||||
@ -28,11 +28,11 @@ if(OPENLDAP_USE_REENTRANT_LIBS)
|
||||
endif()
|
||||
|
||||
if(OPENLDAP_ROOT_DIR)
|
||||
find_path(OPENLDAP_INCLUDE_DIR NAMES "ldap.h" "lber.h" PATHS "${OPENLDAP_ROOT_DIR}" PATH_SUFFIXES "include" NO_DEFAULT_PATH)
|
||||
find_path(OPENLDAP_INCLUDE_DIRS NAMES "ldap.h" "lber.h" PATHS "${OPENLDAP_ROOT_DIR}" PATH_SUFFIXES "include" NO_DEFAULT_PATH)
|
||||
find_library(OPENLDAP_LDAP_LIBRARY NAMES "ldap${_r_suffix}" PATHS "${OPENLDAP_ROOT_DIR}" PATH_SUFFIXES "lib" NO_DEFAULT_PATH)
|
||||
find_library(OPENLDAP_LBER_LIBRARY NAMES "lber" PATHS "${OPENLDAP_ROOT_DIR}" PATH_SUFFIXES "lib" NO_DEFAULT_PATH)
|
||||
else()
|
||||
find_path(OPENLDAP_INCLUDE_DIR NAMES "ldap.h" "lber.h")
|
||||
find_path(OPENLDAP_INCLUDE_DIRS NAMES "ldap.h" "lber.h")
|
||||
find_library(OPENLDAP_LDAP_LIBRARY NAMES "ldap${_r_suffix}")
|
||||
find_library(OPENLDAP_LBER_LIBRARY NAMES "lber")
|
||||
endif()
|
||||
@ -44,10 +44,10 @@ set(OPENLDAP_LIBRARIES ${OPENLDAP_LDAP_LIBRARY} ${OPENLDAP_LBER_LIBRARY})
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(
|
||||
OpenLDAP DEFAULT_MSG
|
||||
OPENLDAP_INCLUDE_DIR OPENLDAP_LDAP_LIBRARY OPENLDAP_LBER_LIBRARY
|
||||
OPENLDAP_INCLUDE_DIRS OPENLDAP_LDAP_LIBRARY OPENLDAP_LBER_LIBRARY
|
||||
)
|
||||
|
||||
mark_as_advanced(OPENLDAP_INCLUDE_DIR OPENLDAP_LIBRARIES OPENLDAP_LDAP_LIBRARY OPENLDAP_LBER_LIBRARY)
|
||||
mark_as_advanced(OPENLDAP_INCLUDE_DIRS OPENLDAP_LIBRARIES OPENLDAP_LDAP_LIBRARY OPENLDAP_LBER_LIBRARY)
|
||||
|
||||
if(OPENLDAP_USE_STATIC_LIBS)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_orig_CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
|
@ -16,11 +16,16 @@ if (ENABLE_LDAP)
|
||||
set (OPENLDAP_USE_REENTRANT_LIBS 1)
|
||||
|
||||
if (NOT USE_INTERNAL_LDAP_LIBRARY)
|
||||
if (APPLE AND NOT OPENLDAP_ROOT_DIR)
|
||||
set (OPENLDAP_ROOT_DIR "/usr/local/opt/openldap")
|
||||
endif ()
|
||||
if (OPENLDAP_USE_STATIC_LIBS)
|
||||
message (WARNING "Unable to use external static OpenLDAP libraries, falling back to the bundled version.")
|
||||
set (USE_INTERNAL_LDAP_LIBRARY 1)
|
||||
else ()
|
||||
if (APPLE AND NOT OPENLDAP_ROOT_DIR)
|
||||
set (OPENLDAP_ROOT_DIR "/usr/local/opt/openldap")
|
||||
endif ()
|
||||
|
||||
find_package (OpenLDAP)
|
||||
find_package (OpenLDAP)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (NOT OPENLDAP_FOUND AND NOT MISSING_INTERNAL_LDAP_LIBRARY)
|
||||
@ -54,7 +59,10 @@ if (ENABLE_LDAP)
|
||||
else ()
|
||||
set (USE_INTERNAL_LDAP_LIBRARY 1)
|
||||
set (OPENLDAP_ROOT_DIR "${ClickHouse_SOURCE_DIR}/contrib/openldap")
|
||||
set (OPENLDAP_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/openldap/include")
|
||||
set (OPENLDAP_INCLUDE_DIRS
|
||||
"${ClickHouse_SOURCE_DIR}/contrib/openldap-cmake/${_system_name}_${_system_processor}/include"
|
||||
"${ClickHouse_SOURCE_DIR}/contrib/openldap/include"
|
||||
)
|
||||
# Below, 'ldap'/'ldap_r' and 'lber' will be resolved to
|
||||
# the targets defined in contrib/openldap-cmake/CMakeLists.txt
|
||||
if (OPENLDAP_USE_REENTRANT_LIBS)
|
||||
@ -73,4 +81,4 @@ if (ENABLE_LDAP)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
message (STATUS "Using ldap=${USE_LDAP}: ${OPENLDAP_INCLUDE_DIR} : ${OPENLDAP_LIBRARIES}")
|
||||
message (STATUS "Using ldap=${USE_LDAP}: ${OPENLDAP_INCLUDE_DIRS} : ${OPENLDAP_LIBRARIES}")
|
||||
|
@ -22,7 +22,7 @@ elseif (COMPILER_CLANG)
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${APPLE_CLANG_MINIMUM_VERSION})
|
||||
message (FATAL_ERROR "AppleClang compiler version must be at least ${APPLE_CLANG_MINIMUM_VERSION} (Xcode ${XCODE_MINIMUM_VERSION}).")
|
||||
elseif (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.0.0)
|
||||
# char8_t is available staring (upstream vanilla) Clang 7, but prior to Clang 8,
|
||||
# char8_t is available starting (upstream vanilla) Clang 7, but prior to Clang 8,
|
||||
# it is not enabled by -std=c++20 and can be enabled with an explicit -fchar8_t.
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fchar8_t")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fchar8_t")
|
||||
|
2
contrib/CMakeLists.txt
vendored
2
contrib/CMakeLists.txt
vendored
@ -102,7 +102,7 @@ if (USE_INTERNAL_SSL_LIBRARY)
|
||||
add_library(OpenSSL::SSL ALIAS ${OPENSSL_SSL_LIBRARY})
|
||||
endif ()
|
||||
|
||||
if (ENABLE_LDAP AND USE_INTERNAL_LDAP_LIBRARY)
|
||||
if (USE_INTERNAL_LDAP_LIBRARY)
|
||||
add_subdirectory (openldap-cmake)
|
||||
endif ()
|
||||
|
||||
|
@ -35,7 +35,7 @@ RUN apt-get update \
|
||||
ENV TZ=Europe/Moscow
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
RUN pip3 install urllib3 testflows==1.6.24 docker-compose docker dicttoxml kazoo tzlocal
|
||||
RUN pip3 install urllib3 testflows==1.6.39 docker-compose docker dicttoxml kazoo tzlocal
|
||||
|
||||
ENV DOCKER_CHANNEL stable
|
||||
ENV DOCKER_VERSION 17.09.1-ce
|
||||
|
@ -215,6 +215,9 @@ try
|
||||
|
||||
/// Skip networking
|
||||
|
||||
/// Sets external authenticators config (LDAP).
|
||||
context->setExternalAuthenticatorsConfig(config());
|
||||
|
||||
setupUsers();
|
||||
|
||||
/// Limit on total number of concurrently executing queries.
|
||||
|
@ -295,7 +295,7 @@ int Server::main(const std::vector<std::string> & /*args*/)
|
||||
#endif
|
||||
|
||||
/** Context contains all that query execution is dependent:
|
||||
* settings, available functions, data types, aggregate functions, databases...
|
||||
* settings, available functions, data types, aggregate functions, databases, ...
|
||||
*/
|
||||
auto shared_context = Context::createShared();
|
||||
auto global_context = std::make_unique<Context>(Context::createGlobal(shared_context.get()));
|
||||
@ -543,6 +543,7 @@ int Server::main(const std::vector<std::string> & /*args*/)
|
||||
//buildLoggers(*config, logger());
|
||||
global_context->setClustersConfig(config);
|
||||
global_context->setMacros(std::make_unique<Macros>(*config, "macros"));
|
||||
global_context->setExternalAuthenticatorsConfig(*config);
|
||||
|
||||
/// Setup protection to avoid accidental DROP for big tables (that are greater than 50 GB by default)
|
||||
if (config->has("max_table_size_to_drop"))
|
||||
|
@ -215,6 +215,47 @@
|
||||
<!-- Path to folder where users and roles created by SQL commands are stored. -->
|
||||
<access_control_path>/var/lib/clickhouse/access/</access_control_path>
|
||||
|
||||
<!-- External user directories (LDAP). -->
|
||||
<ldap_servers>
|
||||
<!-- List LDAP servers with their connection parameters here to later use them as authenticators for dedicated users,
|
||||
who have 'ldap' authentication mechanism specified instead of 'password'.
|
||||
Parameters:
|
||||
host - LDAP server hostname or IP, this parameter is mandatory and cannot be empty.
|
||||
port - LDAP server port, default is 636 if enable_tls is set to true, 389 otherwise.
|
||||
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.
|
||||
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).
|
||||
Specify 'starttls' for legacy StartTLS protocol (plain text (ldap://) protocol, upgraded to TLS).
|
||||
tls_minimum_protocol_version - the minimum protocol version of SSL/TLS.
|
||||
Accepted values are: 'ssl2', 'ssl3', 'tls1.0', 'tls1.1', 'tls1.2' (the default).
|
||||
tls_require_cert - SSL/TLS peer certificate verification behavior.
|
||||
Accepted values are: 'never', 'allow', 'try', 'demand' (the default).
|
||||
tls_cert_file - path to certificate file.
|
||||
tls_key_file - path to certificate key file.
|
||||
tls_ca_cert_file - path to CA certificate file.
|
||||
tls_ca_cert_dir - path to the directory containing CA certificates.
|
||||
tls_cipher_suite - allowed cipher suite.
|
||||
Example:
|
||||
<my_ldap_server>
|
||||
<host>localhost</host>
|
||||
<port>636</port>
|
||||
<auth_dn_prefix>uid=</auth_dn_prefix>
|
||||
<auth_dn_suffix>,ou=users,dc=example,dc=com</auth_dn_suffix>
|
||||
<enable_tls>yes</enable_tls>
|
||||
<tls_minimum_protocol_version>tls1.2</tls_minimum_protocol_version>
|
||||
<tls_require_cert>demand</tls_require_cert>
|
||||
<tls_cert_file>/path/to/tls_cert_file</tls_cert_file>
|
||||
<tls_key_file>/path/to/tls_key_file</tls_key_file>
|
||||
<tls_ca_cert_file>/path/to/tls_ca_cert_file</tls_ca_cert_file>
|
||||
<tls_ca_cert_dir>/path/to/tls_ca_cert_dir</tls_ca_cert_dir>
|
||||
<tls_cipher_suite>ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384</tls_cipher_suite>
|
||||
</my_ldap_server>
|
||||
-->
|
||||
</ldap_servers>
|
||||
|
||||
<!-- Path to configuration file with users, access rights, profiles of settings, quotas. -->
|
||||
<users_config>users.xml</users_config>
|
||||
|
||||
|
@ -44,6 +44,9 @@
|
||||
If you want to specify double SHA1, place it in 'password_double_sha1_hex' element.
|
||||
Example: <password_double_sha1_hex>e395796d6546b1b65db9d665cd43f0e858dd4303</password_double_sha1_hex>
|
||||
|
||||
If you want to specify a previously defined LDAP server (see 'ldap_servers' in main config) for authentication, place its name in 'server' element inside 'ldap' element.
|
||||
Example: <ldap><server>my_ldap_server</server></ldap>
|
||||
|
||||
How to generate decent password:
|
||||
Execute: PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | sha256sum | tr -d '-'
|
||||
In first line will be password and in second - corresponding SHA256.
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <Access/QuotaCache.h>
|
||||
#include <Access/QuotaUsage.h>
|
||||
#include <Access/SettingsProfilesCache.h>
|
||||
#include <Access/ExternalAuthenticators.h>
|
||||
#include <Core/Settings.h>
|
||||
#include <Poco/ExpireCache.h>
|
||||
#include <mutex>
|
||||
@ -64,7 +65,8 @@ AccessControlManager::AccessControlManager()
|
||||
role_cache(std::make_unique<RoleCache>(*this)),
|
||||
row_policy_cache(std::make_unique<RowPolicyCache>(*this)),
|
||||
quota_cache(std::make_unique<QuotaCache>(*this)),
|
||||
settings_profiles_cache(std::make_unique<SettingsProfilesCache>(*this))
|
||||
settings_profiles_cache(std::make_unique<SettingsProfilesCache>(*this)),
|
||||
external_authenticators(std::make_unique<ExternalAuthenticators>())
|
||||
{
|
||||
}
|
||||
|
||||
@ -79,6 +81,12 @@ void AccessControlManager::setLocalDirectory(const String & directory_path)
|
||||
}
|
||||
|
||||
|
||||
void AccessControlManager::setExternalAuthenticatorsConfig(const Poco::Util::AbstractConfiguration & config)
|
||||
{
|
||||
external_authenticators->setConfig(config, getLogger());
|
||||
}
|
||||
|
||||
|
||||
void AccessControlManager::setUsersConfig(const Poco::Util::AbstractConfiguration & users_config)
|
||||
{
|
||||
auto & users_config_access_storage = dynamic_cast<UsersConfigAccessStorage &>(getStorageByIndex(USERS_CONFIG_ACCESS_STORAGE_INDEX));
|
||||
@ -163,4 +171,9 @@ std::shared_ptr<const SettingsChanges> AccessControlManager::getProfileSettings(
|
||||
return settings_profiles_cache->getProfileSettings(profile_name);
|
||||
}
|
||||
|
||||
const ExternalAuthenticators & AccessControlManager::getExternalAuthenticators() const
|
||||
{
|
||||
return *external_authenticators;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ class EnabledSettings;
|
||||
class SettingsProfilesCache;
|
||||
class SettingsProfileElements;
|
||||
class ClientInfo;
|
||||
class ExternalAuthenticators;
|
||||
struct Settings;
|
||||
|
||||
|
||||
@ -48,6 +49,7 @@ public:
|
||||
~AccessControlManager();
|
||||
|
||||
void setLocalDirectory(const String & directory);
|
||||
void setExternalAuthenticatorsConfig(const Poco::Util::AbstractConfiguration & config);
|
||||
void setUsersConfig(const Poco::Util::AbstractConfiguration & users_config);
|
||||
void setDefaultProfileName(const String & default_profile_name);
|
||||
|
||||
@ -85,6 +87,8 @@ public:
|
||||
|
||||
std::shared_ptr<const SettingsChanges> getProfileSettings(const String & profile_name) const;
|
||||
|
||||
const ExternalAuthenticators & getExternalAuthenticators() const;
|
||||
|
||||
private:
|
||||
class ContextAccessCache;
|
||||
std::unique_ptr<ContextAccessCache> context_access_cache;
|
||||
@ -92,6 +96,7 @@ private:
|
||||
std::unique_ptr<RowPolicyCache> row_policy_cache;
|
||||
std::unique_ptr<QuotaCache> quota_cache;
|
||||
std::unique_ptr<SettingsProfilesCache> settings_profiles_cache;
|
||||
std::unique_ptr<ExternalAuthenticators> external_authenticators;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include <Access/Authentication.h>
|
||||
#include <Access/ExternalAuthenticators.h>
|
||||
#include <Access/LDAPClient.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Poco/SHA1Engine.h>
|
||||
|
||||
@ -37,6 +39,9 @@ Authentication::Digest Authentication::getPasswordDoubleSHA1() const
|
||||
case DOUBLE_SHA1_PASSWORD:
|
||||
return password_hash;
|
||||
|
||||
case LDAP_SERVER:
|
||||
throw Exception("Cannot get password double SHA1 for user with 'LDAP_SERVER' authentication.", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
case MAX_TYPE:
|
||||
break;
|
||||
}
|
||||
@ -44,7 +49,7 @@ Authentication::Digest Authentication::getPasswordDoubleSHA1() const
|
||||
}
|
||||
|
||||
|
||||
bool Authentication::isCorrectPassword(const String & password_) const
|
||||
bool Authentication::isCorrectPassword(const String & password_, const String & user_, const ExternalAuthenticators & external_authenticators) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
@ -75,6 +80,16 @@ bool Authentication::isCorrectPassword(const String & password_) const
|
||||
return encodeSHA1(first_sha1) == password_hash;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
case MAX_TYPE:
|
||||
break;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ namespace ErrorCodes
|
||||
extern const int NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
class ExternalAuthenticators;
|
||||
|
||||
/// Authentication type and encrypted password for checking when an user logins.
|
||||
class Authentication
|
||||
@ -38,6 +39,9 @@ public:
|
||||
/// This kind of hash is used by the `mysql_native_password` authentication plugin.
|
||||
DOUBLE_SHA1_PASSWORD,
|
||||
|
||||
/// Password is checked by a [remote] LDAP server. Connection will be made at each authentication attempt.
|
||||
LDAP_SERVER,
|
||||
|
||||
MAX_TYPE,
|
||||
};
|
||||
|
||||
@ -78,8 +82,14 @@ public:
|
||||
/// Allowed to use for Type::NO_PASSWORD, Type::PLAINTEXT_PASSWORD, Type::DOUBLE_SHA1_PASSWORD.
|
||||
Digest getPasswordDoubleSHA1() const;
|
||||
|
||||
/// Sets an external authentication server name.
|
||||
/// When authentication type is LDAP_SERVER, server name is expected to be the name of a preconfigured LDAP server.
|
||||
const String & getServerName() const;
|
||||
void setServerName(const String & server_name_);
|
||||
|
||||
/// Checks if the provided password is correct. Returns false if not.
|
||||
bool isCorrectPassword(const String & password) const;
|
||||
/// 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;
|
||||
|
||||
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); }
|
||||
@ -93,6 +103,7 @@ private:
|
||||
|
||||
Type type = Type::NO_PASSWORD;
|
||||
Digest password_hash;
|
||||
String server_name;
|
||||
};
|
||||
|
||||
|
||||
@ -127,6 +138,11 @@ inline const Authentication::TypeInfo & Authentication::TypeInfo::get(Type type_
|
||||
static const auto info = make_info("DOUBLE_SHA1_PASSWORD");
|
||||
return info;
|
||||
}
|
||||
case LDAP_SERVER:
|
||||
{
|
||||
static const auto info = make_info("LDAP_SERVER");
|
||||
return info;
|
||||
}
|
||||
case MAX_TYPE: break;
|
||||
}
|
||||
throw Exception("Unknown authentication type: " + std::to_string(static_cast<int>(type_)), ErrorCodes::LOGICAL_ERROR);
|
||||
@ -176,6 +192,9 @@ inline void Authentication::setPassword(const String & password_)
|
||||
case DOUBLE_SHA1_PASSWORD:
|
||||
return setPasswordHashBinary(encodeDoubleSHA1(password_));
|
||||
|
||||
case LDAP_SERVER:
|
||||
throw Exception("Cannot specify password for the 'LDAP_SERVER' authentication type", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
case MAX_TYPE: break;
|
||||
}
|
||||
throw Exception("setPassword(): authentication type " + toString(type) + " not supported", ErrorCodes::NOT_IMPLEMENTED);
|
||||
@ -200,6 +219,8 @@ inline void Authentication::setPasswordHashHex(const String & hash)
|
||||
|
||||
inline String Authentication::getPasswordHashHex() const
|
||||
{
|
||||
if (type == LDAP_SERVER)
|
||||
throw Exception("Cannot get password of a user with the 'LDAP_SERVER' authentication type", ErrorCodes::LOGICAL_ERROR);
|
||||
String hex;
|
||||
hex.resize(password_hash.size() * 2);
|
||||
boost::algorithm::hex(password_hash.begin(), password_hash.end(), hex.data());
|
||||
@ -242,9 +263,22 @@ inline void Authentication::setPasswordHashBinary(const Digest & hash)
|
||||
return;
|
||||
}
|
||||
|
||||
case LDAP_SERVER:
|
||||
throw Exception("Cannot specify password for the 'LDAP_SERVER' authentication type", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
case MAX_TYPE: break;
|
||||
}
|
||||
throw Exception("setPasswordHashBinary(): authentication type " + toString(type) + " not supported", ErrorCodes::NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
inline const String & Authentication::getServerName() const
|
||||
{
|
||||
return server_name;
|
||||
}
|
||||
|
||||
inline void Authentication::setServerName(const String & server_name_)
|
||||
{
|
||||
server_name = server_name_;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -293,7 +293,7 @@ bool ContextAccess::isCorrectPassword(const String & password) const
|
||||
std::lock_guard lock{mutex};
|
||||
if (!user)
|
||||
return false;
|
||||
return user->authentication.isCorrectPassword(password);
|
||||
return user->authentication.isCorrectPassword(password, user_name, manager->getExternalAuthenticators());
|
||||
}
|
||||
|
||||
bool ContextAccess::isClientHostAllowed() const
|
||||
|
182
src/Access/ExternalAuthenticators.cpp
Normal file
182
src/Access/ExternalAuthenticators.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
#include <Access/ExternalAuthenticators.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/quoteString.h>
|
||||
#include <Poco/Util/AbstractConfiguration.h>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
auto parseLDAPServer(const Poco::Util::AbstractConfiguration & config, const String & ldap_server_name)
|
||||
{
|
||||
if (ldap_server_name.empty())
|
||||
throw Exception("LDAP server name cannot be empty", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
LDAPServerParams params;
|
||||
|
||||
const String ldap_server_config = "ldap_servers." + ldap_server_name;
|
||||
|
||||
const bool has_host = config.has(ldap_server_config + ".host");
|
||||
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_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");
|
||||
const bool has_tls_cert_file = config.has(ldap_server_config + ".tls_cert_file");
|
||||
const bool has_tls_key_file = config.has(ldap_server_config + ".tls_key_file");
|
||||
const bool has_tls_ca_cert_file = config.has(ldap_server_config + ".tls_ca_cert_file");
|
||||
const bool has_tls_ca_cert_dir = config.has(ldap_server_config + ".tls_ca_cert_dir");
|
||||
const bool has_tls_cipher_suite = config.has(ldap_server_config + ".tls_cipher_suite");
|
||||
|
||||
if (!has_host)
|
||||
throw Exception("Missing 'host' entry", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
params.host = config.getString(ldap_server_config + ".host");
|
||||
|
||||
if (params.host.empty())
|
||||
throw Exception("Empty 'host' entry", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
if (has_auth_dn_prefix)
|
||||
params.auth_dn_prefix = config.getString(ldap_server_config + ".auth_dn_prefix");
|
||||
|
||||
if (has_auth_dn_suffix)
|
||||
params.auth_dn_suffix = config.getString(ldap_server_config + ".auth_dn_suffix");
|
||||
|
||||
if (has_enable_tls)
|
||||
{
|
||||
String enable_tls_lc_str = config.getString(ldap_server_config + ".enable_tls");
|
||||
boost::to_lower(enable_tls_lc_str);
|
||||
|
||||
if (enable_tls_lc_str == "starttls")
|
||||
params.enable_tls = LDAPServerParams::TLSEnable::YES_STARTTLS;
|
||||
else if (config.getBool(ldap_server_config + ".enable_tls"))
|
||||
params.enable_tls = LDAPServerParams::TLSEnable::YES;
|
||||
else
|
||||
params.enable_tls = LDAPServerParams::TLSEnable::NO;
|
||||
}
|
||||
|
||||
if (has_tls_minimum_protocol_version)
|
||||
{
|
||||
String tls_minimum_protocol_version_lc_str = config.getString(ldap_server_config + ".tls_minimum_protocol_version");
|
||||
boost::to_lower(tls_minimum_protocol_version_lc_str);
|
||||
|
||||
if (tls_minimum_protocol_version_lc_str == "ssl2")
|
||||
params.tls_minimum_protocol_version = LDAPServerParams::TLSProtocolVersion::SSL2;
|
||||
else if (tls_minimum_protocol_version_lc_str == "ssl3")
|
||||
params.tls_minimum_protocol_version = LDAPServerParams::TLSProtocolVersion::SSL3;
|
||||
else if (tls_minimum_protocol_version_lc_str == "tls1.0")
|
||||
params.tls_minimum_protocol_version = LDAPServerParams::TLSProtocolVersion::TLS1_0;
|
||||
else if (tls_minimum_protocol_version_lc_str == "tls1.1")
|
||||
params.tls_minimum_protocol_version = LDAPServerParams::TLSProtocolVersion::TLS1_1;
|
||||
else if (tls_minimum_protocol_version_lc_str == "tls1.2")
|
||||
params.tls_minimum_protocol_version = LDAPServerParams::TLSProtocolVersion::TLS1_2;
|
||||
else
|
||||
throw Exception("Bad value for 'tls_minimum_protocol_version' entry, allowed values are: 'ssl2', 'ssl3', 'tls1.0', 'tls1.1', 'tls1.2'", ErrorCodes::BAD_ARGUMENTS);
|
||||
}
|
||||
|
||||
if (has_tls_require_cert)
|
||||
{
|
||||
String tls_require_cert_lc_str = config.getString(ldap_server_config + ".tls_require_cert");
|
||||
boost::to_lower(tls_require_cert_lc_str);
|
||||
|
||||
if (tls_require_cert_lc_str == "never")
|
||||
params.tls_require_cert = LDAPServerParams::TLSRequireCert::NEVER;
|
||||
else if (tls_require_cert_lc_str == "allow")
|
||||
params.tls_require_cert = LDAPServerParams::TLSRequireCert::ALLOW;
|
||||
else if (tls_require_cert_lc_str == "try")
|
||||
params.tls_require_cert = LDAPServerParams::TLSRequireCert::TRY;
|
||||
else if (tls_require_cert_lc_str == "demand")
|
||||
params.tls_require_cert = LDAPServerParams::TLSRequireCert::DEMAND;
|
||||
else
|
||||
throw Exception("Bad value for 'tls_require_cert' entry, allowed values are: 'never', 'allow', 'try', 'demand'", ErrorCodes::BAD_ARGUMENTS);
|
||||
}
|
||||
|
||||
if (has_tls_cert_file)
|
||||
params.tls_cert_file = config.getString(ldap_server_config + ".tls_cert_file");
|
||||
|
||||
if (has_tls_key_file)
|
||||
params.tls_key_file = config.getString(ldap_server_config + ".tls_key_file");
|
||||
|
||||
if (has_tls_ca_cert_file)
|
||||
params.tls_ca_cert_file = config.getString(ldap_server_config + ".tls_ca_cert_file");
|
||||
|
||||
if (has_tls_ca_cert_dir)
|
||||
params.tls_ca_cert_dir = config.getString(ldap_server_config + ".tls_ca_cert_dir");
|
||||
|
||||
if (has_tls_cipher_suite)
|
||||
params.tls_cipher_suite = config.getString(ldap_server_config + ".tls_cipher_suite");
|
||||
|
||||
if (has_port)
|
||||
{
|
||||
const auto port = config.getInt64(ldap_server_config + ".port");
|
||||
if (port < 0 || port > 65535)
|
||||
throw Exception("Bad value for 'port' entry", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
params.port = port;
|
||||
}
|
||||
else
|
||||
params.port = (params.enable_tls == LDAPServerParams::TLSEnable::YES ? 636 : 389);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
void parseAndAddLDAPServers(ExternalAuthenticators & external_authenticators, const Poco::Util::AbstractConfiguration & config, Poco::Logger * log)
|
||||
{
|
||||
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));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tryLogCurrentException(log, "Could not parse LDAP server " + backQuote(ldap_server_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ExternalAuthenticators::reset()
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
ldap_server_params.clear();
|
||||
}
|
||||
|
||||
void ExternalAuthenticators::setConfig(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log)
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
reset();
|
||||
parseAndAddLDAPServers(*this, config, log);
|
||||
}
|
||||
|
||||
void ExternalAuthenticators::setLDAPServerParams(const String & server, const LDAPServerParams & params)
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
ldap_server_params.erase(server);
|
||||
ldap_server_params[server] = params;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
39
src/Access/ExternalAuthenticators.h
Normal file
39
src/Access/ExternalAuthenticators.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <Access/LDAPParams.h>
|
||||
#include <Core/Types.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
namespace Poco
|
||||
{
|
||||
class Logger;
|
||||
|
||||
namespace Util
|
||||
{
|
||||
class AbstractConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
class ExternalAuthenticators
|
||||
{
|
||||
public:
|
||||
void reset();
|
||||
void setConfig(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log);
|
||||
|
||||
void setLDAPServerParams(const String & server, const LDAPServerParams & params);
|
||||
LDAPServerParams getLDAPServerParams(const String & server) const;
|
||||
|
||||
private:
|
||||
mutable std::recursive_mutex mutex;
|
||||
std::map<String, LDAPServerParams> ldap_server_params;
|
||||
};
|
||||
|
||||
}
|
331
src/Access/LDAPClient.cpp
Normal file
331
src/Access/LDAPClient.cpp
Normal file
@ -0,0 +1,331 @@
|
||||
#include <Access/LDAPClient.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <ext/scope_guard.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int BAD_ARGUMENTS;
|
||||
extern const int FEATURE_IS_NOT_ENABLED_AT_BUILD_TIME;
|
||||
extern const int LDAP_ERROR;
|
||||
}
|
||||
|
||||
LDAPClient::LDAPClient(const LDAPServerParams & params_)
|
||||
: params(params_)
|
||||
{
|
||||
}
|
||||
|
||||
LDAPClient::~LDAPClient()
|
||||
{
|
||||
closeConnection();
|
||||
}
|
||||
|
||||
void LDAPClient::openConnection()
|
||||
{
|
||||
const bool graceful_bind_failure = false;
|
||||
diag(openConnection(graceful_bind_failure));
|
||||
}
|
||||
|
||||
#if USE_LDAP
|
||||
|
||||
namespace
|
||||
{
|
||||
auto escapeForLDAP(const String & src)
|
||||
{
|
||||
String dest;
|
||||
dest.reserve(src.size() * 2);
|
||||
|
||||
for (auto ch : src)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case ',':
|
||||
case '\\':
|
||||
case '#':
|
||||
case '+':
|
||||
case '<':
|
||||
case '>':
|
||||
case ';':
|
||||
case '"':
|
||||
case '=':
|
||||
dest += '\\';
|
||||
break;
|
||||
}
|
||||
dest += ch;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
void LDAPClient::diag(const int rc)
|
||||
{
|
||||
if (rc != LDAP_SUCCESS)
|
||||
{
|
||||
String text;
|
||||
const char * raw_err_str = ldap_err2string(rc);
|
||||
|
||||
if (raw_err_str)
|
||||
text = raw_err_str;
|
||||
|
||||
if (handle)
|
||||
{
|
||||
String message;
|
||||
char * raw_message = nullptr;
|
||||
ldap_get_option(handle, LDAP_OPT_DIAGNOSTIC_MESSAGE, &raw_message);
|
||||
|
||||
if (raw_message)
|
||||
{
|
||||
message = raw_message;
|
||||
ldap_memfree(raw_message);
|
||||
raw_message = nullptr;
|
||||
}
|
||||
|
||||
if (!message.empty())
|
||||
{
|
||||
if (!text.empty())
|
||||
text += ": ";
|
||||
text += message;
|
||||
}
|
||||
}
|
||||
|
||||
throw Exception(text, ErrorCodes::LDAP_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
int LDAPClient::openConnection(const bool graceful_bind_failure)
|
||||
{
|
||||
closeConnection();
|
||||
|
||||
{
|
||||
LDAPURLDesc url;
|
||||
std::memset(&url, 0, sizeof(url));
|
||||
|
||||
url.lud_scheme = const_cast<char *>(params.enable_tls == LDAPServerParams::TLSEnable::YES ? "ldaps" : "ldap");
|
||||
url.lud_host = const_cast<char *>(params.host.c_str());
|
||||
url.lud_port = params.port;
|
||||
url.lud_scope = LDAP_SCOPE_DEFAULT;
|
||||
|
||||
auto * uri = ldap_url_desc2str(&url);
|
||||
if (!uri)
|
||||
throw Exception("ldap_url_desc2str() failed", ErrorCodes::LDAP_ERROR);
|
||||
|
||||
SCOPE_EXIT({ ldap_memfree(uri); });
|
||||
|
||||
diag(ldap_initialize(&handle, uri));
|
||||
if (!handle)
|
||||
throw Exception("ldap_initialize() failed", ErrorCodes::LDAP_ERROR);
|
||||
}
|
||||
|
||||
{
|
||||
int value = 0;
|
||||
switch (params.protocol_version)
|
||||
{
|
||||
case LDAPServerParams::ProtocolVersion::V2: value = LDAP_VERSION2; break;
|
||||
case LDAPServerParams::ProtocolVersion::V3: value = LDAP_VERSION3; break;
|
||||
}
|
||||
diag(ldap_set_option(handle, LDAP_OPT_PROTOCOL_VERSION, &value));
|
||||
}
|
||||
|
||||
diag(ldap_set_option(handle, LDAP_OPT_RESTART, LDAP_OPT_ON));
|
||||
|
||||
#ifdef LDAP_OPT_KEEPCONN
|
||||
diag(ldap_set_option(handle, LDAP_OPT_KEEPCONN, LDAP_OPT_ON));
|
||||
#endif
|
||||
|
||||
#ifdef LDAP_OPT_TIMEOUT
|
||||
{
|
||||
::timeval operation_timeout;
|
||||
operation_timeout.tv_sec = params.operation_timeout.count();
|
||||
operation_timeout.tv_usec = 0;
|
||||
diag(ldap_set_option(handle, LDAP_OPT_TIMEOUT, &operation_timeout));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LDAP_OPT_NETWORK_TIMEOUT
|
||||
{
|
||||
::timeval network_timeout;
|
||||
network_timeout.tv_sec = params.network_timeout.count();
|
||||
network_timeout.tv_usec = 0;
|
||||
diag(ldap_set_option(handle, LDAP_OPT_NETWORK_TIMEOUT, &network_timeout));
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
const int search_timeout = params.search_timeout.count();
|
||||
diag(ldap_set_option(handle, LDAP_OPT_TIMELIMIT, &search_timeout));
|
||||
}
|
||||
|
||||
{
|
||||
const int size_limit = params.search_limit;
|
||||
diag(ldap_set_option(handle, LDAP_OPT_SIZELIMIT, &size_limit));
|
||||
}
|
||||
|
||||
#ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN
|
||||
{
|
||||
int value = 0;
|
||||
switch (params.tls_minimum_protocol_version)
|
||||
{
|
||||
case LDAPServerParams::TLSProtocolVersion::SSL2: value = LDAP_OPT_X_TLS_PROTOCOL_SSL2; break;
|
||||
case LDAPServerParams::TLSProtocolVersion::SSL3: value = LDAP_OPT_X_TLS_PROTOCOL_SSL3; break;
|
||||
case LDAPServerParams::TLSProtocolVersion::TLS1_0: value = LDAP_OPT_X_TLS_PROTOCOL_TLS1_0; break;
|
||||
case LDAPServerParams::TLSProtocolVersion::TLS1_1: value = LDAP_OPT_X_TLS_PROTOCOL_TLS1_1; break;
|
||||
case LDAPServerParams::TLSProtocolVersion::TLS1_2: value = LDAP_OPT_X_TLS_PROTOCOL_TLS1_2; break;
|
||||
}
|
||||
diag(ldap_set_option(handle, LDAP_OPT_X_TLS_PROTOCOL_MIN, &value));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
|
||||
{
|
||||
int value = 0;
|
||||
switch (params.tls_require_cert)
|
||||
{
|
||||
case LDAPServerParams::TLSRequireCert::NEVER: value = LDAP_OPT_X_TLS_NEVER; break;
|
||||
case LDAPServerParams::TLSRequireCert::ALLOW: value = LDAP_OPT_X_TLS_ALLOW; break;
|
||||
case LDAPServerParams::TLSRequireCert::TRY: value = LDAP_OPT_X_TLS_TRY; break;
|
||||
case LDAPServerParams::TLSRequireCert::DEMAND: value = LDAP_OPT_X_TLS_DEMAND; break;
|
||||
}
|
||||
diag(ldap_set_option(handle, LDAP_OPT_X_TLS_REQUIRE_CERT, &value));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LDAP_OPT_X_TLS_CERTFILE
|
||||
if (!params.tls_cert_file.empty())
|
||||
diag(ldap_set_option(handle, LDAP_OPT_X_TLS_CERTFILE, params.tls_cert_file.c_str()));
|
||||
#endif
|
||||
|
||||
#ifdef LDAP_OPT_X_TLS_KEYFILE
|
||||
if (!params.tls_key_file.empty())
|
||||
diag(ldap_set_option(handle, LDAP_OPT_X_TLS_KEYFILE, params.tls_key_file.c_str()));
|
||||
#endif
|
||||
|
||||
#ifdef LDAP_OPT_X_TLS_CACERTFILE
|
||||
if (!params.tls_ca_cert_file.empty())
|
||||
diag(ldap_set_option(handle, LDAP_OPT_X_TLS_CACERTFILE, params.tls_ca_cert_file.c_str()));
|
||||
#endif
|
||||
|
||||
#ifdef LDAP_OPT_X_TLS_CACERTDIR
|
||||
if (!params.tls_ca_cert_dir.empty())
|
||||
diag(ldap_set_option(handle, LDAP_OPT_X_TLS_CACERTDIR, params.tls_ca_cert_dir.c_str()));
|
||||
#endif
|
||||
|
||||
#ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
|
||||
if (!params.tls_cipher_suite.empty())
|
||||
diag(ldap_set_option(handle, LDAP_OPT_X_TLS_CIPHER_SUITE, params.tls_cipher_suite.c_str()));
|
||||
#endif
|
||||
|
||||
#ifdef LDAP_OPT_X_TLS_NEWCTX
|
||||
{
|
||||
const int i_am_a_server = 0;
|
||||
diag(ldap_set_option(handle, LDAP_OPT_X_TLS_NEWCTX, &i_am_a_server));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (params.enable_tls == LDAPServerParams::TLSEnable::YES_STARTTLS)
|
||||
diag(ldap_start_tls_s(handle, nullptr, nullptr));
|
||||
|
||||
int rc = LDAP_OTHER;
|
||||
|
||||
switch (params.sasl_mechanism)
|
||||
{
|
||||
case LDAPServerParams::SASLMechanism::SIMPLE:
|
||||
{
|
||||
const String dn = params.auth_dn_prefix + escapeForLDAP(params.user) + params.auth_dn_suffix;
|
||||
|
||||
::berval cred;
|
||||
cred.bv_val = const_cast<char *>(params.password.c_str());
|
||||
cred.bv_len = params.password.size();
|
||||
|
||||
rc = ldap_sasl_bind_s(handle, dn.c_str(), LDAP_SASL_SIMPLE, &cred, nullptr, nullptr, nullptr);
|
||||
|
||||
if (!graceful_bind_failure)
|
||||
diag(rc);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void LDAPClient::closeConnection() noexcept
|
||||
{
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
ldap_unbind_ext_s(handle, nullptr, nullptr);
|
||||
handle = nullptr;
|
||||
}
|
||||
|
||||
bool LDAPSimpleAuthClient::check()
|
||||
{
|
||||
if (params.user.empty())
|
||||
throw Exception("LDAP authentication of a user with an empty name is not allowed", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
if (params.password.empty())
|
||||
return false; // Silently reject authentication attempt if the password is empty as if it didn't match.
|
||||
|
||||
SCOPE_EXIT({ closeConnection(); });
|
||||
|
||||
const bool graceful_bind_failure = true;
|
||||
const auto rc = openConnection(graceful_bind_failure);
|
||||
|
||||
bool result = false;
|
||||
|
||||
switch (rc)
|
||||
{
|
||||
case LDAP_SUCCESS:
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case LDAP_INVALID_CREDENTIALS:
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
result = false;
|
||||
diag(rc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#else // USE_LDAP
|
||||
|
||||
void LDAPClient::diag(const int)
|
||||
{
|
||||
throw Exception("ClickHouse was built without LDAP support", ErrorCodes::FEATURE_IS_NOT_ENABLED_AT_BUILD_TIME);
|
||||
}
|
||||
|
||||
int LDAPClient::openConnection(const bool)
|
||||
{
|
||||
throw Exception("ClickHouse was built without LDAP support", ErrorCodes::FEATURE_IS_NOT_ENABLED_AT_BUILD_TIME);
|
||||
}
|
||||
|
||||
void LDAPClient::closeConnection() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
bool LDAPSimpleAuthClient::check()
|
||||
{
|
||||
throw Exception("ClickHouse was built without LDAP support", ErrorCodes::FEATURE_IS_NOT_ENABLED_AT_BUILD_TIME);
|
||||
}
|
||||
|
||||
#endif // USE_LDAP
|
||||
|
||||
}
|
55
src/Access/LDAPClient.h
Normal file
55
src/Access/LDAPClient.h
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined(ARCADIA_BUILD)
|
||||
# include "config_core.h"
|
||||
#endif
|
||||
|
||||
#include <Access/LDAPParams.h>
|
||||
#include <Core/Types.h>
|
||||
|
||||
#if USE_LDAP
|
||||
# include <ldap.h>
|
||||
# define MAYBE_NORETURN
|
||||
#else
|
||||
# define MAYBE_NORETURN [[noreturn]]
|
||||
#endif
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
class LDAPClient
|
||||
{
|
||||
public:
|
||||
explicit LDAPClient(const LDAPServerParams & params_);
|
||||
~LDAPClient();
|
||||
|
||||
LDAPClient(const LDAPClient &) = delete;
|
||||
LDAPClient(LDAPClient &&) = delete;
|
||||
LDAPClient & operator= (const LDAPClient &) = delete;
|
||||
LDAPClient & operator= (LDAPClient &&) = delete;
|
||||
|
||||
protected:
|
||||
MAYBE_NORETURN void diag(const int rc);
|
||||
MAYBE_NORETURN void openConnection();
|
||||
int openConnection(const bool graceful_bind_failure = false);
|
||||
void closeConnection() noexcept;
|
||||
|
||||
protected:
|
||||
const LDAPServerParams params;
|
||||
#if USE_LDAP
|
||||
LDAP * handle = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
class LDAPSimpleAuthClient
|
||||
: private LDAPClient
|
||||
{
|
||||
public:
|
||||
using LDAPClient::LDAPClient;
|
||||
bool check();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#undef MAYBE_NORETURN
|
76
src/Access/LDAPParams.h
Normal file
76
src/Access/LDAPParams.h
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/Types.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
struct LDAPServerParams
|
||||
{
|
||||
enum class ProtocolVersion
|
||||
{
|
||||
V2,
|
||||
V3
|
||||
};
|
||||
|
||||
enum class TLSEnable
|
||||
{
|
||||
NO,
|
||||
YES_STARTTLS,
|
||||
YES
|
||||
};
|
||||
|
||||
enum class TLSProtocolVersion
|
||||
{
|
||||
SSL2,
|
||||
SSL3,
|
||||
TLS1_0,
|
||||
TLS1_1,
|
||||
TLS1_2
|
||||
};
|
||||
|
||||
enum class TLSRequireCert
|
||||
{
|
||||
NEVER,
|
||||
ALLOW,
|
||||
TRY,
|
||||
DEMAND
|
||||
};
|
||||
|
||||
enum class SASLMechanism
|
||||
{
|
||||
SIMPLE
|
||||
};
|
||||
|
||||
ProtocolVersion protocol_version = ProtocolVersion::V3;
|
||||
|
||||
String host;
|
||||
std::uint16_t port = 636;
|
||||
|
||||
TLSEnable enable_tls = TLSEnable::YES;
|
||||
TLSProtocolVersion tls_minimum_protocol_version = TLSProtocolVersion::TLS1_2;
|
||||
TLSRequireCert tls_require_cert = TLSRequireCert::DEMAND;
|
||||
String tls_cert_file;
|
||||
String tls_key_file;
|
||||
String tls_ca_cert_file;
|
||||
String tls_ca_cert_dir;
|
||||
String tls_cipher_suite;
|
||||
|
||||
SASLMechanism sasl_mechanism = SASLMechanism::SIMPLE;
|
||||
|
||||
String auth_dn_prefix;
|
||||
String auth_dn_suffix;
|
||||
|
||||
String user;
|
||||
String password;
|
||||
|
||||
std::chrono::seconds operation_timeout{40};
|
||||
std::chrono::seconds network_timeout{30};
|
||||
std::chrono::seconds search_timeout{20};
|
||||
std::uint32_t search_limit = 100;
|
||||
};
|
||||
|
||||
}
|
@ -56,14 +56,15 @@ namespace
|
||||
bool has_password_plaintext = config.has(user_config + ".password");
|
||||
bool has_password_sha256_hex = config.has(user_config + ".password_sha256_hex");
|
||||
bool has_password_double_sha1_hex = config.has(user_config + ".password_double_sha1_hex");
|
||||
bool has_ldap = config.has(user_config + ".ldap");
|
||||
|
||||
size_t num_password_fields = has_no_password + has_password_plaintext + has_password_sha256_hex + has_password_double_sha1_hex;
|
||||
size_t num_password_fields = has_no_password + has_password_plaintext + has_password_sha256_hex + has_password_double_sha1_hex + has_ldap;
|
||||
if (num_password_fields > 1)
|
||||
throw Exception("More than one field of 'password', 'password_sha256_hex', 'password_double_sha1_hex', 'no_password' are used to specify password for user " + user_name + ". Must be only one of them.",
|
||||
throw Exception("More than one field of 'password', 'password_sha256_hex', 'password_double_sha1_hex', 'no_password', 'ldap' are used to specify password for user " + user_name + ". Must be only one of them.",
|
||||
ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
if (num_password_fields < 1)
|
||||
throw Exception("Either 'password' or 'password_sha256_hex' or 'password_double_sha1_hex' or 'no_password' must be specified for user " + user_name + ".", ErrorCodes::BAD_ARGUMENTS);
|
||||
throw Exception("Either 'password' or 'password_sha256_hex' or 'password_double_sha1_hex' or 'no_password' or 'ldap' must be specified for user " + user_name + ".", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
if (has_password_plaintext)
|
||||
{
|
||||
@ -80,6 +81,19 @@ namespace
|
||||
user->authentication = Authentication{Authentication::DOUBLE_SHA1_PASSWORD};
|
||||
user->authentication.setPasswordHashHex(config.getString(user_config + ".password_double_sha1_hex"));
|
||||
}
|
||||
else if (has_ldap)
|
||||
{
|
||||
bool has_ldap_server = config.has(user_config + ".ldap.server");
|
||||
if (!has_ldap_server)
|
||||
throw Exception("Missing mandatory 'server' in 'ldap', with LDAP server name, for user " + user_name + ".", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
const auto ldap_server_name = config.getString(user_config + ".ldap.server");
|
||||
if (ldap_server_name.empty())
|
||||
throw Exception("LDAP server name cannot be empty for user " + user_name + ".", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
user->authentication = Authentication{Authentication::LDAP_SERVER};
|
||||
user->authentication.setServerName(ldap_server_name);
|
||||
}
|
||||
|
||||
const auto profile_name_config = user_config + ".profile";
|
||||
if (config.has(profile_name_config))
|
||||
|
@ -17,9 +17,11 @@ SRCS(
|
||||
EnabledRolesInfo.cpp
|
||||
EnabledRowPolicies.cpp
|
||||
EnabledSettings.cpp
|
||||
ExternalAuthenticators.cpp
|
||||
GrantedRoles.cpp
|
||||
IAccessEntity.cpp
|
||||
IAccessStorage.cpp
|
||||
LDAPClient.cpp
|
||||
MemoryAccessStorage.cpp
|
||||
MultipleAccessStorage.cpp
|
||||
Quota.cpp
|
||||
|
@ -332,7 +332,7 @@ if (OPENSSL_CRYPTO_LIBRARY)
|
||||
endif ()
|
||||
|
||||
if (USE_LDAP)
|
||||
dbms_target_include_directories (SYSTEM BEFORE PRIVATE ${OPENLDAP_INCLUDE_DIR})
|
||||
dbms_target_include_directories (SYSTEM BEFORE PRIVATE ${OPENLDAP_INCLUDE_DIRS})
|
||||
dbms_target_link_libraries (PRIVATE ${OPENLDAP_LIBRARIES})
|
||||
endif ()
|
||||
dbms_target_include_directories (SYSTEM BEFORE PRIVATE ${SPARSEHASH_INCLUDE_DIR})
|
||||
|
@ -498,6 +498,7 @@ namespace ErrorCodes
|
||||
extern const int NOT_A_LEADER = 529;
|
||||
extern const int CANNOT_CONNECT_RABBITMQ = 530;
|
||||
extern const int CANNOT_FSTAT = 531;
|
||||
extern const int LDAP_ERROR = 532;
|
||||
|
||||
extern const int KEEPER_EXCEPTION = 999;
|
||||
extern const int POCO_EXCEPTION = 1000;
|
||||
|
@ -10,3 +10,4 @@
|
||||
#cmakedefine01 USE_INTERNAL_LLVM_LIBRARY
|
||||
#cmakedefine01 USE_SSL
|
||||
#cmakedefine01 USE_OPENCL
|
||||
#cmakedefine01 USE_LDAP
|
||||
|
@ -621,6 +621,7 @@ void Context::setConfig(const ConfigurationPtr & config)
|
||||
{
|
||||
auto lock = getLock();
|
||||
shared->config = config;
|
||||
shared->access_control_manager.setExternalAuthenticatorsConfig(*shared->config);
|
||||
}
|
||||
|
||||
const Poco::Util::AbstractConfiguration & Context::getConfigRef() const
|
||||
@ -640,6 +641,11 @@ const AccessControlManager & Context::getAccessControlManager() const
|
||||
return shared->access_control_manager;
|
||||
}
|
||||
|
||||
void Context::setExternalAuthenticatorsConfig(const Poco::Util::AbstractConfiguration & config)
|
||||
{
|
||||
auto lock = getLock();
|
||||
shared->access_control_manager.setExternalAuthenticatorsConfig(config);
|
||||
}
|
||||
|
||||
void Context::setUsersConfig(const ConfigurationPtr & config)
|
||||
{
|
||||
|
@ -245,6 +245,9 @@ public:
|
||||
AccessControlManager & getAccessControlManager();
|
||||
const AccessControlManager & getAccessControlManager() const;
|
||||
|
||||
/// Sets external authenticators config (LDAP).
|
||||
void setExternalAuthenticatorsConfig(const Poco::Util::AbstractConfiguration & config);
|
||||
|
||||
/** Take the list of users, quotas and configuration profiles from this config.
|
||||
* The list of users is completely replaced.
|
||||
* The accumulated quota values are not reset if the quota is not deleted.
|
||||
|
@ -33,27 +33,32 @@ namespace
|
||||
}
|
||||
|
||||
String authentication_type_name = Authentication::TypeInfo::get(authentication_type).name;
|
||||
std::optional<String> password;
|
||||
std::optional<String> by_value;
|
||||
|
||||
if (show_password)
|
||||
if (show_password || authentication_type == Authentication::LDAP_SERVER)
|
||||
{
|
||||
switch (authentication_type)
|
||||
{
|
||||
case Authentication::PLAINTEXT_PASSWORD:
|
||||
{
|
||||
password = authentication.getPassword();
|
||||
by_value = authentication.getPassword();
|
||||
break;
|
||||
}
|
||||
case Authentication::SHA256_PASSWORD:
|
||||
{
|
||||
authentication_type_name = "sha256_hash";
|
||||
password = authentication.getPasswordHashHex();
|
||||
by_value = authentication.getPasswordHashHex();
|
||||
break;
|
||||
}
|
||||
case Authentication::DOUBLE_SHA1_PASSWORD:
|
||||
{
|
||||
authentication_type_name = "double_sha1_hash";
|
||||
password = authentication.getPasswordHashHex();
|
||||
by_value = authentication.getPasswordHashHex();
|
||||
break;
|
||||
}
|
||||
case Authentication::LDAP_SERVER:
|
||||
{
|
||||
by_value = authentication.getServerName();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -65,9 +70,9 @@ namespace
|
||||
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " IDENTIFIED WITH " << authentication_type_name
|
||||
<< (settings.hilite ? IAST::hilite_none : "");
|
||||
if (password)
|
||||
if (by_value)
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " BY " << (settings.hilite ? IAST::hilite_none : "")
|
||||
<< quoteString(*password);
|
||||
<< quoteString(*by_value);
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,14 +13,14 @@ class ASTRolesOrUsersSet;
|
||||
class ASTSettingsProfileElements;
|
||||
|
||||
/** CREATE USER [IF NOT EXISTS | OR REPLACE] name
|
||||
* [NOT IDENTIFIED | IDENTIFIED [WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash}] BY {'password'|'hash'}]
|
||||
* [NOT IDENTIFIED | IDENTIFIED [WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash|ldap_server}] BY {'password'|'hash'|'server_name'}]
|
||||
* [HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
* [DEFAULT ROLE role [,...]]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
*
|
||||
* ALTER USER [IF EXISTS] name
|
||||
* [RENAME TO new_name]
|
||||
* [NOT IDENTIFIED | IDENTIFIED [WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash}] BY {'password'|'hash'}]
|
||||
* [NOT IDENTIFIED | IDENTIFIED [WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash|ldap_server}] BY {'password'|'hash'|'server_name'}]
|
||||
* [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
* [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
|
@ -49,6 +49,7 @@ namespace
|
||||
std::optional<Authentication::Type> type;
|
||||
bool expect_password = false;
|
||||
bool expect_hash = false;
|
||||
bool expect_server_name = false;
|
||||
|
||||
if (ParserKeyword{"WITH"}.ignore(pos, expected))
|
||||
{
|
||||
@ -57,7 +58,12 @@ namespace
|
||||
if (ParserKeyword{Authentication::TypeInfo::get(check_type).raw_name}.ignore(pos, expected))
|
||||
{
|
||||
type = check_type;
|
||||
expect_password = (check_type != Authentication::NO_PASSWORD);
|
||||
|
||||
if (check_type == Authentication::LDAP_SERVER)
|
||||
expect_server_name = true;
|
||||
else if (check_type != Authentication::NO_PASSWORD)
|
||||
expect_password = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -85,21 +91,23 @@ namespace
|
||||
expect_password = true;
|
||||
}
|
||||
|
||||
String password;
|
||||
if (expect_password || expect_hash)
|
||||
String value;
|
||||
if (expect_password || expect_hash || expect_server_name)
|
||||
{
|
||||
ASTPtr ast;
|
||||
if (!ParserKeyword{"BY"}.ignore(pos, expected) || !ParserStringLiteral{}.parse(pos, ast, expected))
|
||||
return false;
|
||||
|
||||
password = ast->as<const ASTLiteral &>().value.safeGet<String>();
|
||||
value = ast->as<const ASTLiteral &>().value.safeGet<String>();
|
||||
}
|
||||
|
||||
authentication = Authentication{*type};
|
||||
if (expect_password)
|
||||
authentication.setPassword(password);
|
||||
authentication.setPassword(value);
|
||||
else if (expect_hash)
|
||||
authentication.setPasswordHashHex(password);
|
||||
authentication.setPasswordHashHex(value);
|
||||
else if (expect_server_name)
|
||||
authentication.setServerName(value);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
@ -7,13 +7,13 @@ namespace DB
|
||||
{
|
||||
/** Parses queries like
|
||||
* CREATE USER [IF NOT EXISTS | OR REPLACE] name
|
||||
* [NOT IDENTIFIED | IDENTIFIED [WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash}] BY {'password'|'hash'}]
|
||||
* [NOT IDENTIFIED | IDENTIFIED [WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash|ldap_server}] BY {'password'|'hash'|'server_name'}]
|
||||
* [HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
*
|
||||
* ALTER USER [IF EXISTS] name
|
||||
* [RENAME TO new_name]
|
||||
* [NOT IDENTIFIED | IDENTIFIED [WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash}] BY {'password'|'hash'}]
|
||||
* [NOT IDENTIFIED | IDENTIFIED [WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash|ldap_server}] BY {'password'|'hash'|'server_name'}]
|
||||
* [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
*/
|
||||
|
@ -230,7 +230,7 @@ void MySQLHandler::authenticate(const String & user_name, const String & auth_pl
|
||||
// For compatibility with JavaScript MySQL client, Native41 authentication plugin is used when possible (if password is specified using double SHA1). Otherwise SHA256 plugin is used.
|
||||
auto user = connection_context.getAccessControlManager().read<User>(user_name);
|
||||
const DB::Authentication::Type user_auth_type = user->authentication.getType();
|
||||
if (user_auth_type != DB::Authentication::DOUBLE_SHA1_PASSWORD && user_auth_type != DB::Authentication::PLAINTEXT_PASSWORD && user_auth_type != DB::Authentication::NO_PASSWORD)
|
||||
if (user_auth_type == DB::Authentication::SHA256_PASSWORD)
|
||||
{
|
||||
authPluginSSL();
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ const char * auto_config_build[]
|
||||
"USE_HYPERSCAN", "@ENABLE_HYPERSCAN@",
|
||||
"USE_SIMDJSON", "@USE_SIMDJSON@",
|
||||
"USE_GRPC", "@USE_GRPC@",
|
||||
"USE_LDAP", "@USE_LDAP@",
|
||||
|
||||
nullptr, nullptr
|
||||
};
|
||||
|
@ -12,6 +12,10 @@
|
||||
#include <Access/AccessControlManager.h>
|
||||
#include <Access/User.h>
|
||||
#include <Access/AccessFlags.h>
|
||||
#include <Poco/JSON/JSON.h>
|
||||
#include <Poco/JSON/Object.h>
|
||||
#include <Poco/JSON/Stringifier.h>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -35,7 +39,7 @@ NamesAndTypesList StorageSystemUsers::getNamesAndTypes()
|
||||
{"id", std::make_shared<DataTypeUUID>()},
|
||||
{"storage", std::make_shared<DataTypeString>()},
|
||||
{"auth_type", std::make_shared<DataTypeEnum8>(getAuthenticationTypeEnumValues())},
|
||||
{"auth_params", std::make_shared<DataTypeArray>(std::make_shared<DataTypeString>())},
|
||||
{"auth_params", std::make_shared<DataTypeString>()},
|
||||
{"host_ip", std::make_shared<DataTypeArray>(std::make_shared<DataTypeString>())},
|
||||
{"host_names", std::make_shared<DataTypeArray>(std::make_shared<DataTypeString>())},
|
||||
{"host_names_regexp", std::make_shared<DataTypeArray>(std::make_shared<DataTypeString>())},
|
||||
@ -59,8 +63,7 @@ void StorageSystemUsers::fillData(MutableColumns & res_columns, const Context &
|
||||
auto & column_id = assert_cast<ColumnUInt128 &>(*res_columns[column_index++]).getData();
|
||||
auto & column_storage = assert_cast<ColumnString &>(*res_columns[column_index++]);
|
||||
auto & column_auth_type = assert_cast<ColumnInt8 &>(*res_columns[column_index++]).getData();
|
||||
auto & column_auth_params = assert_cast<ColumnString &>(assert_cast<ColumnArray &>(*res_columns[column_index]).getData());
|
||||
auto & column_auth_params_offsets = assert_cast<ColumnArray &>(*res_columns[column_index++]).getOffsets();
|
||||
auto & column_auth_params = assert_cast<ColumnString &>(*res_columns[column_index++]);
|
||||
auto & column_host_ip = assert_cast<ColumnString &>(assert_cast<ColumnArray &>(*res_columns[column_index]).getData());
|
||||
auto & column_host_ip_offsets = assert_cast<ColumnArray &>(*res_columns[column_index++]).getOffsets();
|
||||
auto & column_host_names = assert_cast<ColumnString &>(assert_cast<ColumnArray &>(*res_columns[column_index]).getData());
|
||||
@ -86,7 +89,24 @@ void StorageSystemUsers::fillData(MutableColumns & res_columns, const Context &
|
||||
column_id.push_back(id);
|
||||
column_storage.insertData(storage_name.data(), storage_name.length());
|
||||
column_auth_type.push_back(static_cast<Int8>(authentication.getType()));
|
||||
column_auth_params_offsets.push_back(column_auth_params.size());
|
||||
|
||||
if (authentication.getType() == Authentication::Type::LDAP_SERVER)
|
||||
{
|
||||
Poco::JSON::Object auth_params_json;
|
||||
|
||||
auth_params_json.set("server", authentication.getServerName());
|
||||
|
||||
std::ostringstream oss;
|
||||
Poco::JSON::Stringifier::stringify(auth_params_json, oss);
|
||||
const auto str = oss.str();
|
||||
|
||||
column_auth_params.insertData(str.data(), str.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
static constexpr std::string_view empty_json{"{}"};
|
||||
column_auth_params.insertData(empty_json.data(), empty_json.length());
|
||||
}
|
||||
|
||||
if (allowed_hosts.containsAnyHost())
|
||||
{
|
||||
|
@ -200,8 +200,8 @@ def test_introspection():
|
||||
assert expected_access2 in instance.query("SHOW ACCESS")
|
||||
|
||||
assert instance.query("SELECT name, storage, auth_type, auth_params, host_ip, host_names, host_names_regexp, host_names_like, default_roles_all, default_roles_list, default_roles_except from system.users WHERE name IN ('A', 'B') ORDER BY name") ==\
|
||||
TSV([[ "A", "disk", "no_password", "[]", "['::/0']", "[]", "[]", "[]", 1, "[]", "[]" ],
|
||||
[ "B", "disk", "no_password", "[]", "['::/0']", "[]", "[]", "[]", 1, "[]", "[]" ]])
|
||||
TSV([[ "A", "disk", "no_password", "{}", "['::/0']", "[]", "[]", "[]", 1, "[]", "[]" ],
|
||||
[ "B", "disk", "no_password", "{}", "['::/0']", "[]", "[]", "[]", 1, "[]", "[]" ]])
|
||||
|
||||
assert instance.query("SELECT * from system.grants WHERE user_name IN ('A', 'B') ORDER BY user_name, access_type, grant_option") ==\
|
||||
TSV([[ "A", "\N", "SELECT", "test", "table", "\N", 0, 0 ],
|
||||
|
@ -95,10 +95,10 @@ CREATE USER u2_01292 DEFAULT ROLE r1_01292, r2_01292 SETTINGS readonly = 1
|
||||
CREATE USER u3_01292 HOST LIKE \'%.%.myhost.com\' DEFAULT ROLE r1_01292, r2_01292
|
||||
CREATE USER u4_01292 HOST LIKE \'%.%.myhost.com\' DEFAULT ROLE r1_01292, r2_01292
|
||||
-- system.users
|
||||
u1_01292 disk plaintext_password [] [] ['localhost'] [] [] 1 [] []
|
||||
u2_01292 disk no_password [] [] [] [] ['%.%.myhost.com'] 0 [] []
|
||||
u3_01292 disk sha256_password [] ['192.169.1.1','192.168.0.0/16'] ['localhost'] [] [] 0 ['r1_01292'] []
|
||||
u4_01292 disk double_sha1_password [] ['::/0'] [] [] [] 1 [] ['r1_01292']
|
||||
u1_01292 disk plaintext_password {} [] ['localhost'] [] [] 1 [] []
|
||||
u2_01292 disk no_password {} [] [] [] ['%.%.myhost.com'] 0 [] []
|
||||
u3_01292 disk sha256_password {} ['192.169.1.1','192.168.0.0/16'] ['localhost'] [] [] 0 ['r1_01292'] []
|
||||
u4_01292 disk double_sha1_password {} ['::/0'] [] [] [] 1 [] ['r1_01292']
|
||||
-- system.settings_profile_elements
|
||||
\N u1_01292 \N 0 readonly 1 \N \N \N \N
|
||||
\N u2_01292 \N 0 \N \N \N \N \N default
|
||||
|
@ -167,6 +167,16 @@ class Cluster(object):
|
||||
self.docker_compose += f" --project-directory \"{docker_compose_project_dir}\" --file \"{docker_compose_file_path}\""
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def shell(self, node):
|
||||
"""Returns unique shell terminal to be used.
|
||||
"""
|
||||
if node is None:
|
||||
return Shell()
|
||||
|
||||
return Shell(command=[
|
||||
"/bin/bash", "--noediting", "-c", f"{self.docker_compose} exec {node} bash --noediting"
|
||||
], name=node)
|
||||
|
||||
def bash(self, node, timeout=60):
|
||||
"""Returns thread-local bash terminal
|
||||
to a specific node.
|
||||
|
0
tests/testflows/ldap/__init__.py
Normal file
0
tests/testflows/ldap/__init__.py
Normal file
22
tests/testflows/ldap/configs/CA/ca.crt
Normal file
22
tests/testflows/ldap/configs/CA/ca.crt
Normal file
@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDlTCCAn2gAwIBAgIUJBqw2dHM2DDCZjYSkPOESlvDH6swDQYJKoZIhvcNAQEL
|
||||
BQAwWjELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMQ8wDQYDVQQHDAZPdHRhd2Ex
|
||||
ETAPBgNVBAoMCEFsdGluaXR5MQswCQYDVQQLDAJRQTENMAsGA1UEAwwEcm9vdDAe
|
||||
Fw0yMDA2MTExOTAzNDhaFw0zMDA2MDkxOTAzNDhaMFoxCzAJBgNVBAYTAkNBMQsw
|
||||
CQYDVQQIDAJPTjEPMA0GA1UEBwwGT3R0YXdhMREwDwYDVQQKDAhBbHRpbml0eTEL
|
||||
MAkGA1UECwwCUUExDTALBgNVBAMMBHJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQC9Irr0zGV+HCI2fZ0ht4hR5It4Sbjz4RwZV8ENRP/+TEz8l9eK
|
||||
J6ygxhKX7SMYzIs/jS9Gsq4plX1r2ujW1qRf8yLpR4+dGLP+jBRi1drj0XjZXosT
|
||||
SERjWzgPauWxL9LN8+l26eBAqz6fw5e0W8WRSTgf5iGiCcKOTmaATIUjP0CdfWKK
|
||||
qpktI4vhe++CXZFJ3usR+8KZ/FwwbCLJM/3J2HnbcXfcaYPYvr1tfqLudKSTbG9H
|
||||
M3+AVwjctdesc/0sbd51Zsm0ClQptMbuKnDCYauGg61kNkgbgPgRmH9Pzo67DtxF
|
||||
/WW+PtOzq8xLOifciQ9Piboy9QBSQZGwf4wzAgMBAAGjUzBRMB0GA1UdDgQWBBSi
|
||||
njya0RDozx3OZTLYFpwqYnlpIDAfBgNVHSMEGDAWgBSinjya0RDozx3OZTLYFpwq
|
||||
YnlpIDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBAD7VyFg7F
|
||||
U1C25KFvtauchAOjCW6w7U/b3z1dVZvcQ88/kH1VsLUcfGixlSilUEfPTJsi7OA0
|
||||
R5BQdh2GGcjUJv4iqEFGU05KvMVmRRKn08P62+ZhJxKMxG26VzcliRZzCMkI6d0W
|
||||
lFwI6nM45yeqdHVh5k4xbuJzqpbD9BtXXLI+/Ra9Fx8S9ETA3GdidpZLU5P1VLxq
|
||||
UuedfqyAVWZXpr6TAURGxouRmRzul9yFzbSUex+MLEIPrstjtEwV3+tBQZJz9xAS
|
||||
TVPj+Nv3LO7GCq54bdwkq1ioWbSL2hEmABkj6kdW/JwmfhGHf/2rirDVMzrTYw07
|
||||
dFJfAZC+FEsv
|
||||
-----END CERTIFICATE-----
|
30
tests/testflows/ldap/configs/CA/ca.key
Normal file
30
tests/testflows/ldap/configs/CA/ca.key
Normal file
@ -0,0 +1,30 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-256-CBC,D06B9754A2069EBB4E77065DC9B605A1
|
||||
|
||||
FJT794Z6AUuUB5Vp5W2iR6zzCvQUg2dtKoE+xhFdbgC7lmSfA2W/O9fx15Il67Yj
|
||||
Bbpm9Y6yteUSDQpJrvBdkhXeBkYEa5K1CA+0Jdx98nzwP3KBhHNxVVrTWRc5kniB
|
||||
LMV3iBQEbAafxgL7gN+EWr3eV7w7ZSqT7D5br/mlBALU62gv2UzwTXLu1CgyNWMC
|
||||
HIPjIX50Zga+BnhZhtQvM4Yj1gOsn+X6AaEZ3KjTfCDqthYQf2ldswW4gAlPAq83
|
||||
+INq9Spx+QG97Z+1XO2DmmGTZL0z+OFLT+3y26/UcftM26ODY09Dcf3gt0n6RIUV
|
||||
0KsD1qQL0ppu4CHVnbIkOKMBe86qBl+kG8FVmyhgZ8D9ULlF1tpyTVKvHR82V2B5
|
||||
ztbc5EY1Fhb+r7OVVJlbCeo/bWmWybODZrpN49x5gGZpM3+8ApaHupGZ+cRFkQKG
|
||||
rDpqC5gflT3WwFNxtP5noWcV+Gzb3riXNM3c8G5aIpLZwmmaTLK9ahKqMcq4Ljf+
|
||||
hir8kuCMqIKt3m7Ceoj4wAHSP8xO0y/cc1WYNb3CI0Emk795aR6IPUw4vDEXHG27
|
||||
OLoCJTvl/JKRWJGkdQx8wKAs/uw/qwtbhYoQJccTjfvy4NXH3tpSgxCE8OTWuEch
|
||||
TAN8ra1PDGAUu+1MeT5gZ9uI1BEU6hXMME4mVRpJdcmw9MVy3V+B6rkUqX3kFAfR
|
||||
e2ueF5qgIp+A4/UlVe5cKdWAQxu4BnUESLooA7cbgcLypdao9bRx9bXH8S3aNgxW
|
||||
IdgICpc/v8wAX2yqMe191KgR9Vh1p0RCw/kEGVgWfY/IaQpsaYuq5quZbvr/fN5T
|
||||
d++ySAMaPysaCadLUdZJLw56uk4Y+PYzR+ygjTX9dCCHedrAU8RYM55FJ/fyD3bQ
|
||||
Hn9/n7PZyWy6u/TYt6dhlcYxaS3Opzw4eAQB8tGZJRYQ3AKpHpTEC57lXoMnUPKo
|
||||
+nBmb0+YulylMZdns0WIBJlcv6qzIaNhDMrjyi18n1ezzPIGH7ivUjoXy2FL23q5
|
||||
f3aqJK4UUDEDkC8IeZkS+ykYxnohjFDhUyBe5gjryLqdMdy9EerehCWPf425AztX
|
||||
c/EWPzDl46qmxWhugOlz3Fiw95VlYu0MUDRayHuZiYPplgJypChuU4EHJ+q8V2z3
|
||||
BwjSo1bD4nfc8f68qEOtdZ1u/ClcolMwlZQYDJz/DiE4JOcd2Gx4QSF5vaInm0/4
|
||||
mMj/ZWna4DAYFbH8IGh7xUPDqeIWhBYlgrD69ajKyay5Vu3La/d2QW20BhX35Ro2
|
||||
ZJVR+lfioMmxn4y481H2pv+5gOlGwh02Oa8qLhZBb8W+DvFShNk6mk87eCForFFT
|
||||
CDgmvfsC/cS2wZkcFTecq6vbjFlt+OF13NCKlcO3wCm44D+bwVPeMrU6HycCVQw7
|
||||
SASrnP/th5sJbv11byb2lKgVdVHWk090bqnDwB9H2hGIb9JnPC9ZpaL/mocYyzTi
|
||||
H9fcBrMYkL09FJGr3Uff7qEY4XQTMlLadXue3iKd19PRgV8cRyKp37MYI9/3iLwv
|
||||
eYHLtMfrifZahf1ksOPeBphnlfzWo9qqfooUCaGxfSlNPUHhrHZ4aMiRyTE8Xeh2
|
||||
-----END RSA PRIVATE KEY-----
|
1
tests/testflows/ldap/configs/CA/ca.srl
Normal file
1
tests/testflows/ldap/configs/CA/ca.srl
Normal file
@ -0,0 +1 @@
|
||||
227B125D27B6B1A4B5955361365DF8EC2D7098C1
|
5
tests/testflows/ldap/configs/CA/dhparam.pem
Normal file
5
tests/testflows/ldap/configs/CA/dhparam.pem
Normal file
@ -0,0 +1,5 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MIGHAoGBAJitt2hhnpDViQ5ko2ipBMdjy+bZ6FR/WdZ987R7lQvBkKehPXmxtEyV
|
||||
AO6ofv5CZSDJokc5bUeBOAtg0EhMTCH82uPdwQvt58jRXcxXBg4JTjkx+oW9LBv2
|
||||
FdZsbaX8+SYivmiZ0Jp8T/HBm/4DA9VBS0O5GFRS4C7dHhmSTPfDAgEC
|
||||
-----END DH PARAMETERS-----
|
1
tests/testflows/ldap/configs/CA/passphrase.txt
Normal file
1
tests/testflows/ldap/configs/CA/passphrase.txt
Normal file
@ -0,0 +1 @@
|
||||
altinity
|
6
tests/testflows/ldap/configs/clickhouse/common.xml
Normal file
6
tests/testflows/ldap/configs/clickhouse/common.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<yandex>
|
||||
<timezone>Europe/Moscow</timezone>
|
||||
<listen_host replace="replace">0.0.0.0</listen_host>
|
||||
<path>/var/lib/clickhouse/</path>
|
||||
<tmp_path>/var/lib/clickhouse/tmp/</tmp_path>
|
||||
</yandex>
|
17
tests/testflows/ldap/configs/clickhouse/config.d/logs.xml
Normal file
17
tests/testflows/ldap/configs/clickhouse/config.d/logs.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<yandex>
|
||||
<shutdown_wait_unfinished>3</shutdown_wait_unfinished>
|
||||
<logger>
|
||||
<level>trace</level>
|
||||
<log>/var/log/clickhouse-server/log.log</log>
|
||||
<errorlog>/var/log/clickhouse-server/log.err.log</errorlog>
|
||||
<size>1000M</size>
|
||||
<count>10</count>
|
||||
<stderr>/var/log/clickhouse-server/stderr.log</stderr>
|
||||
<stdout>/var/log/clickhouse-server/stdout.log</stdout>
|
||||
</logger>
|
||||
<part_log>
|
||||
<database>system</database>
|
||||
<table>part_log</table>
|
||||
<flush_interval_milliseconds>500</flush_interval_milliseconds>
|
||||
</part_log>
|
||||
</yandex>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<yandex>
|
||||
<https_port>8443</https_port>
|
||||
<tcp_port_secure>9440</tcp_port_secure>
|
||||
</yandex>
|
107
tests/testflows/ldap/configs/clickhouse/config.d/remote.xml
Normal file
107
tests/testflows/ldap/configs/clickhouse/config.d/remote.xml
Normal file
@ -0,0 +1,107 @@
|
||||
<?xml version="1.0"?>
|
||||
<yandex>
|
||||
<remote_servers>
|
||||
<replicated_cluster>
|
||||
<shard>
|
||||
<internal_replication>true</internal_replication>
|
||||
<replica>
|
||||
<host>clickhouse1</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
<replica>
|
||||
<host>clickhouse2</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
<replica>
|
||||
<host>clickhouse3</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
</replicated_cluster>
|
||||
<!--
|
||||
<replicated_cluster_readonly>
|
||||
<shard>
|
||||
<internal_replication>true</internal_replication>
|
||||
<replica>
|
||||
<host>clickhouse1</host>
|
||||
<port>9000</port>
|
||||
<user>readonly</user>
|
||||
</replica>
|
||||
<replica>
|
||||
<host>clickhouse2</host>
|
||||
<port>9000</port>
|
||||
<user>readonly</user>
|
||||
</replica>
|
||||
<replica>
|
||||
<host>clickhouse3</host>
|
||||
<port>9000</port>
|
||||
<user>readonly</user>
|
||||
</replica>
|
||||
</shard>
|
||||
</replicated_cluster_readonly>
|
||||
-->
|
||||
<replicated_cluster_secure>
|
||||
<shard>
|
||||
<internal_replication>true</internal_replication>
|
||||
<replica>
|
||||
<host>clickhouse1</host>
|
||||
<port>9440</port>
|
||||
<secure>1</secure>
|
||||
</replica>
|
||||
<replica>
|
||||
<host>clickhouse2</host>
|
||||
<port>9440</port>
|
||||
<secure>1</secure>
|
||||
</replica>
|
||||
<replica>
|
||||
<host>clickhouse3</host>
|
||||
<port>9440</port>
|
||||
<secure>1</secure>
|
||||
</replica>
|
||||
</shard>
|
||||
</replicated_cluster_secure>
|
||||
<sharded_cluster>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>clickhouse1</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>clickhouse2</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>clickhouse3</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
</sharded_cluster>
|
||||
<sharded_cluster_secure>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>clickhouse1</host>
|
||||
<port>9440</port>
|
||||
<secure>1</secure>
|
||||
</replica>
|
||||
</shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>clickhouse2</host>
|
||||
<port>9440</port>
|
||||
<secure>1</secure>
|
||||
</replica>
|
||||
</shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>clickhouse3</host>
|
||||
<port>9440</port>
|
||||
<secure>1</secure>
|
||||
</replica>
|
||||
</shard>
|
||||
</sharded_cluster_secure>
|
||||
</remote_servers>
|
||||
</yandex>
|
17
tests/testflows/ldap/configs/clickhouse/config.d/ssl.xml
Normal file
17
tests/testflows/ldap/configs/clickhouse/config.d/ssl.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<yandex>
|
||||
<openSSL>
|
||||
<server>
|
||||
<certificateFile>/etc/clickhouse-server/ssl/server.crt</certificateFile>
|
||||
<privateKeyFile>/etc/clickhouse-server/ssl/server.key</privateKeyFile>
|
||||
<verificationMode>none</verificationMode>
|
||||
<cacheSessions>true</cacheSessions>
|
||||
</server>
|
||||
<client>
|
||||
<cacheSessions>true</cacheSessions>
|
||||
<verificationMode>none</verificationMode>
|
||||
<invalidCertificateHandler>
|
||||
<name>AcceptCertificateHandler</name>
|
||||
</invalidCertificateHandler>
|
||||
</client>
|
||||
</openSSL>
|
||||
</yandex>
|
20
tests/testflows/ldap/configs/clickhouse/config.d/storage.xml
Normal file
20
tests/testflows/ldap/configs/clickhouse/config.d/storage.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<yandex>
|
||||
|
||||
<storage_configuration>
|
||||
<disks>
|
||||
<default>
|
||||
<keep_free_space_bytes>1024</keep_free_space_bytes>
|
||||
</default>
|
||||
</disks>
|
||||
<policies>
|
||||
<default>
|
||||
<volumes>
|
||||
<default>
|
||||
<disk>default</disk>
|
||||
</default>
|
||||
</volumes>
|
||||
</default>
|
||||
</policies>
|
||||
</storage_configuration>
|
||||
|
||||
</yandex>
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<yandex>
|
||||
<zookeeper>
|
||||
<node index="1">
|
||||
<host>zookeeper</host>
|
||||
<port>2181</port>
|
||||
</node>
|
||||
<session_timeout_ms>15000</session_timeout_ms>
|
||||
</zookeeper>
|
||||
</yandex>
|
436
tests/testflows/ldap/configs/clickhouse/config.xml
Normal file
436
tests/testflows/ldap/configs/clickhouse/config.xml
Normal file
@ -0,0 +1,436 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
NOTE: User and query level settings are set up in "users.xml" file.
|
||||
-->
|
||||
<yandex>
|
||||
<logger>
|
||||
<!-- Possible levels: https://github.com/pocoproject/poco/blob/develop/Foundation/include/Poco/Logger.h#L105 -->
|
||||
<level>trace</level>
|
||||
<log>/var/log/clickhouse-server/clickhouse-server.log</log>
|
||||
<errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
|
||||
<size>1000M</size>
|
||||
<count>10</count>
|
||||
<!-- <console>1</console> --> <!-- Default behavior is autodetection (log to console if not daemon mode and is tty) -->
|
||||
</logger>
|
||||
<!--display_name>production</display_name--> <!-- It is the name that will be shown in the client -->
|
||||
<http_port>8123</http_port>
|
||||
<tcp_port>9000</tcp_port>
|
||||
|
||||
<!-- For HTTPS and SSL over native protocol. -->
|
||||
<!--
|
||||
<https_port>8443</https_port>
|
||||
<tcp_port_secure>9440</tcp_port_secure>
|
||||
-->
|
||||
|
||||
<!-- Used with https_port and tcp_port_secure. Full ssl options list: https://github.com/ClickHouse-Extras/poco/blob/master/NetSSL_OpenSSL/include/Poco/Net/SSLManager.h#L71 -->
|
||||
<openSSL>
|
||||
<server> <!-- Used for https server AND secure tcp port -->
|
||||
<!-- openssl req -subj "/CN=localhost" -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout /etc/clickhouse-server/server.key -out /etc/clickhouse-server/server.crt -->
|
||||
<certificateFile>/etc/clickhouse-server/server.crt</certificateFile>
|
||||
<privateKeyFile>/etc/clickhouse-server/server.key</privateKeyFile>
|
||||
<!-- openssl dhparam -out /etc/clickhouse-server/dhparam.pem 4096 -->
|
||||
<dhParamsFile>/etc/clickhouse-server/dhparam.pem</dhParamsFile>
|
||||
<verificationMode>none</verificationMode>
|
||||
<loadDefaultCAFile>true</loadDefaultCAFile>
|
||||
<cacheSessions>true</cacheSessions>
|
||||
<disableProtocols>sslv2,sslv3</disableProtocols>
|
||||
<preferServerCiphers>true</preferServerCiphers>
|
||||
</server>
|
||||
|
||||
<client> <!-- Used for connecting to https dictionary source -->
|
||||
<loadDefaultCAFile>true</loadDefaultCAFile>
|
||||
<cacheSessions>true</cacheSessions>
|
||||
<disableProtocols>sslv2,sslv3</disableProtocols>
|
||||
<preferServerCiphers>true</preferServerCiphers>
|
||||
<!-- Use for self-signed: <verificationMode>none</verificationMode> -->
|
||||
<invalidCertificateHandler>
|
||||
<!-- Use for self-signed: <name>AcceptCertificateHandler</name> -->
|
||||
<name>RejectCertificateHandler</name>
|
||||
</invalidCertificateHandler>
|
||||
</client>
|
||||
</openSSL>
|
||||
|
||||
<!-- Default root page on http[s] server. For example load UI from https://tabix.io/ when opening http://localhost:8123 -->
|
||||
<!--
|
||||
<http_server_default_response><![CDATA[<html ng-app="SMI2"><head><base href="http://ui.tabix.io/"></head><body><div ui-view="" class="content-ui"></div><script src="http://loader.tabix.io/master.js"></script></body></html>]]></http_server_default_response>
|
||||
-->
|
||||
|
||||
<!-- Port for communication between replicas. Used for data exchange. -->
|
||||
<interserver_http_port>9009</interserver_http_port>
|
||||
|
||||
<!-- Hostname that is used by other replicas to request this server.
|
||||
If not specified, than it is determined analoguous to 'hostname -f' command.
|
||||
This setting could be used to switch replication to another network interface.
|
||||
-->
|
||||
<!--
|
||||
<interserver_http_host>example.yandex.ru</interserver_http_host>
|
||||
-->
|
||||
|
||||
<!-- Listen specified host. use :: (wildcard IPv6 address), if you want to accept connections both with IPv4 and IPv6 from everywhere. -->
|
||||
<!-- <listen_host>::</listen_host> -->
|
||||
<!-- Same for hosts with disabled ipv6: -->
|
||||
<!-- <listen_host>0.0.0.0</listen_host> -->
|
||||
|
||||
<!-- Default values - try listen localhost on ipv4 and ipv6: -->
|
||||
<!--
|
||||
<listen_host>::1</listen_host>
|
||||
<listen_host>127.0.0.1</listen_host>
|
||||
-->
|
||||
<!-- Don't exit if ipv6 or ipv4 unavailable, but listen_host with this protocol specified -->
|
||||
<!-- <listen_try>0</listen_try> -->
|
||||
|
||||
<!-- Allow listen on same address:port -->
|
||||
<!-- <listen_reuse_port>0</listen_reuse_port> -->
|
||||
|
||||
<!-- <listen_backlog>64</listen_backlog> -->
|
||||
|
||||
<max_connections>4096</max_connections>
|
||||
<keep_alive_timeout>3</keep_alive_timeout>
|
||||
|
||||
<!-- Maximum number of concurrent queries. -->
|
||||
<max_concurrent_queries>100</max_concurrent_queries>
|
||||
|
||||
<!-- Set limit on number of open files (default: maximum). This setting makes sense on Mac OS X because getrlimit() fails to retrieve
|
||||
correct maximum value. -->
|
||||
<!-- <max_open_files>262144</max_open_files> -->
|
||||
|
||||
<!-- Size of cache of uncompressed blocks of data, used in tables of MergeTree family.
|
||||
In bytes. Cache is single for server. Memory is allocated only on demand.
|
||||
Cache is used when 'use_uncompressed_cache' user setting turned on (off by default).
|
||||
Uncompressed cache is advantageous only for very short queries and in rare cases.
|
||||
-->
|
||||
<uncompressed_cache_size>8589934592</uncompressed_cache_size>
|
||||
|
||||
<!-- Approximate size of mark cache, used in tables of MergeTree family.
|
||||
In bytes. Cache is single for server. Memory is allocated only on demand.
|
||||
You should not lower this value.
|
||||
-->
|
||||
<mark_cache_size>5368709120</mark_cache_size>
|
||||
|
||||
|
||||
<!-- Path to data directory, with trailing slash. -->
|
||||
<path>/var/lib/clickhouse/</path>
|
||||
|
||||
<!-- Path to temporary data for processing hard queries. -->
|
||||
<tmp_path>/var/lib/clickhouse/tmp/</tmp_path>
|
||||
|
||||
<!-- Directory with user provided files that are accessible by 'file' table function. -->
|
||||
<user_files_path>/var/lib/clickhouse/user_files/</user_files_path>
|
||||
|
||||
<!-- Path to folder where users and roles created by SQL commands are stored. -->
|
||||
<access_control_path>/var/lib/clickhouse/access/</access_control_path>
|
||||
|
||||
<!-- Path to configuration file with users, access rights, profiles of settings, quotas. -->
|
||||
<users_config>users.xml</users_config>
|
||||
|
||||
<!-- Default profile of settings. -->
|
||||
<default_profile>default</default_profile>
|
||||
|
||||
<!-- System profile of settings. This settings are used by internal processes (Buffer storage, Distibuted DDL worker and so on). -->
|
||||
<!-- <system_profile>default</system_profile> -->
|
||||
|
||||
<!-- Default database. -->
|
||||
<default_database>default</default_database>
|
||||
|
||||
<!-- Server time zone could be set here.
|
||||
|
||||
Time zone is used when converting between String and DateTime types,
|
||||
when printing DateTime in text formats and parsing DateTime from text,
|
||||
it is used in date and time related functions, if specific time zone was not passed as an argument.
|
||||
|
||||
Time zone is specified as identifier from IANA time zone database, like UTC or Africa/Abidjan.
|
||||
If not specified, system time zone at server startup is used.
|
||||
|
||||
Please note, that server could display time zone alias instead of specified name.
|
||||
Example: W-SU is an alias for Europe/Moscow and Zulu is an alias for UTC.
|
||||
-->
|
||||
<!-- <timezone>Europe/Moscow</timezone> -->
|
||||
|
||||
<!-- You can specify umask here (see "man umask"). Server will apply it on startup.
|
||||
Number is always parsed as octal. Default umask is 027 (other users cannot read logs, data files, etc; group can only read).
|
||||
-->
|
||||
<!-- <umask>022</umask> -->
|
||||
|
||||
<!-- Perform mlockall after startup to lower first queries latency
|
||||
and to prevent clickhouse executable from being paged out under high IO load.
|
||||
Enabling this option is recommended but will lead to increased startup time for up to a few seconds.
|
||||
-->
|
||||
<mlock_executable>false</mlock_executable>
|
||||
|
||||
<!-- Configuration of clusters that could be used in Distributed tables.
|
||||
https://clickhouse.yandex/docs/en/table_engines/distributed/
|
||||
-->
|
||||
<remote_servers incl="remote" >
|
||||
<!-- Test only shard config for testing distributed storage -->
|
||||
<test_shard_localhost>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
</test_shard_localhost>
|
||||
<test_cluster_two_shards_localhost>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
</test_cluster_two_shards_localhost>
|
||||
<test_shard_localhost_secure>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>9440</port>
|
||||
<secure>1</secure>
|
||||
</replica>
|
||||
</shard>
|
||||
</test_shard_localhost_secure>
|
||||
<test_unavailable_shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>1</port>
|
||||
</replica>
|
||||
</shard>
|
||||
</test_unavailable_shard>
|
||||
</remote_servers>
|
||||
|
||||
|
||||
<!-- If element has 'incl' attribute, then for it's value will be used corresponding substitution from another file.
|
||||
By default, path to file with substitutions is /etc/metrika.xml. It could be changed in config in 'include_from' element.
|
||||
Values for substitutions are specified in /yandex/name_of_substitution elements in that file.
|
||||
-->
|
||||
|
||||
<!-- ZooKeeper is used to store metadata about replicas, when using Replicated tables.
|
||||
Optional. If you don't use replicated tables, you could omit that.
|
||||
|
||||
See https://clickhouse.yandex/docs/en/table_engines/replication/
|
||||
-->
|
||||
<zookeeper incl="zookeeper" optional="true" />
|
||||
|
||||
<!-- Substitutions for parameters of replicated tables.
|
||||
Optional. If you don't use replicated tables, you could omit that.
|
||||
|
||||
See https://clickhouse.yandex/docs/en/table_engines/replication/#creating-replicated-tables
|
||||
-->
|
||||
<macros incl="macros" optional="true" />
|
||||
|
||||
|
||||
<!-- Reloading interval for embedded dictionaries, in seconds. Default: 3600. -->
|
||||
<builtin_dictionaries_reload_interval>3600</builtin_dictionaries_reload_interval>
|
||||
|
||||
|
||||
<!-- Maximum session timeout, in seconds. Default: 3600. -->
|
||||
<max_session_timeout>3600</max_session_timeout>
|
||||
|
||||
<!-- Default session timeout, in seconds. Default: 60. -->
|
||||
<default_session_timeout>60</default_session_timeout>
|
||||
|
||||
<!-- Sending data to Graphite for monitoring. Several sections can be defined. -->
|
||||
<!--
|
||||
interval - send every X second
|
||||
root_path - prefix for keys
|
||||
hostname_in_path - append hostname to root_path (default = true)
|
||||
metrics - send data from table system.metrics
|
||||
events - send data from table system.events
|
||||
asynchronous_metrics - send data from table system.asynchronous_metrics
|
||||
-->
|
||||
<!--
|
||||
<graphite>
|
||||
<host>localhost</host>
|
||||
<port>42000</port>
|
||||
<timeout>0.1</timeout>
|
||||
<interval>60</interval>
|
||||
<root_path>one_min</root_path>
|
||||
<hostname_in_path>true</hostname_in_path>
|
||||
|
||||
<metrics>true</metrics>
|
||||
<events>true</events>
|
||||
<asynchronous_metrics>true</asynchronous_metrics>
|
||||
</graphite>
|
||||
<graphite>
|
||||
<host>localhost</host>
|
||||
<port>42000</port>
|
||||
<timeout>0.1</timeout>
|
||||
<interval>1</interval>
|
||||
<root_path>one_sec</root_path>
|
||||
|
||||
<metrics>true</metrics>
|
||||
<events>true</events>
|
||||
<asynchronous_metrics>false</asynchronous_metrics>
|
||||
</graphite>
|
||||
-->
|
||||
|
||||
|
||||
<!-- Query log. Used only for queries with setting log_queries = 1. -->
|
||||
<query_log>
|
||||
<!-- What table to insert data. If table is not exist, it will be created.
|
||||
When query log structure is changed after system update,
|
||||
then old table will be renamed and new table will be created automatically.
|
||||
-->
|
||||
<database>system</database>
|
||||
<table>query_log</table>
|
||||
<!--
|
||||
PARTITION BY expr https://clickhouse.yandex/docs/en/table_engines/custom_partitioning_key/
|
||||
Example:
|
||||
event_date
|
||||
toMonday(event_date)
|
||||
toYYYYMM(event_date)
|
||||
toStartOfHour(event_time)
|
||||
-->
|
||||
<partition_by>toYYYYMM(event_date)</partition_by>
|
||||
<!-- Interval of flushing data. -->
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</query_log>
|
||||
|
||||
<!-- Trace log. Stores stack traces collected by query profilers.
|
||||
See query_profiler_real_time_period_ns and query_profiler_cpu_time_period_ns settings. -->
|
||||
<trace_log>
|
||||
<database>system</database>
|
||||
<table>trace_log</table>
|
||||
|
||||
<partition_by>toYYYYMM(event_date)</partition_by>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</trace_log>
|
||||
|
||||
<!-- Query thread log. Has information about all threads participated in query execution.
|
||||
Used only for queries with setting log_query_threads = 1. -->
|
||||
<query_thread_log>
|
||||
<database>system</database>
|
||||
<table>query_thread_log</table>
|
||||
<partition_by>toYYYYMM(event_date)</partition_by>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</query_thread_log>
|
||||
|
||||
<!-- Uncomment if use part log.
|
||||
Part log contains information about all actions with parts in MergeTree tables (creation, deletion, merges, downloads).
|
||||
<part_log>
|
||||
<database>system</database>
|
||||
<table>part_log</table>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</part_log>
|
||||
-->
|
||||
|
||||
<!-- Uncomment to write text log into table.
|
||||
Text log contains all information from usual server log but stores it in structured and efficient way.
|
||||
<text_log>
|
||||
<database>system</database>
|
||||
<table>text_log</table>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</text_log>
|
||||
-->
|
||||
|
||||
<!-- Parameters for embedded dictionaries, used in Yandex.Metrica.
|
||||
See https://clickhouse.yandex/docs/en/dicts/internal_dicts/
|
||||
-->
|
||||
|
||||
<!-- Path to file with region hierarchy. -->
|
||||
<!-- <path_to_regions_hierarchy_file>/opt/geo/regions_hierarchy.txt</path_to_regions_hierarchy_file> -->
|
||||
|
||||
<!-- Path to directory with files containing names of regions -->
|
||||
<!-- <path_to_regions_names_files>/opt/geo/</path_to_regions_names_files> -->
|
||||
|
||||
|
||||
<!-- Configuration of external dictionaries. See:
|
||||
https://clickhouse.yandex/docs/en/dicts/external_dicts/
|
||||
-->
|
||||
<dictionaries_config>*_dictionary.xml</dictionaries_config>
|
||||
|
||||
<!-- Uncomment if you want data to be compressed 30-100% better.
|
||||
Don't do that if you just started using ClickHouse.
|
||||
-->
|
||||
<compression incl="compression">
|
||||
<!--
|
||||
<!- - Set of variants. Checked in order. Last matching case wins. If nothing matches, lz4 will be used. - ->
|
||||
<case>
|
||||
|
||||
<!- - Conditions. All must be satisfied. Some conditions may be omitted. - ->
|
||||
<min_part_size>10000000000</min_part_size> <!- - Min part size in bytes. - ->
|
||||
<min_part_size_ratio>0.01</min_part_size_ratio> <!- - Min size of part relative to whole table size. - ->
|
||||
|
||||
<!- - What compression method to use. - ->
|
||||
<method>zstd</method>
|
||||
</case>
|
||||
-->
|
||||
</compression>
|
||||
|
||||
<!-- Allow to execute distributed DDL queries (CREATE, DROP, ALTER, RENAME) on cluster.
|
||||
Works only if ZooKeeper is enabled. Comment it if such functionality isn't required. -->
|
||||
<distributed_ddl>
|
||||
<!-- Path in ZooKeeper to queue with DDL queries -->
|
||||
<path>/clickhouse/task_queue/ddl</path>
|
||||
|
||||
<!-- Settings from this profile will be used to execute DDL queries -->
|
||||
<!-- <profile>default</profile> -->
|
||||
</distributed_ddl>
|
||||
|
||||
<!-- Settings to fine tune MergeTree tables. See documentation in source code, in MergeTreeSettings.h -->
|
||||
<!--
|
||||
<merge_tree>
|
||||
<max_suspicious_broken_parts>5</max_suspicious_broken_parts>
|
||||
</merge_tree>
|
||||
-->
|
||||
|
||||
<!-- Protection from accidental DROP.
|
||||
If size of a MergeTree table is greater than max_table_size_to_drop (in bytes) than table could not be dropped with any DROP query.
|
||||
If you want do delete one table and don't want to restart clickhouse-server, you could create special file <clickhouse-path>/flags/force_drop_table and make DROP once.
|
||||
By default max_table_size_to_drop is 50GB; max_table_size_to_drop=0 allows to DROP any tables.
|
||||
The same for max_partition_size_to_drop.
|
||||
Uncomment to disable protection.
|
||||
-->
|
||||
<!-- <max_table_size_to_drop>0</max_table_size_to_drop> -->
|
||||
<!-- <max_partition_size_to_drop>0</max_partition_size_to_drop> -->
|
||||
|
||||
<!-- Example of parameters for GraphiteMergeTree table engine -->
|
||||
<graphite_rollup_example>
|
||||
<pattern>
|
||||
<regexp>click_cost</regexp>
|
||||
<function>any</function>
|
||||
<retention>
|
||||
<age>0</age>
|
||||
<precision>3600</precision>
|
||||
</retention>
|
||||
<retention>
|
||||
<age>86400</age>
|
||||
<precision>60</precision>
|
||||
</retention>
|
||||
</pattern>
|
||||
<default>
|
||||
<function>max</function>
|
||||
<retention>
|
||||
<age>0</age>
|
||||
<precision>60</precision>
|
||||
</retention>
|
||||
<retention>
|
||||
<age>3600</age>
|
||||
<precision>300</precision>
|
||||
</retention>
|
||||
<retention>
|
||||
<age>86400</age>
|
||||
<precision>3600</precision>
|
||||
</retention>
|
||||
</default>
|
||||
</graphite_rollup_example>
|
||||
|
||||
<!-- Directory in <clickhouse-path> containing schema files for various input formats.
|
||||
The directory will be created if it doesn't exist.
|
||||
-->
|
||||
<format_schema_path>/var/lib/clickhouse/format_schemas/</format_schema_path>
|
||||
|
||||
<!-- Uncomment to disable ClickHouse internal DNS caching. -->
|
||||
<!-- <disable_internal_dns_cache>1</disable_internal_dns_cache> -->
|
||||
</yandex>
|
8
tests/testflows/ldap/configs/clickhouse/ssl/dhparam.pem
Normal file
8
tests/testflows/ldap/configs/clickhouse/ssl/dhparam.pem
Normal file
@ -0,0 +1,8 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MIIBCAKCAQEAua92DDli13gJ+//ZXyGaggjIuidqB0crXfhUlsrBk9BV1hH3i7fR
|
||||
XGP9rUdk2ubnB3k2ejBStL5oBrkHm9SzUFSQHqfDjLZjKoUpOEmuDc4cHvX1XTR5
|
||||
Pr1vf5cd0yEncJWG5W4zyUB8k++SUdL2qaeslSs+f491HBLDYn/h8zCgRbBvxhxb
|
||||
9qeho1xcbnWeqkN6Kc9bgGozA16P9NLuuLttNnOblkH+lMBf42BSne/TWt3AlGZf
|
||||
slKmmZcySUhF8aKfJnLKbkBCFqOtFRh8zBA9a7g+BT/lSANATCDPaAk1YVih2EKb
|
||||
dpc3briTDbRsiqg2JKMI7+VdULY9bh3EawIBAg==
|
||||
-----END DH PARAMETERS-----
|
19
tests/testflows/ldap/configs/clickhouse/ssl/server.crt
Normal file
19
tests/testflows/ldap/configs/clickhouse/ssl/server.crt
Normal file
@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC/TCCAeWgAwIBAgIJANjx1QSR77HBMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
|
||||
BAMMCWxvY2FsaG9zdDAgFw0xODA3MzAxODE2MDhaGA8yMjkyMDUxNDE4MTYwOFow
|
||||
FDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||
CgKCAQEAs9uSo6lJG8o8pw0fbVGVu0tPOljSWcVSXH9uiJBwlZLQnhN4SFSFohfI
|
||||
4K8U1tBDTnxPLUo/V1K9yzoLiRDGMkwVj6+4+hE2udS2ePTQv5oaMeJ9wrs+5c9T
|
||||
4pOtlq3pLAdm04ZMB1nbrEysceVudHRkQbGHzHp6VG29Fw7Ga6YpqyHQihRmEkTU
|
||||
7UCYNA+Vk7aDPdMS/khweyTpXYZimaK9f0ECU3/VOeG3fH6Sp2X6FN4tUj/aFXEj
|
||||
sRmU5G2TlYiSIUMF2JPdhSihfk1hJVALrHPTU38SOL+GyyBRWdNcrIwVwbpvsvPg
|
||||
pryMSNxnpr0AK0dFhjwnupIv5hJIOQIDAQABo1AwTjAdBgNVHQ4EFgQUjPLb3uYC
|
||||
kcamyZHK4/EV8jAP0wQwHwYDVR0jBBgwFoAUjPLb3uYCkcamyZHK4/EV8jAP0wQw
|
||||
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAM/ocuDvfPus/KpMVD51j
|
||||
4IdlU8R0vmnYLQ+ygzOAo7+hUWP5j0yvq4ILWNmQX6HNvUggCgFv9bjwDFhb/5Vr
|
||||
85ieWfTd9+LTjrOzTw4avdGwpX9G+6jJJSSq15tw5ElOIFb/qNA9O4dBiu8vn03C
|
||||
L/zRSXrARhSqTW5w/tZkUcSTT+M5h28+Lgn9ysx4Ff5vi44LJ1NnrbJbEAIYsAAD
|
||||
+UA+4MBFKx1r6hHINULev8+lCfkpwIaeS8RL+op4fr6kQPxnULw8wT8gkuc8I4+L
|
||||
P9gg/xDHB44T3ADGZ5Ib6O0DJaNiToO6rnoaaxs0KkotbvDWvRoxEytSbXKoYjYp
|
||||
0g==
|
||||
-----END CERTIFICATE-----
|
28
tests/testflows/ldap/configs/clickhouse/ssl/server.key
Normal file
28
tests/testflows/ldap/configs/clickhouse/ssl/server.key
Normal file
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCz25KjqUkbyjyn
|
||||
DR9tUZW7S086WNJZxVJcf26IkHCVktCeE3hIVIWiF8jgrxTW0ENOfE8tSj9XUr3L
|
||||
OguJEMYyTBWPr7j6ETa51LZ49NC/mhox4n3Cuz7lz1Pik62WreksB2bThkwHWdus
|
||||
TKxx5W50dGRBsYfMenpUbb0XDsZrpimrIdCKFGYSRNTtQJg0D5WTtoM90xL+SHB7
|
||||
JOldhmKZor1/QQJTf9U54bd8fpKnZfoU3i1SP9oVcSOxGZTkbZOViJIhQwXYk92F
|
||||
KKF+TWElUAusc9NTfxI4v4bLIFFZ01ysjBXBum+y8+CmvIxI3GemvQArR0WGPCe6
|
||||
ki/mEkg5AgMBAAECggEATrbIBIxwDJOD2/BoUqWkDCY3dGevF8697vFuZKIiQ7PP
|
||||
TX9j4vPq0DfsmDjHvAPFkTHiTQXzlroFik3LAp+uvhCCVzImmHq0IrwvZ9xtB43f
|
||||
7Pkc5P6h1l3Ybo8HJ6zRIY3TuLtLxuPSuiOMTQSGRL0zq3SQ5DKuGwkz+kVjHXUN
|
||||
MR2TECFwMHKQ5VLrC+7PMpsJYyOMlDAWhRfUalxC55xOXTpaN8TxNnwQ8K2ISVY5
|
||||
212Jz/a4hn4LdwxSz3Tiu95PN072K87HLWx3EdT6vW4Ge5P/A3y+smIuNAlanMnu
|
||||
plHBRtpATLiTxZt/n6npyrfQVbYjSH7KWhB8hBHtaQKBgQDh9Cq1c/KtqDtE0Ccr
|
||||
/r9tZNTUwBE6VP+3OJeKdEdtsfuxjOCkS1oAjgBJiSDOiWPh1DdoDeVZjPKq6pIu
|
||||
Mq12OE3Doa8znfCXGbkSzEKOb2unKZMJxzrz99kXt40W5DtrqKPNb24CNqTiY8Aa
|
||||
CjtcX+3weat82VRXvph6U8ltMwKBgQDLxjiQQzNoY7qvg7CwJCjf9qq8jmLK766g
|
||||
1FHXopqS+dTxDLM8eJSRrpmxGWJvNeNc1uPhsKsKgotqAMdBUQTf7rSTbt4MyoH5
|
||||
bUcRLtr+0QTK9hDWMOOvleqNXha68vATkohWYfCueNsC60qD44o8RZAS6UNy3ENq
|
||||
cM1cxqe84wKBgQDKkHutWnooJtajlTxY27O/nZKT/HA1bDgniMuKaz4R4Gr1PIez
|
||||
on3YW3V0d0P7BP6PWRIm7bY79vkiMtLEKdiKUGWeyZdo3eHvhDb/3DCawtau8L2K
|
||||
GZsHVp2//mS1Lfz7Qh8/L/NedqCQ+L4iWiPnZ3THjjwn3CoZ05ucpvrAMwKBgB54
|
||||
nay039MUVq44Owub3KDg+dcIU62U+cAC/9oG7qZbxYPmKkc4oL7IJSNecGHA5SbU
|
||||
2268RFdl/gLz6tfRjbEOuOHzCjFPdvAdbysanpTMHLNc6FefJ+zxtgk9sJh0C4Jh
|
||||
vxFrw9nTKKzfEl12gQ1SOaEaUIO0fEBGbe8ZpauRAoGAMAlGV+2/K4ebvAJKOVTa
|
||||
dKAzQ+TD2SJmeR1HZmKDYddNqwtZlzg3v4ZhCk4eaUmGeC1Bdh8MDuB3QQvXz4Dr
|
||||
vOIP4UVaOr+uM+7TgAgVnP4/K6IeJGzUDhX93pmpWhODfdu/oojEKVcpCojmEmS1
|
||||
KCBtmIrQLqzMpnBpLNuSY+Q=
|
||||
-----END PRIVATE KEY-----
|
133
tests/testflows/ldap/configs/clickhouse/users.xml
Normal file
133
tests/testflows/ldap/configs/clickhouse/users.xml
Normal file
@ -0,0 +1,133 @@
|
||||
<?xml version="1.0"?>
|
||||
<yandex>
|
||||
<!-- Profiles of settings. -->
|
||||
<profiles>
|
||||
<!-- Default settings. -->
|
||||
<default>
|
||||
<!-- Maximum memory usage for processing single query, in bytes. -->
|
||||
<max_memory_usage>10000000000</max_memory_usage>
|
||||
|
||||
<!-- Use cache of uncompressed blocks of data. Meaningful only for processing many of very short queries. -->
|
||||
<use_uncompressed_cache>0</use_uncompressed_cache>
|
||||
|
||||
<!-- How to choose between replicas during distributed query processing.
|
||||
random - choose random replica from set of replicas with minimum number of errors
|
||||
nearest_hostname - from set of replicas with minimum number of errors, choose replica
|
||||
with minimum number of different symbols between replica's hostname and local hostname
|
||||
(Hamming distance).
|
||||
in_order - first live replica is chosen in specified order.
|
||||
first_or_random - if first replica one has higher number of errors, pick a random one from replicas with minimum number of errors.
|
||||
-->
|
||||
<load_balancing>random</load_balancing>
|
||||
</default>
|
||||
|
||||
<!-- Profile that allows only read queries. -->
|
||||
<readonly>
|
||||
<readonly>1</readonly>
|
||||
</readonly>
|
||||
</profiles>
|
||||
|
||||
<!-- Users and ACL. -->
|
||||
<users>
|
||||
<!-- If user name was not specified, 'default' user is used. -->
|
||||
<default>
|
||||
<!-- Password could be specified in plaintext or in SHA256 (in hex format).
|
||||
|
||||
If you want to specify password in plaintext (not recommended), place it in 'password' element.
|
||||
Example: <password>qwerty</password>.
|
||||
Password could be empty.
|
||||
|
||||
If you want to specify SHA256, place it in 'password_sha256_hex' element.
|
||||
Example: <password_sha256_hex>65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5</password_sha256_hex>
|
||||
Restrictions of SHA256: impossibility to connect to ClickHouse using MySQL JS client (as of July 2019).
|
||||
|
||||
If you want to specify double SHA1, place it in 'password_double_sha1_hex' element.
|
||||
Example: <password_double_sha1_hex>e395796d6546b1b65db9d665cd43f0e858dd4303</password_double_sha1_hex>
|
||||
|
||||
How to generate decent password:
|
||||
Execute: PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | sha256sum | tr -d '-'
|
||||
In first line will be password and in second - corresponding SHA256.
|
||||
|
||||
How to generate double SHA1:
|
||||
Execute: PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | openssl dgst -sha1 -binary | openssl dgst -sha1
|
||||
In first line will be password and in second - corresponding double SHA1.
|
||||
-->
|
||||
<password></password>
|
||||
|
||||
<!-- List of networks with open access.
|
||||
|
||||
To open access from everywhere, specify:
|
||||
<ip>::/0</ip>
|
||||
|
||||
To open access only from localhost, specify:
|
||||
<ip>::1</ip>
|
||||
<ip>127.0.0.1</ip>
|
||||
|
||||
Each element of list has one of the following forms:
|
||||
<ip> IP-address or network mask. Examples: 213.180.204.3 or 10.0.0.1/8 or 10.0.0.1/255.255.255.0
|
||||
2a02:6b8::3 or 2a02:6b8::3/64 or 2a02:6b8::3/ffff:ffff:ffff:ffff::.
|
||||
<host> Hostname. Example: server01.yandex.ru.
|
||||
To check access, DNS query is performed, and all received addresses compared to peer address.
|
||||
<host_regexp> Regular expression for host names. Example, ^server\d\d-\d\d-\d\.yandex\.ru$
|
||||
To check access, DNS PTR query is performed for peer address and then regexp is applied.
|
||||
Then, for result of PTR query, another DNS query is performed and all received addresses compared to peer address.
|
||||
Strongly recommended that regexp is ends with $
|
||||
All results of DNS requests are cached till server restart.
|
||||
-->
|
||||
<networks incl="networks" replace="replace">
|
||||
<ip>::/0</ip>
|
||||
</networks>
|
||||
|
||||
<!-- Settings profile for user. -->
|
||||
<profile>default</profile>
|
||||
|
||||
<!-- Quota for user. -->
|
||||
<quota>default</quota>
|
||||
|
||||
<!-- Allow access management -->
|
||||
<access_management>1</access_management>
|
||||
|
||||
<!-- Example of row level security policy. -->
|
||||
<!-- <databases>
|
||||
<test>
|
||||
<filtered_table1>
|
||||
<filter>a = 1</filter>
|
||||
</filtered_table1>
|
||||
<filtered_table2>
|
||||
<filter>a + b < 1 or c - d > 5</filter>
|
||||
</filtered_table2>
|
||||
</test>
|
||||
</databases> -->
|
||||
</default>
|
||||
|
||||
<!-- Example of user with readonly access. -->
|
||||
<!-- <readonly>
|
||||
<password></password>
|
||||
<networks incl="networks" replace="replace">
|
||||
<ip>::1</ip>
|
||||
<ip>127.0.0.1</ip>
|
||||
</networks>
|
||||
<profile>readonly</profile>
|
||||
<quota>default</quota>
|
||||
</readonly> -->
|
||||
</users>
|
||||
|
||||
<!-- Quotas. -->
|
||||
<quotas>
|
||||
<!-- Name of quota. -->
|
||||
<default>
|
||||
<!-- Limits for time interval. You could specify many intervals with different limits. -->
|
||||
<interval>
|
||||
<!-- Length of interval. -->
|
||||
<duration>3600</duration>
|
||||
|
||||
<!-- No limits. Just calculate resource usage for time interval. -->
|
||||
<queries>0</queries>
|
||||
<errors>0</errors>
|
||||
<result_rows>0</result_rows>
|
||||
<read_rows>0</read_rows>
|
||||
<execution_time>0</execution_time>
|
||||
</interval>
|
||||
</default>
|
||||
</quotas>
|
||||
</yandex>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<yandex>
|
||||
<macros>
|
||||
<replica>clickhouse1</replica>
|
||||
<shard>01</shard>
|
||||
<shard2>01</shard2>
|
||||
</macros>
|
||||
</yandex>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<yandex>
|
||||
<macros>
|
||||
<replica>clickhouse2</replica>
|
||||
<shard>01</shard>
|
||||
<shard2>02</shard2>
|
||||
</macros>
|
||||
</yandex>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<yandex>
|
||||
<macros>
|
||||
<replica>clickhouse3</replica>
|
||||
<shard>01</shard>
|
||||
<shard2>03</shard2>
|
||||
</macros>
|
||||
</yandex>
|
64
tests/testflows/ldap/configs/ldap1/config/export.ldif
Normal file
64
tests/testflows/ldap/configs/ldap1/config/export.ldif
Normal file
@ -0,0 +1,64 @@
|
||||
# LDIF Export for dc=company,dc=com
|
||||
# Server: openldap (openldap)
|
||||
# Search Scope: sub
|
||||
# Search Filter: (objectClass=*)
|
||||
# Total Entries: 7
|
||||
#
|
||||
# Generated by phpLDAPadmin (http://phpldapadmin.sourceforge.net) on May 22, 2020 5:51 pm
|
||||
# Version: 1.2.5
|
||||
|
||||
# Entry 1: dc=company,dc=com
|
||||
#dn: dc=company,dc=com
|
||||
#dc: company
|
||||
#o: company
|
||||
#objectclass: top
|
||||
#objectclass: dcObject
|
||||
#objectclass: organization
|
||||
|
||||
# Entry 2: cn=admin,dc=company,dc=com
|
||||
#dn: cn=admin,dc=company,dc=com
|
||||
#cn: admin
|
||||
#description: LDAP administrator
|
||||
#objectclass: simpleSecurityObject
|
||||
#objectclass: organizationalRole
|
||||
#userpassword: {SSHA}eUEupkQCTvq9SkrxfWGSe5rX+orrjVbF
|
||||
|
||||
# Entry 3: ou=groups,dc=company,dc=com
|
||||
dn: ou=groups,dc=company,dc=com
|
||||
objectclass: organizationalUnit
|
||||
objectclass: top
|
||||
ou: groups
|
||||
|
||||
# Entry 4: cn=admin,ou=groups,dc=company,dc=com
|
||||
dn: cn=admin,ou=groups,dc=company,dc=com
|
||||
cn: admin
|
||||
gidnumber: 500
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
# Entry 5: cn=users,ou=groups,dc=company,dc=com
|
||||
dn: cn=users,ou=groups,dc=company,dc=com
|
||||
cn: users
|
||||
gidnumber: 501
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
# Entry 6: ou=users,dc=company,dc=com
|
||||
dn: ou=users,dc=company,dc=com
|
||||
objectclass: organizationalUnit
|
||||
objectclass: top
|
||||
ou: users
|
||||
|
||||
# Entry 7: cn=user1,ou=users,dc=company,dc=com
|
||||
dn: cn=user1,ou=users,dc=company,dc=com
|
||||
cn: user1
|
||||
gidnumber: 501
|
||||
givenname: John
|
||||
homedirectory: /home/users/user1
|
||||
objectclass: inetOrgPerson
|
||||
objectclass: posixAccount
|
||||
objectclass: top
|
||||
sn: User
|
||||
uid: user1
|
||||
uidnumber: 1101
|
||||
userpassword: user1
|
22
tests/testflows/ldap/configs/ldap2/certs/ca.crt
Normal file
22
tests/testflows/ldap/configs/ldap2/certs/ca.crt
Normal file
@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDlTCCAn2gAwIBAgIUJBqw2dHM2DDCZjYSkPOESlvDH6swDQYJKoZIhvcNAQEL
|
||||
BQAwWjELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMQ8wDQYDVQQHDAZPdHRhd2Ex
|
||||
ETAPBgNVBAoMCEFsdGluaXR5MQswCQYDVQQLDAJRQTENMAsGA1UEAwwEcm9vdDAe
|
||||
Fw0yMDA2MTExOTAzNDhaFw0zMDA2MDkxOTAzNDhaMFoxCzAJBgNVBAYTAkNBMQsw
|
||||
CQYDVQQIDAJPTjEPMA0GA1UEBwwGT3R0YXdhMREwDwYDVQQKDAhBbHRpbml0eTEL
|
||||
MAkGA1UECwwCUUExDTALBgNVBAMMBHJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQC9Irr0zGV+HCI2fZ0ht4hR5It4Sbjz4RwZV8ENRP/+TEz8l9eK
|
||||
J6ygxhKX7SMYzIs/jS9Gsq4plX1r2ujW1qRf8yLpR4+dGLP+jBRi1drj0XjZXosT
|
||||
SERjWzgPauWxL9LN8+l26eBAqz6fw5e0W8WRSTgf5iGiCcKOTmaATIUjP0CdfWKK
|
||||
qpktI4vhe++CXZFJ3usR+8KZ/FwwbCLJM/3J2HnbcXfcaYPYvr1tfqLudKSTbG9H
|
||||
M3+AVwjctdesc/0sbd51Zsm0ClQptMbuKnDCYauGg61kNkgbgPgRmH9Pzo67DtxF
|
||||
/WW+PtOzq8xLOifciQ9Piboy9QBSQZGwf4wzAgMBAAGjUzBRMB0GA1UdDgQWBBSi
|
||||
njya0RDozx3OZTLYFpwqYnlpIDAfBgNVHSMEGDAWgBSinjya0RDozx3OZTLYFpwq
|
||||
YnlpIDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBAD7VyFg7F
|
||||
U1C25KFvtauchAOjCW6w7U/b3z1dVZvcQ88/kH1VsLUcfGixlSilUEfPTJsi7OA0
|
||||
R5BQdh2GGcjUJv4iqEFGU05KvMVmRRKn08P62+ZhJxKMxG26VzcliRZzCMkI6d0W
|
||||
lFwI6nM45yeqdHVh5k4xbuJzqpbD9BtXXLI+/Ra9Fx8S9ETA3GdidpZLU5P1VLxq
|
||||
UuedfqyAVWZXpr6TAURGxouRmRzul9yFzbSUex+MLEIPrstjtEwV3+tBQZJz9xAS
|
||||
TVPj+Nv3LO7GCq54bdwkq1ioWbSL2hEmABkj6kdW/JwmfhGHf/2rirDVMzrTYw07
|
||||
dFJfAZC+FEsv
|
||||
-----END CERTIFICATE-----
|
5
tests/testflows/ldap/configs/ldap2/certs/dhparam.pem
Normal file
5
tests/testflows/ldap/configs/ldap2/certs/dhparam.pem
Normal file
@ -0,0 +1,5 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MIGHAoGBAJitt2hhnpDViQ5ko2ipBMdjy+bZ6FR/WdZ987R7lQvBkKehPXmxtEyV
|
||||
AO6ofv5CZSDJokc5bUeBOAtg0EhMTCH82uPdwQvt58jRXcxXBg4JTjkx+oW9LBv2
|
||||
FdZsbaX8+SYivmiZ0Jp8T/HBm/4DA9VBS0O5GFRS4C7dHhmSTPfDAgEC
|
||||
-----END DH PARAMETERS-----
|
20
tests/testflows/ldap/configs/ldap2/certs/ldap.crt
Normal file
20
tests/testflows/ldap/configs/ldap2/certs/ldap.crt
Normal file
@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQDCCAigCFCJ7El0ntrGktZVTYTZd+OwtcJjBMA0GCSqGSIb3DQEBCwUAMFox
|
||||
CzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UEBwwGT3R0YXdhMREwDwYD
|
||||
VQQKDAhBbHRpbml0eTELMAkGA1UECwwCUUExDTALBgNVBAMMBHJvb3QwHhcNMjAw
|
||||
NjExMTkxMTQzWhcNMzAwNjA5MTkxMTQzWjBfMQswCQYDVQQGEwJDQTELMAkGA1UE
|
||||
CAwCT04xDzANBgNVBAcMBk90dGF3YTERMA8GA1UECgwIQWx0aW5pdHkxCzAJBgNV
|
||||
BAsMAlFBMRIwEAYDVQQDDAlvcGVubGRhcDIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQC0Mbn//U56URavMgXm82FWP6vBdKuRydFX/L0M5XLlnAtk/IXG
|
||||
/T+4t7nOBJxWmTp/xpsPtSMALE4eFJpEUEqlpVbG5DfBzVWcYOWoMeRAcHWCDkzr
|
||||
PkB6I0dfF0Mm5hoaDhn+ZXjBWvoh/IlJdAnPg5mlejflJBQ7xtFC9eN6WjldXuRO
|
||||
vyntGNuMfVLgITHwXuH2yZ98G0mFO6TU/9dRY/Z3D6RTSzKdb17Yk/VnG+ry92u2
|
||||
0sgXIBvhuJuC3ksWLArwwFoMl8DVa05D4O2H76goGdCcQ0KzqBV8RPXAh3UcgP2e
|
||||
Zu90p2EGIhIk+sZTCkPd4dorxjL9nkRR86HdAgMBAAEwDQYJKoZIhvcNAQELBQAD
|
||||
ggEBAJWiCxJaTksv/BTsh/etxlDY5eHwqStqIuiovEQ8bhGAcKJ3bfWd/YTb8DUS
|
||||
hrLvXrXdOVC+U8PqPFXBpdOqcm5Dc233z52VgUCb+0EKv3lAzgKXRIo32h52skdK
|
||||
NnRrCHDeDzgfEIXR4MEJ99cLEaxWyXQhremmTYWHYznry9/4NYz40gCDxHn9dJAi
|
||||
KxFyDNxhtuKs58zp4PrBoo+542JurAoLPtRGOhdXpU2RkQVU/ho38HsAXDStAB5D
|
||||
vAoSxPuMHKgo17ffrb0oqU3didwaA9fIsz7Mr6RxmI7X03s7hLzNBq9FCqu0U3RR
|
||||
CX4zWGFNJu/ieSGVWLYKQzbYxp8=
|
||||
-----END CERTIFICATE-----
|
17
tests/testflows/ldap/configs/ldap2/certs/ldap.csr
Normal file
17
tests/testflows/ldap/configs/ldap2/certs/ldap.csr
Normal file
@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICpDCCAYwCAQAwXzELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMQ8wDQYDVQQH
|
||||
DAZPdHRhd2ExETAPBgNVBAoMCEFsdGluaXR5MQswCQYDVQQLDAJRQTESMBAGA1UE
|
||||
AwwJb3BlbmxkYXAyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtDG5
|
||||
//1OelEWrzIF5vNhVj+rwXSrkcnRV/y9DOVy5ZwLZPyFxv0/uLe5zgScVpk6f8ab
|
||||
D7UjACxOHhSaRFBKpaVWxuQ3wc1VnGDlqDHkQHB1gg5M6z5AeiNHXxdDJuYaGg4Z
|
||||
/mV4wVr6IfyJSXQJz4OZpXo35SQUO8bRQvXjelo5XV7kTr8p7RjbjH1S4CEx8F7h
|
||||
9smffBtJhTuk1P/XUWP2dw+kU0synW9e2JP1Zxvq8vdrttLIFyAb4bibgt5LFiwK
|
||||
8MBaDJfA1WtOQ+Dth++oKBnQnENCs6gVfET1wId1HID9nmbvdKdhBiISJPrGUwpD
|
||||
3eHaK8Yy/Z5EUfOh3QIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAEzIjZQOT5R7
|
||||
mEJg+RFpCSIoPn3xJ4/VMMyWqA3bTGZKpb4S6GxgsierY/87kPL7jZrMdGYB4Dc3
|
||||
2M3VWZGXlYo8vctH1zLE9VW6CzosUpl20lhdgydoCMz3RQqdJyK8aGeFTeLtk7G/
|
||||
TRCCUFUE6jaA+VtaCPCnOJSff3jUf76xguEu7dgTZgCKV7dtBqald8gIzF3D+AJJ
|
||||
7pEN2UrC3UR0xpe2cj2GhndQJ+WsIyft3zpNFzAO13j8ZPibuVP7oDWcW3ixNCWC
|
||||
213aeRVplJGof8Eo6llDxP+6Fwp1YmOoQmwB1Xm3t4ADn7FLJ14LONLB7q40KviG
|
||||
RyLyqu3IVOI=
|
||||
-----END CERTIFICATE REQUEST-----
|
27
tests/testflows/ldap/configs/ldap2/certs/ldap.key
Normal file
27
tests/testflows/ldap/configs/ldap2/certs/ldap.key
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAtDG5//1OelEWrzIF5vNhVj+rwXSrkcnRV/y9DOVy5ZwLZPyF
|
||||
xv0/uLe5zgScVpk6f8abD7UjACxOHhSaRFBKpaVWxuQ3wc1VnGDlqDHkQHB1gg5M
|
||||
6z5AeiNHXxdDJuYaGg4Z/mV4wVr6IfyJSXQJz4OZpXo35SQUO8bRQvXjelo5XV7k
|
||||
Tr8p7RjbjH1S4CEx8F7h9smffBtJhTuk1P/XUWP2dw+kU0synW9e2JP1Zxvq8vdr
|
||||
ttLIFyAb4bibgt5LFiwK8MBaDJfA1WtOQ+Dth++oKBnQnENCs6gVfET1wId1HID9
|
||||
nmbvdKdhBiISJPrGUwpD3eHaK8Yy/Z5EUfOh3QIDAQABAoIBADugMMIKWcuTxYPX
|
||||
c6iGZHEbxIPRTWyCcalB0nTQAAMGbabPAJ1l8432DZ+kWu806OybFXhPIfPOtVKy
|
||||
0pFEWE8TtPE/V0vj3C5Qye2sBLFmBRwyCzXUdZV00wseMXRPs9dnTyalAR5KMnbI
|
||||
j80kfpKSI2dkV9aU57UYBuq3Xrx/TCGItwL769D4ZZW9BvbpiTZApQQFZ0gwUFFn
|
||||
btPXGU9Ti8H4mfBuZWL+5CaZdqOo76+CXvMPaUK0F9MJp4yX3XxQLRNH3qz/Tyn7
|
||||
h7QOOo0XTqoUmzRw0N9QRVH5LRdSE5yq3aF9aFKjNW59exz+62pufOFadngzkpkn
|
||||
OKCzgWkCgYEA4mOWWMzdYwMn3GtfG7whqlqy7wOmMkNb81zTDQejHBV98dnj0AHr
|
||||
deurfKWzHrAh3DXo6tFeqUIgXabhBPS/0dEx/S5sgLFmuUZP05EUYahfWBgzzmM9
|
||||
C6Oe5xIMLzxsZCJczolsfkEsoFe4o0vkvuLYoQrQL7InzewcDy8cUxsCgYEAy8Na
|
||||
YCnanSNDY03Bulcni+5sF+opaHseeki1pv3nlw8TwsWuZF9ApS+yL7ck9jJjxBRR
|
||||
RC3KGmpoqIr0vTmUYS946ngQWXPE90zfuhJfM+NRv/q0oCjH0qAcxRbTkls5On9v
|
||||
oxJ8rO7gD6K85eHqasWdbCVzdZrobOXzay37tmcCgYBfyUUmw190cjReZauzH3Gb
|
||||
E48b5A5gu/Fe0cqWe8G+szU7rDZgnz9SAGnpbm6QMHPTKZgoKngD42+wUFhq8Wdr
|
||||
zjh5aDgOZ4EQKTjDSmI2Q7g7nNnmnESK9SrZl+BB6C3wXD2qQaj+7nKEUTlVFlpt
|
||||
jaucz+dwFtASp7Djl8pDOwKBgEtr2c3ycArt/ImLRIP2spqm+7e2YvFbcSKOOz6+
|
||||
iLRvTj8v8KcSYtlB2FC1F6dRa4AujQ4RbNduP6LzHDfWUkfOzJDtNBAIPAXVnJJB
|
||||
LqAEKkRHRghqT9x0i3GgS1vHDF3MwcO4mhFgserXr9ffUWeIEgbvrdcAKbv1Oa6Y
|
||||
bK1NAoGAGPm8ISmboDJynjBl9wMrkcy23Pwg9kmyocdWUHh0zMLDKriZNKYB6u/U
|
||||
C+/RTfkohPoHPzkeqWiHp7z3JhMItYUfTkNW6vMCxEGc0NEN6ZyMIjtiDPGN1n6O
|
||||
E7jmODFmj1AQICQGdV5SHp+yKvKyb0YHKyDwETbs4SZBXxVvjEw=
|
||||
-----END RSA PRIVATE KEY-----
|
64
tests/testflows/ldap/configs/ldap2/config/export.ldif
Normal file
64
tests/testflows/ldap/configs/ldap2/config/export.ldif
Normal file
@ -0,0 +1,64 @@
|
||||
# LDIF Export for dc=company,dc=com
|
||||
# Server: openldap (openldap)
|
||||
# Search Scope: sub
|
||||
# Search Filter: (objectClass=*)
|
||||
# Total Entries: 7
|
||||
#
|
||||
# Generated by phpLDAPadmin (http://phpldapadmin.sourceforge.net) on May 22, 2020 5:51 pm
|
||||
# Version: 1.2.5
|
||||
|
||||
# Entry 1: dc=company,dc=com
|
||||
#dn: dc=company,dc=com
|
||||
#dc: company
|
||||
#o: company
|
||||
#objectclass: top
|
||||
#objectclass: dcObject
|
||||
#objectclass: organization
|
||||
|
||||
# Entry 2: cn=admin,dc=company,dc=com
|
||||
#dn: cn=admin,dc=company,dc=com
|
||||
#cn: admin
|
||||
#description: LDAP administrator
|
||||
#objectclass: simpleSecurityObject
|
||||
#objectclass: organizationalRole
|
||||
#userpassword: {SSHA}eUEupkQCTvq9SkrxfWGSe5rX+orrjVbF
|
||||
|
||||
# Entry 3: ou=groups,dc=company,dc=com
|
||||
dn: ou=groups,dc=company,dc=com
|
||||
objectclass: organizationalUnit
|
||||
objectclass: top
|
||||
ou: groups
|
||||
|
||||
# Entry 4: cn=admin,ou=groups,dc=company,dc=com
|
||||
dn: cn=admin,ou=groups,dc=company,dc=com
|
||||
cn: admin
|
||||
gidnumber: 500
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
# Entry 5: cn=users,ou=groups,dc=company,dc=com
|
||||
dn: cn=users,ou=groups,dc=company,dc=com
|
||||
cn: users
|
||||
gidnumber: 501
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
# Entry 6: ou=users,dc=company,dc=com
|
||||
dn: ou=users,dc=company,dc=com
|
||||
objectclass: organizationalUnit
|
||||
objectclass: top
|
||||
ou: users
|
||||
|
||||
# Entry 7: cn=user2,ou=users,dc=company,dc=com
|
||||
dn: cn=user2,ou=users,dc=company,dc=com
|
||||
cn: user2
|
||||
gidnumber: 501
|
||||
givenname: John
|
||||
homedirectory: /home/users/user2
|
||||
objectclass: inetOrgPerson
|
||||
objectclass: posixAccount
|
||||
objectclass: top
|
||||
sn: User
|
||||
uid: user2
|
||||
uidnumber: 1002
|
||||
userpassword: user2
|
22
tests/testflows/ldap/configs/ldap3/certs/ca.crt
Normal file
22
tests/testflows/ldap/configs/ldap3/certs/ca.crt
Normal file
@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDlTCCAn2gAwIBAgIUJBqw2dHM2DDCZjYSkPOESlvDH6swDQYJKoZIhvcNAQEL
|
||||
BQAwWjELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMQ8wDQYDVQQHDAZPdHRhd2Ex
|
||||
ETAPBgNVBAoMCEFsdGluaXR5MQswCQYDVQQLDAJRQTENMAsGA1UEAwwEcm9vdDAe
|
||||
Fw0yMDA2MTExOTAzNDhaFw0zMDA2MDkxOTAzNDhaMFoxCzAJBgNVBAYTAkNBMQsw
|
||||
CQYDVQQIDAJPTjEPMA0GA1UEBwwGT3R0YXdhMREwDwYDVQQKDAhBbHRpbml0eTEL
|
||||
MAkGA1UECwwCUUExDTALBgNVBAMMBHJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQC9Irr0zGV+HCI2fZ0ht4hR5It4Sbjz4RwZV8ENRP/+TEz8l9eK
|
||||
J6ygxhKX7SMYzIs/jS9Gsq4plX1r2ujW1qRf8yLpR4+dGLP+jBRi1drj0XjZXosT
|
||||
SERjWzgPauWxL9LN8+l26eBAqz6fw5e0W8WRSTgf5iGiCcKOTmaATIUjP0CdfWKK
|
||||
qpktI4vhe++CXZFJ3usR+8KZ/FwwbCLJM/3J2HnbcXfcaYPYvr1tfqLudKSTbG9H
|
||||
M3+AVwjctdesc/0sbd51Zsm0ClQptMbuKnDCYauGg61kNkgbgPgRmH9Pzo67DtxF
|
||||
/WW+PtOzq8xLOifciQ9Piboy9QBSQZGwf4wzAgMBAAGjUzBRMB0GA1UdDgQWBBSi
|
||||
njya0RDozx3OZTLYFpwqYnlpIDAfBgNVHSMEGDAWgBSinjya0RDozx3OZTLYFpwq
|
||||
YnlpIDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBAD7VyFg7F
|
||||
U1C25KFvtauchAOjCW6w7U/b3z1dVZvcQ88/kH1VsLUcfGixlSilUEfPTJsi7OA0
|
||||
R5BQdh2GGcjUJv4iqEFGU05KvMVmRRKn08P62+ZhJxKMxG26VzcliRZzCMkI6d0W
|
||||
lFwI6nM45yeqdHVh5k4xbuJzqpbD9BtXXLI+/Ra9Fx8S9ETA3GdidpZLU5P1VLxq
|
||||
UuedfqyAVWZXpr6TAURGxouRmRzul9yFzbSUex+MLEIPrstjtEwV3+tBQZJz9xAS
|
||||
TVPj+Nv3LO7GCq54bdwkq1ioWbSL2hEmABkj6kdW/JwmfhGHf/2rirDVMzrTYw07
|
||||
dFJfAZC+FEsv
|
||||
-----END CERTIFICATE-----
|
5
tests/testflows/ldap/configs/ldap3/certs/dhparam.pem
Normal file
5
tests/testflows/ldap/configs/ldap3/certs/dhparam.pem
Normal file
@ -0,0 +1,5 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MIGHAoGBAJitt2hhnpDViQ5ko2ipBMdjy+bZ6FR/WdZ987R7lQvBkKehPXmxtEyV
|
||||
AO6ofv5CZSDJokc5bUeBOAtg0EhMTCH82uPdwQvt58jRXcxXBg4JTjkx+oW9LBv2
|
||||
FdZsbaX8+SYivmiZ0Jp8T/HBm/4DA9VBS0O5GFRS4C7dHhmSTPfDAgEC
|
||||
-----END DH PARAMETERS-----
|
20
tests/testflows/ldap/configs/ldap3/certs/ldap.crt
Normal file
20
tests/testflows/ldap/configs/ldap3/certs/ldap.crt
Normal file
@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQDCCAigCFCJ7El0ntrGktZVTYTZd+OwtcJjBMA0GCSqGSIb3DQEBCwUAMFox
|
||||
CzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UEBwwGT3R0YXdhMREwDwYD
|
||||
VQQKDAhBbHRpbml0eTELMAkGA1UECwwCUUExDTALBgNVBAMMBHJvb3QwHhcNMjAw
|
||||
NjExMTkxMTQzWhcNMzAwNjA5MTkxMTQzWjBfMQswCQYDVQQGEwJDQTELMAkGA1UE
|
||||
CAwCT04xDzANBgNVBAcMBk90dGF3YTERMA8GA1UECgwIQWx0aW5pdHkxCzAJBgNV
|
||||
BAsMAlFBMRIwEAYDVQQDDAlvcGVubGRhcDIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQC0Mbn//U56URavMgXm82FWP6vBdKuRydFX/L0M5XLlnAtk/IXG
|
||||
/T+4t7nOBJxWmTp/xpsPtSMALE4eFJpEUEqlpVbG5DfBzVWcYOWoMeRAcHWCDkzr
|
||||
PkB6I0dfF0Mm5hoaDhn+ZXjBWvoh/IlJdAnPg5mlejflJBQ7xtFC9eN6WjldXuRO
|
||||
vyntGNuMfVLgITHwXuH2yZ98G0mFO6TU/9dRY/Z3D6RTSzKdb17Yk/VnG+ry92u2
|
||||
0sgXIBvhuJuC3ksWLArwwFoMl8DVa05D4O2H76goGdCcQ0KzqBV8RPXAh3UcgP2e
|
||||
Zu90p2EGIhIk+sZTCkPd4dorxjL9nkRR86HdAgMBAAEwDQYJKoZIhvcNAQELBQAD
|
||||
ggEBAJWiCxJaTksv/BTsh/etxlDY5eHwqStqIuiovEQ8bhGAcKJ3bfWd/YTb8DUS
|
||||
hrLvXrXdOVC+U8PqPFXBpdOqcm5Dc233z52VgUCb+0EKv3lAzgKXRIo32h52skdK
|
||||
NnRrCHDeDzgfEIXR4MEJ99cLEaxWyXQhremmTYWHYznry9/4NYz40gCDxHn9dJAi
|
||||
KxFyDNxhtuKs58zp4PrBoo+542JurAoLPtRGOhdXpU2RkQVU/ho38HsAXDStAB5D
|
||||
vAoSxPuMHKgo17ffrb0oqU3didwaA9fIsz7Mr6RxmI7X03s7hLzNBq9FCqu0U3RR
|
||||
CX4zWGFNJu/ieSGVWLYKQzbYxp8=
|
||||
-----END CERTIFICATE-----
|
17
tests/testflows/ldap/configs/ldap3/certs/ldap.csr
Normal file
17
tests/testflows/ldap/configs/ldap3/certs/ldap.csr
Normal file
@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICpDCCAYwCAQAwXzELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMQ8wDQYDVQQH
|
||||
DAZPdHRhd2ExETAPBgNVBAoMCEFsdGluaXR5MQswCQYDVQQLDAJRQTESMBAGA1UE
|
||||
AwwJb3BlbmxkYXAyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtDG5
|
||||
//1OelEWrzIF5vNhVj+rwXSrkcnRV/y9DOVy5ZwLZPyFxv0/uLe5zgScVpk6f8ab
|
||||
D7UjACxOHhSaRFBKpaVWxuQ3wc1VnGDlqDHkQHB1gg5M6z5AeiNHXxdDJuYaGg4Z
|
||||
/mV4wVr6IfyJSXQJz4OZpXo35SQUO8bRQvXjelo5XV7kTr8p7RjbjH1S4CEx8F7h
|
||||
9smffBtJhTuk1P/XUWP2dw+kU0synW9e2JP1Zxvq8vdrttLIFyAb4bibgt5LFiwK
|
||||
8MBaDJfA1WtOQ+Dth++oKBnQnENCs6gVfET1wId1HID9nmbvdKdhBiISJPrGUwpD
|
||||
3eHaK8Yy/Z5EUfOh3QIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAEzIjZQOT5R7
|
||||
mEJg+RFpCSIoPn3xJ4/VMMyWqA3bTGZKpb4S6GxgsierY/87kPL7jZrMdGYB4Dc3
|
||||
2M3VWZGXlYo8vctH1zLE9VW6CzosUpl20lhdgydoCMz3RQqdJyK8aGeFTeLtk7G/
|
||||
TRCCUFUE6jaA+VtaCPCnOJSff3jUf76xguEu7dgTZgCKV7dtBqald8gIzF3D+AJJ
|
||||
7pEN2UrC3UR0xpe2cj2GhndQJ+WsIyft3zpNFzAO13j8ZPibuVP7oDWcW3ixNCWC
|
||||
213aeRVplJGof8Eo6llDxP+6Fwp1YmOoQmwB1Xm3t4ADn7FLJ14LONLB7q40KviG
|
||||
RyLyqu3IVOI=
|
||||
-----END CERTIFICATE REQUEST-----
|
27
tests/testflows/ldap/configs/ldap3/certs/ldap.key
Normal file
27
tests/testflows/ldap/configs/ldap3/certs/ldap.key
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAtDG5//1OelEWrzIF5vNhVj+rwXSrkcnRV/y9DOVy5ZwLZPyF
|
||||
xv0/uLe5zgScVpk6f8abD7UjACxOHhSaRFBKpaVWxuQ3wc1VnGDlqDHkQHB1gg5M
|
||||
6z5AeiNHXxdDJuYaGg4Z/mV4wVr6IfyJSXQJz4OZpXo35SQUO8bRQvXjelo5XV7k
|
||||
Tr8p7RjbjH1S4CEx8F7h9smffBtJhTuk1P/XUWP2dw+kU0synW9e2JP1Zxvq8vdr
|
||||
ttLIFyAb4bibgt5LFiwK8MBaDJfA1WtOQ+Dth++oKBnQnENCs6gVfET1wId1HID9
|
||||
nmbvdKdhBiISJPrGUwpD3eHaK8Yy/Z5EUfOh3QIDAQABAoIBADugMMIKWcuTxYPX
|
||||
c6iGZHEbxIPRTWyCcalB0nTQAAMGbabPAJ1l8432DZ+kWu806OybFXhPIfPOtVKy
|
||||
0pFEWE8TtPE/V0vj3C5Qye2sBLFmBRwyCzXUdZV00wseMXRPs9dnTyalAR5KMnbI
|
||||
j80kfpKSI2dkV9aU57UYBuq3Xrx/TCGItwL769D4ZZW9BvbpiTZApQQFZ0gwUFFn
|
||||
btPXGU9Ti8H4mfBuZWL+5CaZdqOo76+CXvMPaUK0F9MJp4yX3XxQLRNH3qz/Tyn7
|
||||
h7QOOo0XTqoUmzRw0N9QRVH5LRdSE5yq3aF9aFKjNW59exz+62pufOFadngzkpkn
|
||||
OKCzgWkCgYEA4mOWWMzdYwMn3GtfG7whqlqy7wOmMkNb81zTDQejHBV98dnj0AHr
|
||||
deurfKWzHrAh3DXo6tFeqUIgXabhBPS/0dEx/S5sgLFmuUZP05EUYahfWBgzzmM9
|
||||
C6Oe5xIMLzxsZCJczolsfkEsoFe4o0vkvuLYoQrQL7InzewcDy8cUxsCgYEAy8Na
|
||||
YCnanSNDY03Bulcni+5sF+opaHseeki1pv3nlw8TwsWuZF9ApS+yL7ck9jJjxBRR
|
||||
RC3KGmpoqIr0vTmUYS946ngQWXPE90zfuhJfM+NRv/q0oCjH0qAcxRbTkls5On9v
|
||||
oxJ8rO7gD6K85eHqasWdbCVzdZrobOXzay37tmcCgYBfyUUmw190cjReZauzH3Gb
|
||||
E48b5A5gu/Fe0cqWe8G+szU7rDZgnz9SAGnpbm6QMHPTKZgoKngD42+wUFhq8Wdr
|
||||
zjh5aDgOZ4EQKTjDSmI2Q7g7nNnmnESK9SrZl+BB6C3wXD2qQaj+7nKEUTlVFlpt
|
||||
jaucz+dwFtASp7Djl8pDOwKBgEtr2c3ycArt/ImLRIP2spqm+7e2YvFbcSKOOz6+
|
||||
iLRvTj8v8KcSYtlB2FC1F6dRa4AujQ4RbNduP6LzHDfWUkfOzJDtNBAIPAXVnJJB
|
||||
LqAEKkRHRghqT9x0i3GgS1vHDF3MwcO4mhFgserXr9ffUWeIEgbvrdcAKbv1Oa6Y
|
||||
bK1NAoGAGPm8ISmboDJynjBl9wMrkcy23Pwg9kmyocdWUHh0zMLDKriZNKYB6u/U
|
||||
C+/RTfkohPoHPzkeqWiHp7z3JhMItYUfTkNW6vMCxEGc0NEN6ZyMIjtiDPGN1n6O
|
||||
E7jmODFmj1AQICQGdV5SHp+yKvKyb0YHKyDwETbs4SZBXxVvjEw=
|
||||
-----END RSA PRIVATE KEY-----
|
64
tests/testflows/ldap/configs/ldap3/config/export.ldif
Normal file
64
tests/testflows/ldap/configs/ldap3/config/export.ldif
Normal file
@ -0,0 +1,64 @@
|
||||
# LDIF Export for dc=company,dc=com
|
||||
# Server: openldap (openldap)
|
||||
# Search Scope: sub
|
||||
# Search Filter: (objectClass=*)
|
||||
# Total Entries: 7
|
||||
#
|
||||
# Generated by phpLDAPadmin (http://phpldapadmin.sourceforge.net) on May 22, 2020 5:51 pm
|
||||
# Version: 1.2.5
|
||||
|
||||
# Entry 1: dc=company,dc=com
|
||||
#dn: dc=company,dc=com
|
||||
#dc: company
|
||||
#o: company
|
||||
#objectclass: top
|
||||
#objectclass: dcObject
|
||||
#objectclass: organization
|
||||
|
||||
# Entry 2: cn=admin,dc=company,dc=com
|
||||
#dn: cn=admin,dc=company,dc=com
|
||||
#cn: admin
|
||||
#description: LDAP administrator
|
||||
#objectclass: simpleSecurityObject
|
||||
#objectclass: organizationalRole
|
||||
#userpassword: {SSHA}eUEupkQCTvq9SkrxfWGSe5rX+orrjVbF
|
||||
|
||||
# Entry 3: ou=groups,dc=company,dc=com
|
||||
dn: ou=groups,dc=company,dc=com
|
||||
objectclass: organizationalUnit
|
||||
objectclass: top
|
||||
ou: groups
|
||||
|
||||
# Entry 4: cn=admin,ou=groups,dc=company,dc=com
|
||||
dn: cn=admin,ou=groups,dc=company,dc=com
|
||||
cn: admin
|
||||
gidnumber: 500
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
# Entry 5: cn=users,ou=groups,dc=company,dc=com
|
||||
dn: cn=users,ou=groups,dc=company,dc=com
|
||||
cn: users
|
||||
gidnumber: 501
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
# Entry 6: ou=users,dc=company,dc=com
|
||||
dn: ou=users,dc=company,dc=com
|
||||
objectclass: organizationalUnit
|
||||
objectclass: top
|
||||
ou: users
|
||||
|
||||
# Entry 7: cn=user3,ou=users,dc=company,dc=com
|
||||
dn: cn=user3,ou=users,dc=company,dc=com
|
||||
cn: user3
|
||||
gidnumber: 501
|
||||
givenname: John
|
||||
homedirectory: /home/users/user3
|
||||
objectclass: inetOrgPerson
|
||||
objectclass: posixAccount
|
||||
objectclass: top
|
||||
sn: User
|
||||
uid: user3
|
||||
uidnumber: 1003
|
||||
userpassword: user3
|
64
tests/testflows/ldap/configs/ldap4/config/export.ldif
Normal file
64
tests/testflows/ldap/configs/ldap4/config/export.ldif
Normal file
@ -0,0 +1,64 @@
|
||||
# LDIF Export for dc=company,dc=com
|
||||
# Server: openldap (openldap)
|
||||
# Search Scope: sub
|
||||
# Search Filter: (objectClass=*)
|
||||
# Total Entries: 7
|
||||
#
|
||||
# Generated by phpLDAPadmin (http://phpldapadmin.sourceforge.net) on May 22, 2020 5:51 pm
|
||||
# Version: 1.2.5
|
||||
|
||||
# Entry 1: dc=company,dc=com
|
||||
#dn: dc=company,dc=com
|
||||
#dc: company
|
||||
#o: company
|
||||
#objectclass: top
|
||||
#objectclass: dcObject
|
||||
#objectclass: organization
|
||||
|
||||
# Entry 2: cn=admin,dc=company,dc=com
|
||||
#dn: cn=admin,dc=company,dc=com
|
||||
#cn: admin
|
||||
#description: LDAP administrator
|
||||
#objectclass: simpleSecurityObject
|
||||
#objectclass: organizationalRole
|
||||
#userpassword: {SSHA}eUEupkQCTvq9SkrxfWGSe5rX+orrjVbF
|
||||
|
||||
# Entry 3: ou=groups,dc=company,dc=com
|
||||
dn: ou=groups,dc=company,dc=com
|
||||
objectclass: organizationalUnit
|
||||
objectclass: top
|
||||
ou: groups
|
||||
|
||||
# Entry 4: cn=admin,ou=groups,dc=company,dc=com
|
||||
dn: cn=admin,ou=groups,dc=company,dc=com
|
||||
cn: admin
|
||||
gidnumber: 500
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
# Entry 5: cn=users,ou=groups,dc=company,dc=com
|
||||
dn: cn=users,ou=groups,dc=company,dc=com
|
||||
cn: users
|
||||
gidnumber: 501
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
# Entry 6: ou=users,dc=company,dc=com
|
||||
dn: ou=users,dc=company,dc=com
|
||||
objectclass: organizationalUnit
|
||||
objectclass: top
|
||||
ou: users
|
||||
|
||||
# Entry 7: cn=user4,ou=users,dc=company,dc=com
|
||||
dn: cn=user4,ou=users,dc=company,dc=com
|
||||
cn: user4
|
||||
gidnumber: 501
|
||||
givenname: John
|
||||
homedirectory: /home/users/user4
|
||||
objectclass: inetOrgPerson
|
||||
objectclass: posixAccount
|
||||
objectclass: top
|
||||
sn: User
|
||||
uid: user4
|
||||
uidnumber: 1004
|
||||
userpassword: user4
|
64
tests/testflows/ldap/configs/ldap5/config/export.ldif
Normal file
64
tests/testflows/ldap/configs/ldap5/config/export.ldif
Normal file
@ -0,0 +1,64 @@
|
||||
# LDIF Export for dc=company,dc=com
|
||||
# Server: openldap (openldap)
|
||||
# Search Scope: sub
|
||||
# Search Filter: (objectClass=*)
|
||||
# Total Entries: 7
|
||||
#
|
||||
# Generated by phpLDAPadmin (http://phpldapadmin.sourceforge.net) on May 22, 2020 5:51 pm
|
||||
# Version: 1.2.5
|
||||
|
||||
# Entry 1: dc=company,dc=com
|
||||
#dn: dc=company,dc=com
|
||||
#dc: company
|
||||
#o: company
|
||||
#objectclass: top
|
||||
#objectclass: dcObject
|
||||
#objectclass: organization
|
||||
|
||||
# Entry 2: cn=admin,dc=company,dc=com
|
||||
#dn: cn=admin,dc=company,dc=com
|
||||
#cn: admin
|
||||
#description: LDAP administrator
|
||||
#objectclass: simpleSecurityObject
|
||||
#objectclass: organizationalRole
|
||||
#userpassword: {SSHA}eUEupkQCTvq9SkrxfWGSe5rX+orrjVbF
|
||||
|
||||
# Entry 3: ou=groups,dc=company,dc=com
|
||||
dn: ou=groups,dc=company,dc=com
|
||||
objectclass: organizationalUnit
|
||||
objectclass: top
|
||||
ou: groups
|
||||
|
||||
# Entry 4: cn=admin,ou=groups,dc=company,dc=com
|
||||
dn: cn=admin,ou=groups,dc=company,dc=com
|
||||
cn: admin
|
||||
gidnumber: 500
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
# Entry 5: cn=users,ou=groups,dc=company,dc=com
|
||||
dn: cn=users,ou=groups,dc=company,dc=com
|
||||
cn: users
|
||||
gidnumber: 501
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
# Entry 6: ou=users,dc=company,dc=com
|
||||
dn: ou=users,dc=company,dc=com
|
||||
objectclass: organizationalUnit
|
||||
objectclass: top
|
||||
ou: users
|
||||
|
||||
# Entry 7: cn=user5,ou=users,dc=company,dc=com
|
||||
dn: cn=user5,ou=users,dc=company,dc=com
|
||||
cn: user5
|
||||
gidnumber: 501
|
||||
givenname: John
|
||||
homedirectory: /home/users/user5
|
||||
objectclass: inetOrgPerson
|
||||
objectclass: posixAccount
|
||||
objectclass: top
|
||||
sn: User
|
||||
uid: user5
|
||||
uidnumber: 1005
|
||||
userpassword: user5
|
22
tests/testflows/ldap/configs/ldap5/ldap2/certs/ca.crt
Normal file
22
tests/testflows/ldap/configs/ldap5/ldap2/certs/ca.crt
Normal file
@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDlTCCAn2gAwIBAgIUJBqw2dHM2DDCZjYSkPOESlvDH6swDQYJKoZIhvcNAQEL
|
||||
BQAwWjELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMQ8wDQYDVQQHDAZPdHRhd2Ex
|
||||
ETAPBgNVBAoMCEFsdGluaXR5MQswCQYDVQQLDAJRQTENMAsGA1UEAwwEcm9vdDAe
|
||||
Fw0yMDA2MTExOTAzNDhaFw0zMDA2MDkxOTAzNDhaMFoxCzAJBgNVBAYTAkNBMQsw
|
||||
CQYDVQQIDAJPTjEPMA0GA1UEBwwGT3R0YXdhMREwDwYDVQQKDAhBbHRpbml0eTEL
|
||||
MAkGA1UECwwCUUExDTALBgNVBAMMBHJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQC9Irr0zGV+HCI2fZ0ht4hR5It4Sbjz4RwZV8ENRP/+TEz8l9eK
|
||||
J6ygxhKX7SMYzIs/jS9Gsq4plX1r2ujW1qRf8yLpR4+dGLP+jBRi1drj0XjZXosT
|
||||
SERjWzgPauWxL9LN8+l26eBAqz6fw5e0W8WRSTgf5iGiCcKOTmaATIUjP0CdfWKK
|
||||
qpktI4vhe++CXZFJ3usR+8KZ/FwwbCLJM/3J2HnbcXfcaYPYvr1tfqLudKSTbG9H
|
||||
M3+AVwjctdesc/0sbd51Zsm0ClQptMbuKnDCYauGg61kNkgbgPgRmH9Pzo67DtxF
|
||||
/WW+PtOzq8xLOifciQ9Piboy9QBSQZGwf4wzAgMBAAGjUzBRMB0GA1UdDgQWBBSi
|
||||
njya0RDozx3OZTLYFpwqYnlpIDAfBgNVHSMEGDAWgBSinjya0RDozx3OZTLYFpwq
|
||||
YnlpIDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBAD7VyFg7F
|
||||
U1C25KFvtauchAOjCW6w7U/b3z1dVZvcQ88/kH1VsLUcfGixlSilUEfPTJsi7OA0
|
||||
R5BQdh2GGcjUJv4iqEFGU05KvMVmRRKn08P62+ZhJxKMxG26VzcliRZzCMkI6d0W
|
||||
lFwI6nM45yeqdHVh5k4xbuJzqpbD9BtXXLI+/Ra9Fx8S9ETA3GdidpZLU5P1VLxq
|
||||
UuedfqyAVWZXpr6TAURGxouRmRzul9yFzbSUex+MLEIPrstjtEwV3+tBQZJz9xAS
|
||||
TVPj+Nv3LO7GCq54bdwkq1ioWbSL2hEmABkj6kdW/JwmfhGHf/2rirDVMzrTYw07
|
||||
dFJfAZC+FEsv
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,5 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MIGHAoGBAJitt2hhnpDViQ5ko2ipBMdjy+bZ6FR/WdZ987R7lQvBkKehPXmxtEyV
|
||||
AO6ofv5CZSDJokc5bUeBOAtg0EhMTCH82uPdwQvt58jRXcxXBg4JTjkx+oW9LBv2
|
||||
FdZsbaX8+SYivmiZ0Jp8T/HBm/4DA9VBS0O5GFRS4C7dHhmSTPfDAgEC
|
||||
-----END DH PARAMETERS-----
|
20
tests/testflows/ldap/configs/ldap5/ldap2/certs/ldap.crt
Normal file
20
tests/testflows/ldap/configs/ldap5/ldap2/certs/ldap.crt
Normal file
@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQDCCAigCFCJ7El0ntrGktZVTYTZd+OwtcJjBMA0GCSqGSIb3DQEBCwUAMFox
|
||||
CzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UEBwwGT3R0YXdhMREwDwYD
|
||||
VQQKDAhBbHRpbml0eTELMAkGA1UECwwCUUExDTALBgNVBAMMBHJvb3QwHhcNMjAw
|
||||
NjExMTkxMTQzWhcNMzAwNjA5MTkxMTQzWjBfMQswCQYDVQQGEwJDQTELMAkGA1UE
|
||||
CAwCT04xDzANBgNVBAcMBk90dGF3YTERMA8GA1UECgwIQWx0aW5pdHkxCzAJBgNV
|
||||
BAsMAlFBMRIwEAYDVQQDDAlvcGVubGRhcDIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQC0Mbn//U56URavMgXm82FWP6vBdKuRydFX/L0M5XLlnAtk/IXG
|
||||
/T+4t7nOBJxWmTp/xpsPtSMALE4eFJpEUEqlpVbG5DfBzVWcYOWoMeRAcHWCDkzr
|
||||
PkB6I0dfF0Mm5hoaDhn+ZXjBWvoh/IlJdAnPg5mlejflJBQ7xtFC9eN6WjldXuRO
|
||||
vyntGNuMfVLgITHwXuH2yZ98G0mFO6TU/9dRY/Z3D6RTSzKdb17Yk/VnG+ry92u2
|
||||
0sgXIBvhuJuC3ksWLArwwFoMl8DVa05D4O2H76goGdCcQ0KzqBV8RPXAh3UcgP2e
|
||||
Zu90p2EGIhIk+sZTCkPd4dorxjL9nkRR86HdAgMBAAEwDQYJKoZIhvcNAQELBQAD
|
||||
ggEBAJWiCxJaTksv/BTsh/etxlDY5eHwqStqIuiovEQ8bhGAcKJ3bfWd/YTb8DUS
|
||||
hrLvXrXdOVC+U8PqPFXBpdOqcm5Dc233z52VgUCb+0EKv3lAzgKXRIo32h52skdK
|
||||
NnRrCHDeDzgfEIXR4MEJ99cLEaxWyXQhremmTYWHYznry9/4NYz40gCDxHn9dJAi
|
||||
KxFyDNxhtuKs58zp4PrBoo+542JurAoLPtRGOhdXpU2RkQVU/ho38HsAXDStAB5D
|
||||
vAoSxPuMHKgo17ffrb0oqU3didwaA9fIsz7Mr6RxmI7X03s7hLzNBq9FCqu0U3RR
|
||||
CX4zWGFNJu/ieSGVWLYKQzbYxp8=
|
||||
-----END CERTIFICATE-----
|
17
tests/testflows/ldap/configs/ldap5/ldap2/certs/ldap.csr
Normal file
17
tests/testflows/ldap/configs/ldap5/ldap2/certs/ldap.csr
Normal file
@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICpDCCAYwCAQAwXzELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMQ8wDQYDVQQH
|
||||
DAZPdHRhd2ExETAPBgNVBAoMCEFsdGluaXR5MQswCQYDVQQLDAJRQTESMBAGA1UE
|
||||
AwwJb3BlbmxkYXAyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtDG5
|
||||
//1OelEWrzIF5vNhVj+rwXSrkcnRV/y9DOVy5ZwLZPyFxv0/uLe5zgScVpk6f8ab
|
||||
D7UjACxOHhSaRFBKpaVWxuQ3wc1VnGDlqDHkQHB1gg5M6z5AeiNHXxdDJuYaGg4Z
|
||||
/mV4wVr6IfyJSXQJz4OZpXo35SQUO8bRQvXjelo5XV7kTr8p7RjbjH1S4CEx8F7h
|
||||
9smffBtJhTuk1P/XUWP2dw+kU0synW9e2JP1Zxvq8vdrttLIFyAb4bibgt5LFiwK
|
||||
8MBaDJfA1WtOQ+Dth++oKBnQnENCs6gVfET1wId1HID9nmbvdKdhBiISJPrGUwpD
|
||||
3eHaK8Yy/Z5EUfOh3QIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAEzIjZQOT5R7
|
||||
mEJg+RFpCSIoPn3xJ4/VMMyWqA3bTGZKpb4S6GxgsierY/87kPL7jZrMdGYB4Dc3
|
||||
2M3VWZGXlYo8vctH1zLE9VW6CzosUpl20lhdgydoCMz3RQqdJyK8aGeFTeLtk7G/
|
||||
TRCCUFUE6jaA+VtaCPCnOJSff3jUf76xguEu7dgTZgCKV7dtBqald8gIzF3D+AJJ
|
||||
7pEN2UrC3UR0xpe2cj2GhndQJ+WsIyft3zpNFzAO13j8ZPibuVP7oDWcW3ixNCWC
|
||||
213aeRVplJGof8Eo6llDxP+6Fwp1YmOoQmwB1Xm3t4ADn7FLJ14LONLB7q40KviG
|
||||
RyLyqu3IVOI=
|
||||
-----END CERTIFICATE REQUEST-----
|
27
tests/testflows/ldap/configs/ldap5/ldap2/certs/ldap.key
Normal file
27
tests/testflows/ldap/configs/ldap5/ldap2/certs/ldap.key
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAtDG5//1OelEWrzIF5vNhVj+rwXSrkcnRV/y9DOVy5ZwLZPyF
|
||||
xv0/uLe5zgScVpk6f8abD7UjACxOHhSaRFBKpaVWxuQ3wc1VnGDlqDHkQHB1gg5M
|
||||
6z5AeiNHXxdDJuYaGg4Z/mV4wVr6IfyJSXQJz4OZpXo35SQUO8bRQvXjelo5XV7k
|
||||
Tr8p7RjbjH1S4CEx8F7h9smffBtJhTuk1P/XUWP2dw+kU0synW9e2JP1Zxvq8vdr
|
||||
ttLIFyAb4bibgt5LFiwK8MBaDJfA1WtOQ+Dth++oKBnQnENCs6gVfET1wId1HID9
|
||||
nmbvdKdhBiISJPrGUwpD3eHaK8Yy/Z5EUfOh3QIDAQABAoIBADugMMIKWcuTxYPX
|
||||
c6iGZHEbxIPRTWyCcalB0nTQAAMGbabPAJ1l8432DZ+kWu806OybFXhPIfPOtVKy
|
||||
0pFEWE8TtPE/V0vj3C5Qye2sBLFmBRwyCzXUdZV00wseMXRPs9dnTyalAR5KMnbI
|
||||
j80kfpKSI2dkV9aU57UYBuq3Xrx/TCGItwL769D4ZZW9BvbpiTZApQQFZ0gwUFFn
|
||||
btPXGU9Ti8H4mfBuZWL+5CaZdqOo76+CXvMPaUK0F9MJp4yX3XxQLRNH3qz/Tyn7
|
||||
h7QOOo0XTqoUmzRw0N9QRVH5LRdSE5yq3aF9aFKjNW59exz+62pufOFadngzkpkn
|
||||
OKCzgWkCgYEA4mOWWMzdYwMn3GtfG7whqlqy7wOmMkNb81zTDQejHBV98dnj0AHr
|
||||
deurfKWzHrAh3DXo6tFeqUIgXabhBPS/0dEx/S5sgLFmuUZP05EUYahfWBgzzmM9
|
||||
C6Oe5xIMLzxsZCJczolsfkEsoFe4o0vkvuLYoQrQL7InzewcDy8cUxsCgYEAy8Na
|
||||
YCnanSNDY03Bulcni+5sF+opaHseeki1pv3nlw8TwsWuZF9ApS+yL7ck9jJjxBRR
|
||||
RC3KGmpoqIr0vTmUYS946ngQWXPE90zfuhJfM+NRv/q0oCjH0qAcxRbTkls5On9v
|
||||
oxJ8rO7gD6K85eHqasWdbCVzdZrobOXzay37tmcCgYBfyUUmw190cjReZauzH3Gb
|
||||
E48b5A5gu/Fe0cqWe8G+szU7rDZgnz9SAGnpbm6QMHPTKZgoKngD42+wUFhq8Wdr
|
||||
zjh5aDgOZ4EQKTjDSmI2Q7g7nNnmnESK9SrZl+BB6C3wXD2qQaj+7nKEUTlVFlpt
|
||||
jaucz+dwFtASp7Djl8pDOwKBgEtr2c3ycArt/ImLRIP2spqm+7e2YvFbcSKOOz6+
|
||||
iLRvTj8v8KcSYtlB2FC1F6dRa4AujQ4RbNduP6LzHDfWUkfOzJDtNBAIPAXVnJJB
|
||||
LqAEKkRHRghqT9x0i3GgS1vHDF3MwcO4mhFgserXr9ffUWeIEgbvrdcAKbv1Oa6Y
|
||||
bK1NAoGAGPm8ISmboDJynjBl9wMrkcy23Pwg9kmyocdWUHh0zMLDKriZNKYB6u/U
|
||||
C+/RTfkohPoHPzkeqWiHp7z3JhMItYUfTkNW6vMCxEGc0NEN6ZyMIjtiDPGN1n6O
|
||||
E7jmODFmj1AQICQGdV5SHp+yKvKyb0YHKyDwETbs4SZBXxVvjEw=
|
||||
-----END RSA PRIVATE KEY-----
|
64
tests/testflows/ldap/configs/ldap5/ldap2/config/export.ldif
Normal file
64
tests/testflows/ldap/configs/ldap5/ldap2/config/export.ldif
Normal file
@ -0,0 +1,64 @@
|
||||
# LDIF Export for dc=company,dc=com
|
||||
# Server: openldap (openldap)
|
||||
# Search Scope: sub
|
||||
# Search Filter: (objectClass=*)
|
||||
# Total Entries: 7
|
||||
#
|
||||
# Generated by phpLDAPadmin (http://phpldapadmin.sourceforge.net) on May 22, 2020 5:51 pm
|
||||
# Version: 1.2.5
|
||||
|
||||
# Entry 1: dc=company,dc=com
|
||||
#dn: dc=company,dc=com
|
||||
#dc: company
|
||||
#o: company
|
||||
#objectclass: top
|
||||
#objectclass: dcObject
|
||||
#objectclass: organization
|
||||
|
||||
# Entry 2: cn=admin,dc=company,dc=com
|
||||
#dn: cn=admin,dc=company,dc=com
|
||||
#cn: admin
|
||||
#description: LDAP administrator
|
||||
#objectclass: simpleSecurityObject
|
||||
#objectclass: organizationalRole
|
||||
#userpassword: {SSHA}eUEupkQCTvq9SkrxfWGSe5rX+orrjVbF
|
||||
|
||||
# Entry 3: ou=groups,dc=company,dc=com
|
||||
dn: ou=groups,dc=company,dc=com
|
||||
objectclass: organizationalUnit
|
||||
objectclass: top
|
||||
ou: groups
|
||||
|
||||
# Entry 4: cn=admin,ou=groups,dc=company,dc=com
|
||||
dn: cn=admin,ou=groups,dc=company,dc=com
|
||||
cn: admin
|
||||
gidnumber: 500
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
# Entry 5: cn=users,ou=groups,dc=company,dc=com
|
||||
dn: cn=users,ou=groups,dc=company,dc=com
|
||||
cn: users
|
||||
gidnumber: 501
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
# Entry 6: ou=users,dc=company,dc=com
|
||||
dn: ou=users,dc=company,dc=com
|
||||
objectclass: organizationalUnit
|
||||
objectclass: top
|
||||
ou: users
|
||||
|
||||
# Entry 7: cn=user1,ou=users,dc=company,dc=com
|
||||
dn: cn=user1,ou=users,dc=company,dc=com
|
||||
cn: user1
|
||||
gidnumber: 501
|
||||
givenname: John1
|
||||
homedirectory: /home/users/user1
|
||||
objectclass: inetOrgPerson
|
||||
objectclass: posixAccount
|
||||
objectclass: top
|
||||
sn: User1
|
||||
uid: user1
|
||||
uidnumber: 1001
|
||||
userpassword: user1
|
28
tests/testflows/ldap/docker-compose/clickhouse-service.yml
Normal file
28
tests/testflows/ldap/docker-compose/clickhouse-service.yml
Normal file
@ -0,0 +1,28 @@
|
||||
version: '2.3'
|
||||
|
||||
services:
|
||||
clickhouse:
|
||||
image: yandex/clickhouse-integration-test
|
||||
expose:
|
||||
- "9000"
|
||||
- "9009"
|
||||
- "8123"
|
||||
volumes:
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d:/etc/clickhouse-server/config.d"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.d/:/etc/clickhouse-server/users.d"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/ssl:/etc/clickhouse-server/ssl"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml"
|
||||
- "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse"
|
||||
- "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge"
|
||||
entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log"
|
||||
healthcheck:
|
||||
test: clickhouse client --query='select 1'
|
||||
interval: 3s
|
||||
timeout: 2s
|
||||
retries: 40
|
||||
start_period: 2s
|
||||
cap_add:
|
||||
- SYS_PTRACE
|
||||
security_opt:
|
||||
- label:disable
|
162
tests/testflows/ldap/docker-compose/docker-compose.yml
Normal file
162
tests/testflows/ldap/docker-compose/docker-compose.yml
Normal file
@ -0,0 +1,162 @@
|
||||
version: '2.3'
|
||||
|
||||
services:
|
||||
openldap1:
|
||||
# plain text
|
||||
extends:
|
||||
file: openldap-service.yml
|
||||
service: openldap
|
||||
volumes:
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/ldap1/config:/container/service/slapd/assets/config/bootstrap/ldif/custom"
|
||||
|
||||
openldap2:
|
||||
# TLS - never
|
||||
extends:
|
||||
file: openldap-service.yml
|
||||
service: openldap
|
||||
environment:
|
||||
LDAP_TLS: "true"
|
||||
LDAP_TLS_CRT_FILENAME: "ldap.crt"
|
||||
LDAP_TLS_KEY_FILENAME: "ldap.key"
|
||||
LDAP_TLS_DH_PARAM_FILENAME: "dhparam.pem"
|
||||
LDAP_TLS_CA_CRT_FILENAME: "ca.crt"
|
||||
LDAP_TLS_ENFORCE: "false"
|
||||
LDAP_TLS_VERIFY_CLIENT: "never"
|
||||
volumes:
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/ldap2/config:/container/service/slapd/assets/config/bootstrap/ldif/custom"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/ldap2/certs:/container/service/slapd/assets/certs/"
|
||||
|
||||
openldap3:
|
||||
# plain text - custom port
|
||||
extends:
|
||||
file: openldap-service.yml
|
||||
service: openldap
|
||||
expose:
|
||||
- "3089"
|
||||
environment:
|
||||
LDAP_PORT: "3089"
|
||||
volumes:
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/ldap3/config:/container/service/slapd/assets/config/bootstrap/ldif/custom"
|
||||
|
||||
openldap4:
|
||||
# TLS - never custom port
|
||||
extends:
|
||||
file: openldap-service.yml
|
||||
service: openldap
|
||||
expose:
|
||||
- "3089"
|
||||
- "6036"
|
||||
environment:
|
||||
LDAP_PORT: "3089"
|
||||
LDAPS_PORT: "6036"
|
||||
LDAP_TLS: "true"
|
||||
LDAP_TLS_CRT_FILENAME: "ldap.crt"
|
||||
LDAP_TLS_KEY_FILENAME: "ldap.key"
|
||||
LDAP_TLS_DH_PARAM_FILENAME: "dhparam.pem"
|
||||
LDAP_TLS_CA_CRT_FILENAME: "ca.crt"
|
||||
LDAP_TLS_ENFORCE: "false"
|
||||
LDAP_TLS_VERIFY_CLIENT: "never"
|
||||
LDAP_TLS_CIPHER_SUITE: "SECURE256:+SECURE128:-VERS-TLS-ALL:+VERS-TLS1.2:-RSA:-DHE-DSS:-CAMELLIA-128-CBC:-CAMELLIA-256-CBC"
|
||||
volumes:
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/ldap4/config:/container/service/slapd/assets/config/bootstrap/ldif/custom"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/ldap4/certs:/container/service/slapd/assets/certs/"
|
||||
|
||||
openldap5:
|
||||
# TLS - try
|
||||
extends:
|
||||
file: openldap-service.yml
|
||||
service: openldap
|
||||
environment:
|
||||
LDAP_TLS: "true"
|
||||
LDAP_TLS_CRT_FILENAME: "ldap.crt"
|
||||
LDAP_TLS_KEY_FILENAME: "ldap.key"
|
||||
LDAP_TLS_DH_PARAM_FILENAME: "dhparam.pem"
|
||||
LDAP_TLS_CA_CRT_FILENAME: "ca.crt"
|
||||
LDAP_TLS_ENFORCE: "false"
|
||||
LDAP_TLS_VERIFY_CLIENT: "try"
|
||||
volumes:
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/ldap5/config:/container/service/slapd/assets/config/bootstrap/ldif/custom"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/ldap5/certs:/container/service/slapd/assets/certs/"
|
||||
|
||||
phpldapadmin:
|
||||
extends:
|
||||
file: openldap-service.yml
|
||||
service: phpldapadmin
|
||||
environment:
|
||||
PHPLDAPADMIN_LDAP_HOSTS: "openldap1"
|
||||
depends_on:
|
||||
openldap1:
|
||||
condition: service_healthy
|
||||
|
||||
zookeeper:
|
||||
extends:
|
||||
file: zookeeper-service.yml
|
||||
service: zookeeper
|
||||
|
||||
clickhouse1:
|
||||
extends:
|
||||
file: clickhouse-service.yml
|
||||
service: clickhouse
|
||||
hostname: clickhouse1
|
||||
volumes:
|
||||
- "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/database/:/var/lib/clickhouse/"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/logs/:/var/log/clickhouse-server/"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/config.d:/etc/clickhouse-server/config.d"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/users.d:/etc/clickhouse-server/users.d"
|
||||
depends_on:
|
||||
zookeeper:
|
||||
condition: service_healthy
|
||||
|
||||
clickhouse2:
|
||||
extends:
|
||||
file: clickhouse-service.yml
|
||||
service: clickhouse
|
||||
hostname: clickhouse2
|
||||
volumes:
|
||||
- "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/database/:/var/lib/clickhouse/"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/logs/:/var/log/clickhouse-server/"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/config.d:/etc/clickhouse-server/config.d"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/users.d:/etc/clickhouse-server/users.d"
|
||||
depends_on:
|
||||
zookeeper:
|
||||
condition: service_healthy
|
||||
|
||||
clickhouse3:
|
||||
extends:
|
||||
file: clickhouse-service.yml
|
||||
service: clickhouse
|
||||
hostname: clickhouse3
|
||||
volumes:
|
||||
- "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/database/:/var/lib/clickhouse/"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/logs/:/var/log/clickhouse-server/"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/config.d:/etc/clickhouse-server/config.d"
|
||||
- "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/users.d:/etc/clickhouse-server/users.d"
|
||||
depends_on:
|
||||
zookeeper:
|
||||
condition: service_healthy
|
||||
|
||||
# dummy service which does nothing, but allows to postpone
|
||||
# 'docker-compose up -d' till all dependecies will go healthy
|
||||
all_services_ready:
|
||||
image: hello-world
|
||||
depends_on:
|
||||
clickhouse1:
|
||||
condition: service_healthy
|
||||
clickhouse2:
|
||||
condition: service_healthy
|
||||
clickhouse3:
|
||||
condition: service_healthy
|
||||
zookeeper:
|
||||
condition: service_healthy
|
||||
openldap1:
|
||||
condition: service_healthy
|
||||
openldap2:
|
||||
condition: service_healthy
|
||||
openldap3:
|
||||
condition: service_healthy
|
||||
openldap4:
|
||||
condition: service_healthy
|
||||
openldap5:
|
||||
condition: service_healthy
|
||||
phpldapadmin:
|
||||
condition: service_healthy
|
40
tests/testflows/ldap/docker-compose/openldap-service.yml
Normal file
40
tests/testflows/ldap/docker-compose/openldap-service.yml
Normal file
@ -0,0 +1,40 @@
|
||||
version: '2.3'
|
||||
|
||||
services:
|
||||
openldap:
|
||||
image: osixia/openldap:1.4.0
|
||||
command: "--copy-service --loglevel debug"
|
||||
environment:
|
||||
LDAP_ORGANIZATION: "company"
|
||||
LDAP_DOMAIN: "company.com"
|
||||
LDAP_ADMIN_PASSWORD: "admin"
|
||||
LDAP_TLS: "false"
|
||||
expose:
|
||||
- "389"
|
||||
- "636"
|
||||
healthcheck:
|
||||
test: echo 1
|
||||
interval: 3s
|
||||
timeout: 2s
|
||||
retries: 5
|
||||
start_period: 2s
|
||||
security_opt:
|
||||
- label:disable
|
||||
|
||||
|
||||
phpldapadmin:
|
||||
image: osixia/phpldapadmin:0.9.0
|
||||
container_name: phpldapadmin
|
||||
environment:
|
||||
PHPLDAPADMIN_HTTPS=false:
|
||||
ports:
|
||||
- "8080:80"
|
||||
healthcheck:
|
||||
test: echo 1
|
||||
interval: 3s
|
||||
timeout: 2s
|
||||
retries: 5
|
||||
start_period: 2s
|
||||
security_opt:
|
||||
- label:disable
|
||||
|
18
tests/testflows/ldap/docker-compose/zookeeper-service.yml
Normal file
18
tests/testflows/ldap/docker-compose/zookeeper-service.yml
Normal file
@ -0,0 +1,18 @@
|
||||
version: '2.3'
|
||||
|
||||
services:
|
||||
zookeeper:
|
||||
image: zookeeper:3.4.12
|
||||
expose:
|
||||
- "2181"
|
||||
environment:
|
||||
ZOO_TICK_TIME: 500
|
||||
ZOO_MY_ID: 1
|
||||
healthcheck:
|
||||
test: echo stat | nc localhost 2181
|
||||
interval: 3s
|
||||
timeout: 2s
|
||||
retries: 5
|
||||
start_period: 2s
|
||||
security_opt:
|
||||
- label:disable
|
54
tests/testflows/ldap/regression.py
Executable file
54
tests/testflows/ldap/regression.py
Executable file
@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
from testflows.core import *
|
||||
|
||||
append_path(sys.path, "..")
|
||||
|
||||
from helpers.cluster import Cluster
|
||||
from helpers.argparser import argparser
|
||||
from ldap.requirements import *
|
||||
|
||||
# Cross-outs of known fails
|
||||
xfails = {
|
||||
"connection protocols/tls/tls_require_cert='try'":
|
||||
[(Fail, "can't be tested with self-signed certificates")],
|
||||
"connection protocols/tls/tls_require_cert='demand'":
|
||||
[(Fail, "can't be tested with self-signed certificates")],
|
||||
"connection protocols/starttls/tls_require_cert='try'":
|
||||
[(Fail, "can't be tested with self-signed certificates")],
|
||||
"connection protocols/starttls/tls_require_cert='demand'":
|
||||
[(Fail, "can't be tested with self-signed certificates")],
|
||||
"connection protocols/tls require cert default demand":
|
||||
[(Fail, "can't be tested with self-signed certificates")],
|
||||
"connection protocols/starttls with custom port":
|
||||
[(Fail, "it seems that starttls is not enabled by default on custom plain-text ports in LDAP server")],
|
||||
"connection protocols/tls cipher suite":
|
||||
[(Fail, "can't get it to work")]
|
||||
}
|
||||
|
||||
@TestFeature
|
||||
@Name("ldap authentication")
|
||||
@ArgumentParser(argparser)
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication("1.0")
|
||||
)
|
||||
@XFails(xfails)
|
||||
def regression(self, local, clickhouse_binary_path):
|
||||
"""ClickHouse integration with LDAP regression module.
|
||||
"""
|
||||
nodes = {
|
||||
"clickhouse": ("clickhouse1", "clickhouse2", "clickhouse3"),
|
||||
}
|
||||
|
||||
with Cluster(local, clickhouse_binary_path, nodes=nodes) as cluster:
|
||||
self.context.cluster = cluster
|
||||
|
||||
Scenario(run=load("ldap.tests.sanity", "scenario"))
|
||||
Scenario(run=load("ldap.tests.multiple_servers", "scenario"))
|
||||
Feature(run=load("ldap.tests.connections", "feature"))
|
||||
Feature(run=load("ldap.tests.server_config", "feature"))
|
||||
Feature(run=load("ldap.tests.user_config", "feature"))
|
||||
Feature(run=load("ldap.tests.authentications", "feature"))
|
||||
|
||||
if main():
|
||||
regression()
|
1
tests/testflows/ldap/requirements/__init__.py
Normal file
1
tests/testflows/ldap/requirements/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .requirements import *
|
928
tests/testflows/ldap/requirements/requirements.py
Normal file
928
tests/testflows/ldap/requirements/requirements.py
Normal file
@ -0,0 +1,928 @@
|
||||
# These requirements were auto generated
|
||||
# from software requirements specification (SRS)
|
||||
# document by TestFlows v1.6.200623.1103543.
|
||||
# Do not edit by hand but re-generate instead
|
||||
# using 'tfs requirements generate' command.
|
||||
from testflows.core import Requirement
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support user authentication via an [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_MultipleServers = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.MultipleServers',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support specifying multiple [LDAP] servers that can be used to authenticate\n'
|
||||
'users.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Protocol_PlainText = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Protocol.PlainText',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support user authentication using plain text `ldap://` non secure protocol.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Protocol_TLS = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Protocol.TLS',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support user authentication using `SSL/TLS` `ldaps://` secure protocol.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Protocol_StartTLS = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Protocol.StartTLS',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support user authentication using legacy `StartTLS` protocol which is a\n'
|
||||
'plain text `ldap://` protocol that is upgraded to [TLS].\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_TLS_Certificate_Validation = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.TLS.Certificate.Validation',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support certificate validation used for [TLS] connections.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_TLS_Certificate_SelfSigned = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.TLS.Certificate.SelfSigned',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support self-signed certificates for [TLS] connections.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_TLS_Certificate_SpecificCertificationAuthority = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.TLS.Certificate.SpecificCertificationAuthority',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support certificates signed by specific Certification Authority for [TLS] connections.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Server_Configuration_Invalid = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Server.Configuration.Invalid',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL return an error and prohibit user login if [LDAP] server configuration is not valid.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_User_Configuration_Invalid = Requirement(
|
||||
name='RQ.SRS-007.LDAP.User.Configuration.Invalid',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL return an error and prohibit user login if user configuration is not valid.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Mechanism_Anonymous = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Mechanism.Anonymous',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL return an error and prohibit authentication using [Anonymous Authentication Mechanism of Simple Bind]\n'
|
||||
'authentication mechanism.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Mechanism_Unauthenticated = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Mechanism.Unauthenticated',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL return an error and prohibit authentication using [Unauthenticated Authentication Mechanism of Simple Bind]\n'
|
||||
'authentication mechanism.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Mechanism_NamePassword = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Mechanism.NamePassword',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL allow authentication using only [Name/Password Authentication Mechanism of Simple Bind]\n'
|
||||
'authentication mechanism.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Valid = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Valid',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL only allow user authentication using [LDAP] server if and only if\n'
|
||||
'user name and password match [LDAP] server records for the user.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Invalid',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL return an error and prohibit authentication if either user name or password\n'
|
||||
'do not match [LDAP] server records for the user.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid_DeletedUser = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Invalid.DeletedUser',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL return an error and prohibit authentication if the user\n'
|
||||
'has been deleted from the [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_UsernameChanged = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.UsernameChanged',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL return an error and prohibit authentication if the username is changed\n'
|
||||
'on the [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_PasswordChanged = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.PasswordChanged',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL return an error and prohibit authentication if the password \n'
|
||||
'for the user is changed on the [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_LDAPServerRestart = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.LDAPServerRestart',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support authenticating users after [LDAP] server is restarted.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_ClickHouseServerRestart = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.ClickHouseServerRestart',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support authenticating users after server is restarted.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Parallel = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Parallel',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support parallel authentication of users using [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Parallel_ValidAndInvalid = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Parallel.ValidAndInvalid',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support authentication of valid users and \n'
|
||||
'prohibit authentication of invalid users using [LDAP] server \n'
|
||||
'in parallel without having invalid attempts affecting valid authentications.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_UnreachableServer = Requirement(
|
||||
name='RQ.SRS-007.LDAP.UnreachableServer',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL return an error and prohibit user login if [LDAP] server is unreachable.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Name = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.Name',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL not support empty string as a server name.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Host = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.Host',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `<host>` parameter to specify [LDAP]\n'
|
||||
'server hostname or IP, this parameter SHALL be mandatory and SHALL not be empty. \n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Port = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.Port',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `<port>` parameter to specify [LDAP] server port.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Port_Default = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.Port.Default',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL use default port number `636` if `enable_tls` is set to `yes` or `389` otherwise.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_AuthDN_Prefix = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.AuthDN.Prefix',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `<auth_dn_prefix>` parameter to specify the prefix\n'
|
||||
'of value used to construct the DN to bound to during authentication via [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_AuthDN_Suffix = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.AuthDN.Suffix',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `<auth_dn_suffix>` parameter to specify the suffix \n'
|
||||
'of value used to construct the DN to bound to during authentication via [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_AuthDN_Value = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.AuthDN.Value',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL construct DN as `auth_dn_prefix + escape(user_name) + auth_dn_suffix` string.\n'
|
||||
'\n'
|
||||
"> This implies that auth_dn_suffix should usually have comma ',' as its first non-space character.\n"
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.EnableTLS',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `<enable_tls>` parameter to trigger the use of secure connection to the [LDAP] server. \n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS_Options_Default = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.EnableTLS.Options.Default',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL use `yes` value as the default for `<enable_tls>` parameter\n'
|
||||
'to enable SSL/TLS `ldaps://` protocol. \n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS_Options_No = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.EnableTLS.Options.No',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support specifying `no` as the value of `<enable_tls>` parameter to enable \n'
|
||||
'plain text `ldap://` protocol.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS_Options_Yes = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.EnableTLS.Options.Yes',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support specifying `yes` as the value of `<enable_tls>` parameter to enable \n'
|
||||
'SSL/TLS `ldaps://` protocol.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS_Options_StartTLS = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.EnableTLS.Options.StartTLS',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support specifying `starttls` as the value of `<enable_tls>` parameter to enable \n'
|
||||
'legacy `StartTLS` protocol that used plain text `ldap://` protocol, upgraded to [TLS].\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSMinimumProtocolVersion = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSMinimumProtocolVersion',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `<tls_minimum_protocol_version>` parameter to specify \n'
|
||||
'the minimum protocol version of SSL/TLS.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSMinimumProtocolVersion_Values = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSMinimumProtocolVersion.Values',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support specifying `ssl2`, `ssl3`, `tls1.0`, `tls1.1`, and `tls1.2`\n'
|
||||
'as a value of the `<tls_minimum_protocol_version>` parameter.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSMinimumProtocolVersion_Default = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSMinimumProtocolVersion.Default',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL set `tls1.2` as the default value of the `<tls_minimum_protocol_version>` parameter. \n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSRequireCert',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `<tls_require_cert>` parameter to specify [TLS] peer \n'
|
||||
'certificate verification behavior.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert_Options_Default = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSRequireCert.Options.Default',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL use `demand` value as the default for the `<tls_require_cert>` parameter.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert_Options_Demand = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSRequireCert.Options.Demand',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support specifying `demand` as the value of `<tls_require_cert>` parameter to\n'
|
||||
'enable requesting of client certificate. If no certificate is provided, or a bad certificate is\n'
|
||||
'provided, the session SHALL be immediately terminated.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert_Options_Allow = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSRequireCert.Options.Allow',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support specifying `allow` as the value of `<tls_require_cert>` parameter to\n'
|
||||
'enable requesting of client certificate. If no\n'
|
||||
'certificate is provided, the session SHALL proceed normally. \n'
|
||||
'If a bad certificate is provided, it SHALL be ignored and the session SHALL proceed normally.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert_Options_Try = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSRequireCert.Options.Try',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support specifying `try` as the value of `<tls_require_cert>` parameter to\n'
|
||||
'enable requesting of client certificate. If no certificate is provided, the session\n'
|
||||
'SHALL proceed normally. If a bad certificate is provided, the session SHALL be \n'
|
||||
'immediately terminated.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert_Options_Never = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSRequireCert.Options.Never',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support specifying `never` as the value of `<tls_require_cert>` parameter to\n'
|
||||
'disable requesting of client certificate.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSCertFile = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSCertFile',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `<tls_cert_file>` to specify the path to certificate file used by\n'
|
||||
'[ClickHouse] to establish connection with the [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSKeyFile = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSKeyFile',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `<tls_key_file>` to specify the path to key file for the certificate\n'
|
||||
'specified by the `<tls_cert_file>` parameter.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSCACertDir = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSCACertDir',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `<tls_ca_cert_dir>` parameter to specify to a path to \n'
|
||||
'the directory containing [CA] certificates used to verify certificates provided by the [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSCACertFile = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSCACertFile',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `<tls_ca_cert_file>` parameter to specify a path to a specific \n'
|
||||
'[CA] certificate file used to verify certificates provided by the [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSCipherSuite = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.TLSCipherSuite',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support `tls_cipher_suite` parameter to specify allowed cipher suites.\n'
|
||||
'The value SHALL use the same format as the `ciphersuites` in the [OpenSSL Ciphers].\n'
|
||||
'\n'
|
||||
'For example, \n'
|
||||
'\n'
|
||||
'```xml\n'
|
||||
'<tls_cipher_suite>ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384</tls_cipher_suite>\n'
|
||||
'```\n'
|
||||
'\n'
|
||||
'The available suites SHALL depend on the [OpenSSL] library version and variant used to build\n'
|
||||
'[ClickHouse] and therefore might change.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Syntax = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.Server.Syntax',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support the following example syntax to create an entry for an [LDAP] server inside the `config.xml`\n'
|
||||
'configuration file or of any configuration file inside the `config.d` directory.\n'
|
||||
'\n'
|
||||
'```xml\n'
|
||||
'<yandex>\n'
|
||||
' <my_ldap_server>\n'
|
||||
' <host>localhost</host>\n'
|
||||
' <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'
|
||||
' <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'
|
||||
' <tls_cert_file>/path/to/tls_cert_file</tls_cert_file>\n'
|
||||
' <tls_key_file>/path/to/tls_key_file</tls_key_file>\n'
|
||||
' <tls_ca_cert_file>/path/to/tls_ca_cert_file</tls_ca_cert_file>\n'
|
||||
' <tls_ca_cert_dir>/path/to/tls_ca_cert_dir</tls_ca_cert_dir>\n'
|
||||
' <tls_cipher_suite>ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384</tls_cipher_suite>\n'
|
||||
' </my_ldap_server>\n'
|
||||
'</yandex>\n'
|
||||
'```\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_Syntax = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.Syntax',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support the following example syntax to create a user that is authenticated using\n'
|
||||
'an [LDAP] server inside the `users.xml` file or any configuration file inside the `users.d` directory.\n'
|
||||
'\n'
|
||||
'```xml\n'
|
||||
'<yandex>\n'
|
||||
' <users>\n'
|
||||
' <user_name>\n'
|
||||
' <ldap>\n'
|
||||
' <server>my_ldap_server</server>\n'
|
||||
' </ldap>\n'
|
||||
' </user_name>\n'
|
||||
' </users>\n'
|
||||
'</yandex>\n'
|
||||
'```\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_Name_Empty = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.Name.Empty',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL not support empty string as a user name.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_BothPasswordAndLDAP = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.BothPasswordAndLDAP',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL throw an error if `<ldap>` is specified for the user and at the same \n'
|
||||
'time user configuration contains any of the `<password*>` entries.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_LDAP_InvalidServerName_NotDefined = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.NotDefined',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL throw an error during any authentification attempt\n'
|
||||
'if the name of the [LDAP] server used inside the `<ldap>` entry \n'
|
||||
'is not defined in the `<ldap_servers>` section. \n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_LDAP_InvalidServerName_Empty = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.LDAP.InvalidServerName.Empty',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL throw an error during any authentification attempt\n'
|
||||
'if the name of the [LDAP] server used inside the `<ldap>` entry\n'
|
||||
'is empty. \n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_OnlyOneServer = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.OnlyOneServer',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support specifying only one [LDAP] server for a given user.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_Name_Long = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.Name.Long',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support long user names of at least 256 bytes\n'
|
||||
'to specify users that can be authenticated using an [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Configuration_User_Name_UTF8 = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Configuration.User.Name.UTF8',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support user names that contain [UTF-8] characters.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Username_Empty = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Username.Empty',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL not support authenticating users with empty username.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Username_Long = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Username.Long',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support authenticating users with a long username of at least 256 bytes.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Username_UTF8 = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Username.UTF8',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support authentication users with a username that contains [UTF-8] characters.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Password_Empty = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Password.Empty',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL not support authenticating users with empty passwords\n'
|
||||
'even if an empty password is valid for the user and\n'
|
||||
'is allowed by the [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Password_Long = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Password.Long',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support long password of at least 256 bytes\n'
|
||||
'that can be used to authenticate users using an [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
||||
|
||||
RQ_SRS_007_LDAP_Authentication_Password_UTF8 = Requirement(
|
||||
name='RQ.SRS-007.LDAP.Authentication.Password.UTF8',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL support [UTF-8] characters in passwords\n'
|
||||
'used to authenticate users using an [LDAP] server.\n'
|
||||
),
|
||||
link=None
|
||||
)
|
464
tests/testflows/ldap/tests/authentications.py
Normal file
464
tests/testflows/ldap/tests/authentications.py
Normal file
@ -0,0 +1,464 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import random
|
||||
|
||||
from multiprocessing.dummy import Pool
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
from ldap.tests.common import *
|
||||
from ldap.requirements import *
|
||||
|
||||
servers = {
|
||||
"openldap1": {
|
||||
"host": "openldap1",
|
||||
"port": "389",
|
||||
"enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
},
|
||||
"openldap2": {
|
||||
"host": "openldap2",
|
||||
"port": "636",
|
||||
"enable_tls": "yes",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||||
"tls_require_cert": "never",
|
||||
}
|
||||
}
|
||||
|
||||
@TestStep(When)
|
||||
@Name("I login as {username} and execute query")
|
||||
def login_and_execute_query(self, username, password, exitcode=None, message=None, steps=True):
|
||||
self.context.node.query("SELECT 1",
|
||||
settings=[("user", username), ("password", password)],
|
||||
exitcode=exitcode or 0,
|
||||
message=message, steps=steps)
|
||||
|
||||
@TestScenario
|
||||
def add_user_to_ldap_and_login(self, server, user=None, ch_user=None, login=None, exitcode=None, message=None):
|
||||
"""Add user to LDAP and ClickHouse and then try to login."""
|
||||
self.context.ldap_node = self.context.cluster.node(server)
|
||||
|
||||
if ch_user is None:
|
||||
ch_user = {}
|
||||
if login is None:
|
||||
login = {}
|
||||
if user is None:
|
||||
user = {"cn": "myuser", "userpassword": "myuser"}
|
||||
|
||||
with ldap_user(**user) as user:
|
||||
ch_user["username"] = ch_user.get("username", user["cn"])
|
||||
ch_user["server"] = ch_user.get("server", user["_server"])
|
||||
|
||||
with ldap_authenticated_users(ch_user, config_file=f"ldap_users_{getuid()}.xml", restart=True):
|
||||
username = login.get("username", user["cn"])
|
||||
password = login.get("password", user["userpassword"])
|
||||
login_and_execute_query(username=username, password=password, exitcode=exitcode, message=message)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Parallel("1.0"),
|
||||
RQ_SRS_007_LDAP_Authentication_Parallel_ValidAndInvalid("1.0")
|
||||
)
|
||||
def parallel_login(self, server, user_count=10, timeout=200):
|
||||
"""Check that login of valid and invalid LDAP authenticated users works in parallel."""
|
||||
self.context.ldap_node = self.context.cluster.node(server)
|
||||
user = None
|
||||
|
||||
users = [{"cn": f"parallel_user{i}", "userpassword": randomword(20)} for i in range(user_count)]
|
||||
|
||||
with ldap_users(*users):
|
||||
with ldap_authenticated_users(*[{"username": user["cn"], "server": server} for user in users]):
|
||||
|
||||
def login_with_valid_username_and_password(users, i, iterations=10):
|
||||
with When(f"valid users try to login #{i}"):
|
||||
for i in range(iterations):
|
||||
random_user = users[random.randint(0, len(users)-1)]
|
||||
login_and_execute_query(username=random_user["cn"], password=random_user["userpassword"], steps=False)
|
||||
|
||||
def login_with_valid_username_and_invalid_password(users, i, iterations=10):
|
||||
with When(f"users try to login with valid username and invalid password #{i}"):
|
||||
for i in range(iterations):
|
||||
random_user = users[random.randint(0, len(users)-1)]
|
||||
login_and_execute_query(username=random_user["cn"],
|
||||
password=(random_user["userpassword"] + randomword(1)),
|
||||
exitcode=4,
|
||||
message=f"DB::Exception: {random_user['cn']}: Authentication failed: password is incorrect or there is no user with such name",
|
||||
steps=False)
|
||||
|
||||
def login_with_invalid_username_and_valid_password(users, i, iterations=10):
|
||||
with When(f"users try to login with invalid username and valid password #{i}"):
|
||||
for i in range(iterations):
|
||||
random_user = dict(users[random.randint(0, len(users)-1)])
|
||||
random_user["cn"] += randomword(1)
|
||||
login_and_execute_query(username=random_user["cn"],
|
||||
password=random_user["userpassword"],
|
||||
exitcode=4,
|
||||
message=f"DB::Exception: {random_user['cn']}: Authentication failed: password is incorrect or there is no user with such name",
|
||||
steps=False)
|
||||
|
||||
with When("I login in parallel"):
|
||||
p = Pool(15)
|
||||
tasks = []
|
||||
for i in range(5):
|
||||
tasks.append(p.apply_async(login_with_valid_username_and_password, (users, i, 50,)))
|
||||
tasks.append(p.apply_async(login_with_valid_username_and_invalid_password, (users, i, 50,)))
|
||||
tasks.append(p.apply_async(login_with_invalid_username_and_valid_password, (users, i, 50,)))
|
||||
|
||||
with Then("it should work"):
|
||||
for task in tasks:
|
||||
task.get(timeout=timeout)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid("1.0"),
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid_DeletedUser("1.0")
|
||||
)
|
||||
def login_after_user_is_deleted_from_ldap(self, server):
|
||||
"""Check that login fails after user is deleted from LDAP."""
|
||||
self.context.ldap_node = self.context.cluster.node(server)
|
||||
user = None
|
||||
|
||||
try:
|
||||
with Given(f"I add user to LDAP"):
|
||||
user = {"cn": "myuser", "userpassword": "myuser"}
|
||||
user = add_user_to_ldap(**user)
|
||||
|
||||
with ldap_authenticated_users({"username": user["cn"], "server": server}, config_file=f"ldap_users_{getuid()}.xml", restart=True):
|
||||
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||||
|
||||
with When("I delete this user from LDAP"):
|
||||
delete_user_from_ldap(user)
|
||||
|
||||
with Then("when I try to login again it should fail"):
|
||||
login_and_execute_query(username=user["cn"], password=user["userpassword"],
|
||||
exitcode=4,
|
||||
message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
|
||||
)
|
||||
finally:
|
||||
with Finally("I make sure LDAP user is deleted"):
|
||||
if user is not None:
|
||||
delete_user_from_ldap(user, exitcode=None)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid("1.0"),
|
||||
RQ_SRS_007_LDAP_Authentication_PasswordChanged("1.0")
|
||||
)
|
||||
def login_after_user_password_changed_in_ldap(self, server):
|
||||
"""Check that login fails after user password is changed in LDAP."""
|
||||
self.context.ldap_node = self.context.cluster.node(server)
|
||||
user = None
|
||||
|
||||
try:
|
||||
with Given(f"I add user to LDAP"):
|
||||
user = {"cn": "myuser", "userpassword": "myuser"}
|
||||
user = add_user_to_ldap(**user)
|
||||
|
||||
with ldap_authenticated_users({"username": user["cn"], "server": server}, config_file=f"ldap_users_{getuid()}.xml", restart=True):
|
||||
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||||
|
||||
with When("I change user password in LDAP"):
|
||||
change_user_password_in_ldap(user, "newpassword")
|
||||
|
||||
with Then("when I try to login again it should fail"):
|
||||
login_and_execute_query(username=user["cn"], password=user["userpassword"],
|
||||
exitcode=4,
|
||||
message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
|
||||
)
|
||||
|
||||
with And("when I try to login with the new password it should work"):
|
||||
login_and_execute_query(username=user["cn"], password="newpassword")
|
||||
|
||||
finally:
|
||||
with Finally("I make sure LDAP user is deleted"):
|
||||
if user is not None:
|
||||
delete_user_from_ldap(user, exitcode=None)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid("1.0"),
|
||||
RQ_SRS_007_LDAP_Authentication_UsernameChanged("1.0")
|
||||
)
|
||||
def login_after_user_cn_changed_in_ldap(self, server):
|
||||
"""Check that login fails after user cn is changed in LDAP."""
|
||||
self.context.ldap_node = self.context.cluster.node(server)
|
||||
user = None
|
||||
|
||||
try:
|
||||
with Given(f"I add user to LDAP"):
|
||||
user = {"cn": "myuser", "userpassword": "myuser"}
|
||||
user = add_user_to_ldap(**user)
|
||||
|
||||
with ldap_authenticated_users({"username": user["cn"], "server": server}, config_file=f"ldap_users_{getuid()}.xml", restart=True):
|
||||
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||||
|
||||
with When("I change user password in LDAP"):
|
||||
change_user_cn_in_ldap(user, "myuser2")
|
||||
|
||||
with Then("when I try to login again it should fail"):
|
||||
login_and_execute_query(username=user["cn"], password=user["userpassword"],
|
||||
exitcode=4,
|
||||
message=f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
|
||||
)
|
||||
finally:
|
||||
with Finally("I make sure LDAP user is deleted"):
|
||||
if user is not None:
|
||||
delete_user_from_ldap(user, exitcode=None)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Valid("1.0"),
|
||||
RQ_SRS_007_LDAP_Authentication_LDAPServerRestart("1.0")
|
||||
)
|
||||
def login_after_ldap_server_is_restarted(self, server, timeout=60):
|
||||
"""Check that login succeeds after LDAP server is restarted."""
|
||||
self.context.ldap_node = self.context.cluster.node(server)
|
||||
user = None
|
||||
|
||||
try:
|
||||
with Given(f"I add user to LDAP"):
|
||||
user = {"cn": "myuser", "userpassword": getuid()}
|
||||
user = add_user_to_ldap(**user)
|
||||
|
||||
with ldap_authenticated_users({"username": user["cn"], "server": server}):
|
||||
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||||
|
||||
with When("I restart LDAP server"):
|
||||
self.context.ldap_node.restart()
|
||||
|
||||
with Then("I try to login until it works", description=f"timeout {timeout} sec"):
|
||||
started = time.time()
|
||||
while True:
|
||||
r = self.context.node.query("SELECT 1",
|
||||
settings=[("user", user["cn"]), ("password", user["userpassword"])],
|
||||
no_checks=True)
|
||||
if r.exitcode == 0:
|
||||
break
|
||||
assert time.time() - started < timeout, error(r.output)
|
||||
finally:
|
||||
with Finally("I make sure LDAP user is deleted"):
|
||||
if user is not None:
|
||||
delete_user_from_ldap(user, exitcode=None)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Valid("1.0"),
|
||||
RQ_SRS_007_LDAP_Authentication_ClickHouseServerRestart("1.0")
|
||||
)
|
||||
def login_after_clickhouse_server_is_restarted(self, server, timeout=60):
|
||||
"""Check that login succeeds after ClickHouse server is restarted."""
|
||||
self.context.ldap_node = self.context.cluster.node(server)
|
||||
user = None
|
||||
|
||||
try:
|
||||
with Given(f"I add user to LDAP"):
|
||||
user = {"cn": "myuser", "userpassword": getuid()}
|
||||
user = add_user_to_ldap(**user)
|
||||
|
||||
with ldap_authenticated_users({"username": user["cn"], "server": server}):
|
||||
login_and_execute_query(username=user["cn"], password=user["userpassword"])
|
||||
|
||||
with When("I restart ClickHouse server"):
|
||||
self.context.node.restart()
|
||||
|
||||
with Then("I try to login until it works", description=f"timeout {timeout} sec"):
|
||||
started = time.time()
|
||||
while True:
|
||||
r = self.context.node.query("SELECT 1",
|
||||
settings=[("user", user["cn"]), ("password", user["userpassword"])],
|
||||
no_checks=True)
|
||||
if r.exitcode == 0:
|
||||
break
|
||||
assert time.time() - started < timeout, error(r.output)
|
||||
finally:
|
||||
with Finally("I make sure LDAP user is deleted"):
|
||||
if user is not None:
|
||||
delete_user_from_ldap(user, exitcode=None)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid("1.0"),
|
||||
RQ_SRS_007_LDAP_Authentication_Password_Empty("1.0")
|
||||
)
|
||||
def valid_username_with_valid_empty_password(self, server):
|
||||
"""Check that we can't login using valid username that has empty password."""
|
||||
user = {"cn": "empty_password", "userpassword": ""}
|
||||
exitcode = 4
|
||||
message = f"DB::Exception: {user['cn']}: Authentication failed: password is incorrect or there is no user with such name"
|
||||
|
||||
add_user_to_ldap_and_login(user=user, exitcode=exitcode, message=message, server=server)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid("1.0"),
|
||||
RQ_SRS_007_LDAP_Authentication_Password_Empty("1.0")
|
||||
)
|
||||
def valid_username_and_invalid_empty_password(self, server):
|
||||
"""Check that we can't login using valid username but invalid empty password."""
|
||||
username = "user_non_empty_password"
|
||||
user = {"cn": username, "userpassword": username}
|
||||
login = {"password": ""}
|
||||
|
||||
exitcode = 4
|
||||
message = f"DB::Exception: {username}: Authentication failed: password is incorrect or there is no user with such name"
|
||||
|
||||
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Valid("1.0")
|
||||
)
|
||||
def valid_username_and_password(self, server):
|
||||
"""Check that we can login using valid username and password."""
|
||||
username = "valid_username_and_password"
|
||||
user = {"cn": username, "userpassword": username}
|
||||
|
||||
with When(f"I add user {username} to LDAP and try to login"):
|
||||
add_user_to_ldap_and_login(user=user, server=server)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid("1.0")
|
||||
)
|
||||
def valid_username_and_password_invalid_server(self, server=None):
|
||||
"""Check that we can't login using valid username and valid
|
||||
password but for a different server."""
|
||||
self.context.ldap_node = self.context.cluster.node("openldap1")
|
||||
|
||||
user = {"username": "user2", "userpassword": "user2", "server": "openldap1"}
|
||||
|
||||
exitcode = 4
|
||||
message = f"DB::Exception: user2: Authentication failed: password is incorrect or there is no user with such name"
|
||||
|
||||
with ldap_authenticated_users(user, config_file=f"ldap_users_{getuid()}.xml", restart=True):
|
||||
login_and_execute_query(username="user2", password="user2", exitcode=exitcode, message=message)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Valid("1.0"),
|
||||
RQ_SRS_007_LDAP_Authentication_Username_Long("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_User_Name_Long("1.0")
|
||||
)
|
||||
def valid_long_username_and_short_password(self, server):
|
||||
"""Check that we can login using valid very long username and short password."""
|
||||
username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
||||
user = {"cn": username, "userpassword": "long_username"}
|
||||
|
||||
add_user_to_ldap_and_login(user=user, server=server)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid("1.0")
|
||||
)
|
||||
def invalid_long_username_and_valid_short_password(self, server):
|
||||
"""Check that we can't login using slightly invalid long username but valid password."""
|
||||
username = "long_username_12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
||||
user = {"cn": username, "userpassword": "long_username"}
|
||||
login = {"username": f"{username}?"}
|
||||
|
||||
exitcode = 4
|
||||
message=f"DB::Exception: {login['username']}: Authentication failed: password is incorrect or there is no user with such name"
|
||||
|
||||
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Valid("1.0"),
|
||||
RQ_SRS_007_LDAP_Authentication_Password_Long("1.0")
|
||||
)
|
||||
def valid_short_username_and_long_password(self, server):
|
||||
"""Check that we can login using valid short username with very long password."""
|
||||
username = "long_password"
|
||||
user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"}
|
||||
add_user_to_ldap_and_login(user=user, server=server)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid("1.0")
|
||||
)
|
||||
def valid_short_username_and_invalid_long_password(self, server):
|
||||
"""Check that we can't login using valid short username and invalid long password."""
|
||||
username = "long_password"
|
||||
user = {"cn": username, "userpassword": "long_password_12345678901234567890123456789012345678901234567890123456789012345678901234567890"}
|
||||
login = {"password": user["userpassword"] + "1"}
|
||||
|
||||
exitcode = 4
|
||||
message=f"DB::Exception: {username}: Authentication failed: password is incorrect or there is no user with such name"
|
||||
|
||||
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid("1.0")
|
||||
)
|
||||
def valid_username_and_invalid_password(self, server):
|
||||
"""Check that we can't login using valid username and invalid password."""
|
||||
username = "valid_username_and_invalid_password"
|
||||
user = {"cn": username, "userpassword": username}
|
||||
login = {"password": user["userpassword"] + "1"}
|
||||
|
||||
exitcode = 4
|
||||
message=f"DB::Exception: {username}: Authentication failed: password is incorrect or there is no user with such name"
|
||||
|
||||
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Invalid("1.0")
|
||||
)
|
||||
def invalid_username_and_valid_password(self, server):
|
||||
"""Check that we can't login using slightly invalid username but valid password."""
|
||||
username = "invalid_username_and_valid_password"
|
||||
user = {"cn": username, "userpassword": username}
|
||||
login = {"username": user["cn"] + "1"}
|
||||
|
||||
exitcode = 4
|
||||
message=f"DB::Exception: {login['username']}: Authentication failed: password is incorrect or there is no user with such name"
|
||||
|
||||
add_user_to_ldap_and_login(user=user, login=login, exitcode=exitcode, message=message, server=server)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Valid("1.0"),
|
||||
RQ_SRS_007_LDAP_Authentication_Username_UTF8("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_User_Name_UTF8("1.0")
|
||||
)
|
||||
def valid_utf8_username_and_ascii_password(self, server):
|
||||
"""Check that we can login using valid utf-8 username with ascii password."""
|
||||
username = "utf8_username_Gãńdåłf_Thê_Gręât"
|
||||
user = {"cn": username, "userpassword": "utf8_username"}
|
||||
|
||||
add_user_to_ldap_and_login(user=user, server=server)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Valid("1.0"),
|
||||
RQ_SRS_007_LDAP_Authentication_Password_UTF8("1.0")
|
||||
)
|
||||
def valid_ascii_username_and_utf8_password(self, server):
|
||||
"""Check that we can login using valid ascii username with utf-8 password."""
|
||||
username = "utf8_password"
|
||||
user = {"cn": username, "userpassword": "utf8_password_Gãńdåłf_Thê_Gręât"}
|
||||
|
||||
add_user_to_ldap_and_login(user=user, server=server)
|
||||
|
||||
@TestScenario
|
||||
def empty_username_and_empty_password(self, server=None):
|
||||
"""Check that we can login using empty username and empty password as
|
||||
it will use the default user and that has an empty password."""
|
||||
login_and_execute_query(username="", password="")
|
||||
|
||||
@TestFeature
|
||||
@Name("user authentications")
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Mechanism_NamePassword("1.0")
|
||||
)
|
||||
def feature(self, servers=None, node="clickhouse1"):
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
if servers is None:
|
||||
servers = globals()["servers"]
|
||||
|
||||
with ldap_servers(servers):
|
||||
for scenario in loads(current_module(), Scenario):
|
||||
scenario(server="openldap1")
|
378
tests/testflows/ldap/tests/common.py
Normal file
378
tests/testflows/ldap/tests/common.py
Normal file
@ -0,0 +1,378 @@
|
||||
import os
|
||||
import uuid
|
||||
import time
|
||||
import string
|
||||
import random
|
||||
import textwrap
|
||||
import xml.etree.ElementTree as xmltree
|
||||
|
||||
from collections import namedtuple
|
||||
from contextlib import contextmanager
|
||||
|
||||
import testflows.settings as settings
|
||||
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
def getuid():
|
||||
return str(uuid.uuid1()).replace('-', '_')
|
||||
|
||||
xml_with_utf8 = '<?xml version="1.0" encoding="utf-8"?>\n'
|
||||
|
||||
def xml_indent(elem, level=0, by=" "):
|
||||
i = "\n" + level * by
|
||||
if len(elem):
|
||||
if not elem.text or not elem.text.strip():
|
||||
elem.text = i + by
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
for elem in elem:
|
||||
xml_indent(elem, level + 1)
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
else:
|
||||
if level and (not elem.tail or not elem.tail.strip()):
|
||||
elem.tail = i
|
||||
|
||||
def xml_append(root, tag, text):
|
||||
element = xmltree.Element(tag)
|
||||
element.text = text
|
||||
root.append(element)
|
||||
return element
|
||||
|
||||
Config = namedtuple("Config", "content path name uid preprocessed_name")
|
||||
|
||||
ASCII_CHARS = string.ascii_lowercase + string.ascii_uppercase + string.digits
|
||||
|
||||
def randomword(length, chars=ASCII_CHARS):
|
||||
return ''.join(random.choice(chars) for i in range(length))
|
||||
|
||||
def add_config(config, timeout=20, restart=False):
|
||||
"""Add dynamic configuration file to ClickHouse.
|
||||
|
||||
:param node: node
|
||||
:param config: configuration file description
|
||||
:param timeout: timeout, default: 20 sec
|
||||
"""
|
||||
node = current().context.node
|
||||
try:
|
||||
with Given(f"{config.name}"):
|
||||
if settings.debug:
|
||||
with When("I output the content of the config"):
|
||||
debug(config.content)
|
||||
|
||||
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 When("I add the config", description=config.path):
|
||||
command = f"cat <<HEREDOC > {config.path}\n{config.content}\nHEREDOC"
|
||||
node.command(command, steps=False, exitcode=0)
|
||||
|
||||
with Then(f"{config.preprocessed_name} should be updated", description=f"timeout {timeout}"):
|
||||
started = time.time()
|
||||
command = f"cat /var/lib/clickhouse/preprocessed_configs/{config.preprocessed_name} | grep {config.uid}{' > /dev/null' if not settings.debug else ''}"
|
||||
while time.time() - started < timeout:
|
||||
exitcode = node.command(command, steps=False).exitcode
|
||||
if exitcode == 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
assert exitcode == 0, error()
|
||||
|
||||
if restart:
|
||||
bash.close()
|
||||
logsize = node.command("ls -s --block-size=1 /var/log/clickhouse-server/clickhouse-server.log").output.split(" ")[0].strip()
|
||||
with When("I restart ClickHouse to apply the config changes"):
|
||||
node.restart(safe=False)
|
||||
bash.prompt = bash.__class__.prompt
|
||||
bash.open()
|
||||
bash.send(f"tail -c +{logsize} -f /var/log/clickhouse-server/clickhouse-server.log")
|
||||
|
||||
with When("I wait for config to be loaded"):
|
||||
if restart:
|
||||
bash.expect(f"ConfigReloader: Loaded config '/etc/clickhouse-server/config.xml', performed update on configuration", timeout=timeout)
|
||||
else:
|
||||
bash.expect(f"ConfigReloader: Loaded config '/etc/clickhouse-server/{config.preprocessed_name}', performed update on configuration", timeout=timeout)
|
||||
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")
|
||||
|
||||
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"):
|
||||
started = time.time()
|
||||
command = f"cat /var/lib/clickhouse/preprocessed_configs/{config.preprocessed_name} | grep '{config.uid}'{' > /dev/null' if not settings.debug else ''}"
|
||||
while time.time() - started < timeout:
|
||||
exitcode = node.command(command, steps=False).exitcode
|
||||
if exitcode == 1:
|
||||
break
|
||||
time.sleep(1)
|
||||
assert exitcode == 1, error()
|
||||
|
||||
with When("I wait for config to be loaded"):
|
||||
started = time.time()
|
||||
bash.expect(f"ConfigReloader: Loaded config '/etc/clickhouse-server/{config.preprocessed_name}', performed update on configuration", timeout=timeout)
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
uid = getuid()
|
||||
path = os.path.join(config_d_dir, config_file)
|
||||
name = config_file
|
||||
|
||||
root = xmltree.fromstring("<yandex><ldap_servers></ldap_servers></yandex>")
|
||||
xml_servers = root.find("ldap_servers")
|
||||
xml_servers.append(xmltree.Comment(text=f"LDAP servers {uid}"))
|
||||
|
||||
for _name, server in servers.items():
|
||||
xml_server = xmltree.Element(_name)
|
||||
for key, value in server.items():
|
||||
xml_append(xml_server, key, value)
|
||||
xml_servers.append(xml_server)
|
||||
|
||||
xml_indent(root)
|
||||
content = xml_with_utf8 + str(xmltree.tostring(root, short_empty_elements=False, encoding="utf-8"), "utf-8")
|
||||
|
||||
return Config(content, path, name, uid, "config.xml")
|
||||
|
||||
@contextmanager
|
||||
def ldap_servers(servers, config_d_dir="/etc/clickhouse-server/config.d", config_file="ldap_servers.xml",
|
||||
timeout=20, restart=False):
|
||||
"""Add LDAP servers configuration.
|
||||
"""
|
||||
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"):
|
||||
"""Create LDAP users configuration file content.
|
||||
"""
|
||||
uid = getuid()
|
||||
path = os.path.join(config_d_dir, config_file)
|
||||
name = config_file
|
||||
|
||||
root = xmltree.fromstring("<yandex><users></users></yandex>")
|
||||
xml_users = root.find("users")
|
||||
xml_users.append(xmltree.Comment(text=f"LDAP users {uid}"))
|
||||
|
||||
for user in users:
|
||||
xml_user = xmltree.Element(user['username'])
|
||||
xml_user_server = xmltree.Element("ldap")
|
||||
xml_append(xml_user_server, "server", user["server"])
|
||||
xml_user.append(xml_user_server)
|
||||
xml_users.append(xml_user)
|
||||
|
||||
xml_indent(root)
|
||||
content = xml_with_utf8 + str(xmltree.tostring(root, short_empty_elements=False, encoding="utf-8"), "utf-8")
|
||||
|
||||
return Config(content, path, name, uid, "users.xml")
|
||||
|
||||
@contextmanager
|
||||
def ldap_authenticated_users(*users, config_d_dir="/etc/clickhouse-server/users.d",
|
||||
config_file=None, timeout=20, restart=True, config=None):
|
||||
"""Add LDAP authenticated user configuration.
|
||||
"""
|
||||
if config_file is None:
|
||||
config_file = f"ldap_users_{getuid()}.xml"
|
||||
if config is None:
|
||||
config = create_ldap_users_config_content(*users, config_d_dir=config_d_dir, config_file=config_file)
|
||||
return add_config(config, restart=restart)
|
||||
|
||||
def invalid_server_config(servers, message=None, tail=13, timeout=20):
|
||||
"""Check that ClickHouse errors when trying to load invalid LDAP servers configuration file.
|
||||
"""
|
||||
node = current().context.node
|
||||
if message is None:
|
||||
message = "Exception: Failed to merge config with '/etc/clickhouse-server/config.d/ldap_servers.xml'"
|
||||
|
||||
config = create_ldap_servers_config_content(servers)
|
||||
try:
|
||||
node.command("echo -e \"%s\" > /var/log/clickhouse-server/clickhouse-server.err.log" % ("-\\n" * tail))
|
||||
|
||||
with When("I add the config", description=config.path):
|
||||
command = f"cat <<HEREDOC > {config.path}\n{config.content}\nHEREDOC"
|
||||
node.command(command, steps=False, exitcode=0)
|
||||
|
||||
with Then("server shall fail to merge the new config"):
|
||||
started = time.time()
|
||||
command = f"tail -n {tail} /var/log/clickhouse-server/clickhouse-server.err.log | grep \"{message}\""
|
||||
while time.time() - started < timeout:
|
||||
exitcode = node.command(command, steps=False).exitcode
|
||||
if exitcode == 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
assert exitcode == 0, error()
|
||||
finally:
|
||||
with Finally(f"I remove {config.name}"):
|
||||
with By("removing the config file", description=config.path):
|
||||
node.command(f"rm -rf {config.path}", exitcode=0)
|
||||
|
||||
def invalid_user_config(servers, config, message=None, tail=13, timeout=20):
|
||||
"""Check that ClickHouse errors when trying to load invalid LDAP users configuration file.
|
||||
"""
|
||||
node = current().context.node
|
||||
if message is None:
|
||||
message = "Exception: Failed to merge config with '/etc/clickhouse-server/users.d/ldap_users.xml'"
|
||||
|
||||
with ldap_servers(servers):
|
||||
try:
|
||||
node.command("echo -e \"%s\" > /var/log/clickhouse-server/clickhouse-server.err.log" % ("\\n" * tail))
|
||||
with When("I add the config", description=config.path):
|
||||
command = f"cat <<HEREDOC > {config.path}\n{config.content}\nHEREDOC"
|
||||
node.command(command, steps=False, exitcode=0)
|
||||
|
||||
with Then("server shall fail to merge the new config"):
|
||||
started = time.time()
|
||||
command = f"tail -n {tail} /var/log/clickhouse-server/clickhouse-server.err.log | grep \"{message}\""
|
||||
while time.time() - started < timeout:
|
||||
exitcode = node.command(command, steps=False).exitcode
|
||||
if exitcode == 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
assert exitcode == 0, error()
|
||||
finally:
|
||||
with Finally(f"I remove {config.name}"):
|
||||
with By("removing the config file", description=config.path):
|
||||
node.command(f"rm -rf {config.path}", exitcode=0)
|
||||
|
||||
def add_user_to_ldap(cn, userpassword, givenname=None, homedirectory=None, sn=None, uid=None, uidnumber=None, node=None):
|
||||
"""Add user entry to LDAP."""
|
||||
if node is None:
|
||||
node = current().context.ldap_node
|
||||
if uid is None:
|
||||
uid = cn
|
||||
if givenname is None:
|
||||
givenname = "John"
|
||||
if homedirectory is None:
|
||||
homedirectory = "/home/users"
|
||||
if sn is None:
|
||||
sn = "User"
|
||||
if uidnumber is None:
|
||||
uidnumber = 2000
|
||||
|
||||
user = {
|
||||
"dn": f"cn={cn},ou=users,dc=company,dc=com",
|
||||
"cn": cn,
|
||||
"gidnumber": 501,
|
||||
"givenname": givenname,
|
||||
"homedirectory": homedirectory,
|
||||
"objectclass": ["inetOrgPerson", "posixAccount", "top"],
|
||||
"sn": sn,
|
||||
"uid": uid,
|
||||
"uidnumber": uidnumber,
|
||||
"userpassword": userpassword,
|
||||
"_server": node.name
|
||||
}
|
||||
|
||||
lines = []
|
||||
for key, value in user.items():
|
||||
if key.startswith("_"):
|
||||
continue
|
||||
elif key == "objectclass":
|
||||
for cls in value:
|
||||
lines.append(f"objectclass: {cls}")
|
||||
else:
|
||||
lines.append(f"{key}: {value}")
|
||||
|
||||
ldif = "\n".join(lines)
|
||||
|
||||
r = node.command(
|
||||
f"echo -e \"{ldif}\" | ldapadd -x -H ldap://localhost -D \"cn=admin,dc=company,dc=com\" -w admin")
|
||||
assert r.exitcode == 0, error()
|
||||
|
||||
return user
|
||||
|
||||
def delete_user_from_ldap(user, node=None, exitcode=0):
|
||||
"""Delete user entry from LDAP."""
|
||||
if node is None:
|
||||
node = current().context.ldap_node
|
||||
r = node.command(
|
||||
f"ldapdelete -x -H ldap://localhost -D \"cn=admin,dc=company,dc=com\" -w admin \"{user['dn']}\"")
|
||||
if exitcode is not None:
|
||||
assert r.exitcode == exitcode, error()
|
||||
|
||||
def change_user_password_in_ldap(user, new_password, node=None, exitcode=0):
|
||||
"""Change user password in LDAP."""
|
||||
if node is None:
|
||||
node = current().context.ldap_node
|
||||
|
||||
ldif = (f"dn: {user['dn']}\n"
|
||||
"changetype: modify\n"
|
||||
"replace: userpassword\n"
|
||||
f"userpassword: {new_password}")
|
||||
|
||||
r = node.command(
|
||||
f"echo -e \"{ldif}\" | ldapmodify -x -H ldap://localhost -D \"cn=admin,dc=company,dc=com\" -w admin")
|
||||
|
||||
if exitcode is not None:
|
||||
assert r.exitcode == exitcode, error()
|
||||
|
||||
def change_user_cn_in_ldap(user, new_cn, node=None, exitcode=0):
|
||||
"""Change user password in LDAP."""
|
||||
if node is None:
|
||||
node = current().context.ldap_node
|
||||
|
||||
new_user = dict(user)
|
||||
new_user['dn'] = f"cn={new_cn},ou=users,dc=company,dc=com"
|
||||
new_user['cn'] = new_cn
|
||||
|
||||
ldif = (
|
||||
f"dn: {user['dn']}\n"
|
||||
"changetype: modrdn\n"
|
||||
f"newrdn: cn = {new_user['cn']}\n"
|
||||
f"deleteoldrdn: 1\n"
|
||||
)
|
||||
|
||||
r = node.command(
|
||||
f"echo -e \"{ldif}\" | ldapmodify -x -H ldap://localhost -D \"cn=admin,dc=company,dc=com\" -w admin")
|
||||
|
||||
if exitcode is not None:
|
||||
assert r.exitcode == exitcode, error()
|
||||
|
||||
return new_user
|
||||
|
||||
@contextmanager
|
||||
def ldap_user(cn, userpassword, givenname=None, homedirectory=None, sn=None, uid=None, uidnumber=None, node=None):
|
||||
"""Add new user to the LDAP server."""
|
||||
try:
|
||||
user = None
|
||||
with Given(f"I add user {cn} to LDAP"):
|
||||
user = add_user_to_ldap(cn, userpassword, givenname, homedirectory, sn, uid, uidnumber, node=node)
|
||||
yield user
|
||||
finally:
|
||||
with Finally(f"I delete user {cn} from LDAP"):
|
||||
if user is not None:
|
||||
delete_user_from_ldap(user, node=node)
|
||||
|
||||
@contextmanager
|
||||
def ldap_users(*users, node=None):
|
||||
"""Add multiple new users to the LDAP server."""
|
||||
try:
|
||||
_users = []
|
||||
with Given("I add users to LDAP"):
|
||||
for user in users:
|
||||
with By(f"adding user {user['cn']}"):
|
||||
_users.append(add_user_to_ldap(**user, node=node))
|
||||
yield _users
|
||||
finally:
|
||||
with Finally(f"I delete users from LDAP"):
|
||||
for _user in _users:
|
||||
delete_user_from_ldap(_user, node=node)
|
||||
|
||||
def login(servers, *users, config=None):
|
||||
"""Configure LDAP server and LDAP authenticated users and
|
||||
try to login and execute a query"""
|
||||
with ldap_servers(servers):
|
||||
with ldap_authenticated_users(*users, restart=True, config=config):
|
||||
for user in users:
|
||||
if user.get("login", False):
|
||||
with When(f"I login as {user['username']} and execute query"):
|
||||
current().context.node.query("SELECT 1",
|
||||
settings=[("user", user["username"]), ("password", user["password"])],
|
||||
exitcode=user.get("exitcode", None),
|
||||
message=user.get("message", None))
|
||||
|
285
tests/testflows/ldap/tests/connections.py
Normal file
285
tests/testflows/ldap/tests/connections.py
Normal file
@ -0,0 +1,285 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from ldap.tests.common import login
|
||||
from ldap.requirements import *
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Protocol_PlainText("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS_Options_No("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Port_Default("1.0")
|
||||
)
|
||||
def plain_text(self):
|
||||
"""Check that we can perform LDAP user authentication using `plain text` connection protocol.
|
||||
"""
|
||||
servers = {
|
||||
"openldap1": {
|
||||
"host": "openldap1",
|
||||
"enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}
|
||||
}
|
||||
users = [
|
||||
{"server": "openldap1", "username": "user1", "password": "user1", "login": True}
|
||||
]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Protocol_PlainText("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Port("1.0")
|
||||
)
|
||||
def plain_text_with_custom_port(self):
|
||||
"""Check that we can perform LDAP user authentication using `plain text` connection protocol
|
||||
with the server that uses custom port.
|
||||
"""
|
||||
servers = {
|
||||
"openldap3": {
|
||||
"host": "openldap3",
|
||||
"port": "3089",
|
||||
"enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}
|
||||
}
|
||||
users = [
|
||||
{"server": "openldap3", "username": "user3", "password": "user3", "login": True}
|
||||
]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Protocol_TLS("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Port("1.0")
|
||||
)
|
||||
def tls_with_custom_port(self):
|
||||
"""Check that we can perform LDAP user authentication using `TLS` connection protocol
|
||||
with the server that uses custom port.
|
||||
"""
|
||||
servers = {
|
||||
"openldap4": {
|
||||
"host": "openldap4",
|
||||
"port": "6036",
|
||||
"tls_require_cert": "never",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}
|
||||
}
|
||||
users = [
|
||||
{"server": "openldap4", "username": "user4", "password": "user4", "login": True}
|
||||
]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Protocol_StartTLS("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Port("1.0")
|
||||
)
|
||||
def starttls_with_custom_port(self):
|
||||
"""Check that we can perform LDAP user authentication using `StartTLS` connection protocol
|
||||
with the server that uses custom port.
|
||||
"""
|
||||
servers = {
|
||||
"openldap4": {
|
||||
"host": "openldap4",
|
||||
"port": "3089",
|
||||
"enable_tls": "starttls",
|
||||
"tls_require_cert": "never",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}
|
||||
}
|
||||
users = [
|
||||
{"server": "openldap4", "username": "user4", "password": "user4", "login": True}
|
||||
]
|
||||
login(servers, *users)
|
||||
|
||||
def tls_connection(enable_tls, tls_require_cert):
|
||||
"""Try to login using LDAP user authentication over a TLS connection."""
|
||||
servers = {
|
||||
"openldap2": {
|
||||
"host": "openldap2",
|
||||
"enable_tls": enable_tls,
|
||||
"tls_require_cert": tls_require_cert,
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}
|
||||
}
|
||||
users = [
|
||||
{"server": "openldap2", "username": "user2", "password": "user2", "login": True}
|
||||
]
|
||||
|
||||
requirements = []
|
||||
|
||||
if tls_require_cert == "never":
|
||||
requirements = [RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert_Options_Never("1.0")]
|
||||
elif tls_require_cert == "allow":
|
||||
requirements = [RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert_Options_Allow("1.0")]
|
||||
elif tls_require_cert == "try":
|
||||
requirements = [RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert_Options_Try("1.0")]
|
||||
elif tls_require_cert == "demand":
|
||||
requirements = [RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert_Options_Demand("1.0")]
|
||||
|
||||
with Example(name=f"tls_require_cert='{tls_require_cert}'", requirements=requirements):
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Examples("enable_tls tls_require_cert", [
|
||||
("yes", "never"),
|
||||
("yes", "allow"),
|
||||
("yes", "try"),
|
||||
("yes", "demand")
|
||||
])
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Protocol_TLS("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS_Options_Yes("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Port_Default("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSMinimumProtocolVersion_Default("1.0")
|
||||
)
|
||||
def tls(self):
|
||||
"""Check that we can perform LDAP user authentication using `TLS` connection protocol.
|
||||
"""
|
||||
for example in self.examples:
|
||||
tls_connection(*example)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS_Options_Default("1.0")
|
||||
)
|
||||
def tls_enable_tls_default_yes(self):
|
||||
"""Check that the default value for the `enable_tls` is set to `yes`."""
|
||||
servers = {
|
||||
"openldap2": {
|
||||
"host": "openldap2",
|
||||
"tls_require_cert": "never",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}
|
||||
}
|
||||
users = [
|
||||
{"server": "openldap2", "username": "user2", "password": "user2", "login": True}
|
||||
]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSRequireCert_Options_Default("1.0")
|
||||
)
|
||||
def tls_require_cert_default_demand(self):
|
||||
"""Check that the default value for the `tls_require_cert` is set to `demand`."""
|
||||
servers = {
|
||||
"openldap2": {
|
||||
"host": "openldap2",
|
||||
"enable_tls": "yes",
|
||||
"port": "636",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}
|
||||
}
|
||||
users = [
|
||||
{"server": "openldap2", "username": "user2", "password": "user2", "login": True}
|
||||
]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Examples("enable_tls tls_require_cert", [
|
||||
("starttls", "never"),
|
||||
("starttls", "allow"),
|
||||
("starttls", "try"),
|
||||
("starttls", "demand")
|
||||
])
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_Protocol_StartTLS("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_EnableTLS_Options_StartTLS("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Port_Default("1.0")
|
||||
)
|
||||
def starttls(self):
|
||||
"""Check that we can perform LDAP user authentication using legacy `StartTLS` connection protocol.
|
||||
"""
|
||||
for example in self.examples:
|
||||
tls_connection(*example)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSCipherSuite("1.0")
|
||||
)
|
||||
def tls_cipher_suite(self):
|
||||
"""Check that `tls_cipher_suite` parameter can be used specify allowed cipher suites."""
|
||||
servers = {
|
||||
"openldap4": {
|
||||
"host": "openldap4",
|
||||
"port": "6036",
|
||||
"tls_require_cert": "never",
|
||||
"tls_cipher_suite": "SECURE256:+SECURE128:-VERS-TLS-ALL:+VERS-TLS1.2:-RSA:-DHE-DSS:-CAMELLIA-128-CBC:-CAMELLIA-256-CBC",
|
||||
"tls_minimum_protocol_version": "tls1.2",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}
|
||||
}
|
||||
users = [
|
||||
{"server": "openldap4", "username": "user4", "password": "user4", "login": True}
|
||||
]
|
||||
login(servers, *users)
|
||||
|
||||
@TestOutline(Scenario)
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSMinimumProtocolVersion("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_TLSMinimumProtocolVersion_Values("1.0")
|
||||
)
|
||||
@Examples("version exitcode message", [
|
||||
("ssl2", None, None),
|
||||
("ssl3", None, None),
|
||||
("tls1.0", None, None),
|
||||
("tls1.1", None, None),
|
||||
("tls1.2", None, None),
|
||||
("tls1.3", 36, "DB::Exception: LDAP server 'openldap4' is not configured")
|
||||
])
|
||||
def tls_minimum_protocol_version(self, version, exitcode, message):
|
||||
"""Check that `tls_minimum_protocol_version` parameter can be used specify
|
||||
to specify the minimum protocol version of SSL/TLS."""
|
||||
|
||||
servers = {
|
||||
"openldap4": {
|
||||
"host": "openldap4",
|
||||
"port": "6036",
|
||||
"tls_require_cert": "never",
|
||||
"tls_minimum_protocol_version": version,
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}
|
||||
}
|
||||
|
||||
users = [{
|
||||
"server": "openldap4", "username": "user4", "password": "user4",
|
||||
"login": True, "exitcode": int(exitcode) if exitcode is not None else None, "message": message
|
||||
}]
|
||||
|
||||
# Note: this code was an attempt to produce a negative case but did not work
|
||||
# ldap_node = self.context.cluster.node("openldap4")
|
||||
# ldif = (
|
||||
# "dn: cn=config\n"
|
||||
# "changetype: modify\n"
|
||||
# "replace: olcTLSProtocolMin\n"
|
||||
# "olcTLSProtocolMin: 3.5"
|
||||
# )
|
||||
#
|
||||
# r = ldap_node.command(
|
||||
# f"echo -e \"{ldif}\" | ldapmodify -x -H ldaps://localhost:6036 -D \"cn=admin,cn=config\" -w config")
|
||||
#
|
||||
# ldap_node.restart()
|
||||
|
||||
login(servers, *users)
|
||||
|
||||
@TestFeature
|
||||
@Name("connection protocols")
|
||||
def feature(self, node="clickhouse1"):
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
for scenario in loads(current_module(), Scenario):
|
||||
scenario()
|
38
tests/testflows/ldap/tests/multiple_servers.py
Normal file
38
tests/testflows/ldap/tests/multiple_servers.py
Normal file
@ -0,0 +1,38 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from ldap.tests.common import login
|
||||
from ldap.requirements import RQ_SRS_007_LDAP_Authentication_MultipleServers
|
||||
|
||||
@TestScenario
|
||||
@Name("multiple servers")
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Authentication_MultipleServers("1.0")
|
||||
)
|
||||
def scenario(self, node="clickhouse1"):
|
||||
"""Check that multiple LDAP servers can be used to
|
||||
authenticate users.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
servers = {
|
||||
"openldap1": {
|
||||
"host": "openldap1",
|
||||
"port": "389",
|
||||
"enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
},
|
||||
"openldap2": {
|
||||
"host": "openldap2",
|
||||
"port": "636",
|
||||
"enable_tls": "yes",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||||
"tls_require_cert": "never",
|
||||
},
|
||||
}
|
||||
users = [
|
||||
{"server": "openldap1", "username": "user1", "password": "user1", "login": True},
|
||||
{"server": "openldap2", "username": "user2", "password": "user2", "login": True}
|
||||
]
|
||||
login(servers, *users)
|
42
tests/testflows/ldap/tests/sanity.py
Normal file
42
tests/testflows/ldap/tests/sanity.py
Normal file
@ -0,0 +1,42 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from ldap.tests.common import add_user_to_ldap, delete_user_from_ldap
|
||||
|
||||
@TestScenario
|
||||
@Name("sanity")
|
||||
def scenario(self, server="openldap1"):
|
||||
"""Check that LDAP server is up and running by
|
||||
executing ldapsearch, ldapadd, and ldapdelete commands.
|
||||
"""
|
||||
self.context.ldap_node = self.context.cluster.node(server)
|
||||
|
||||
with When("I search LDAP database"):
|
||||
r = self.context.ldap_node.command(
|
||||
"ldapsearch -x -H ldap://localhost -b \"dc=company,dc=com\" -D \"cn=admin,dc=company,dc=com\" -w admin")
|
||||
assert r.exitcode == 0, error()
|
||||
|
||||
with Then("I should find an entry for user1"):
|
||||
assert "dn: cn=user1,ou=users,dc=company,dc=com" in r.output, error()
|
||||
|
||||
with When("I add new user to LDAP"):
|
||||
user = add_user_to_ldap(cn="myuser", userpassword="myuser")
|
||||
|
||||
with And("I search LDAP database again"):
|
||||
r = self.context.ldap_node.command(
|
||||
"ldapsearch -x -H ldap://localhost -b \"dc=company,dc=com\" -D \"cn=admin,dc=company,dc=com\" -w admin")
|
||||
assert r.exitcode == 0, error()
|
||||
|
||||
with Then("I should find an entry for the new user"):
|
||||
assert f"dn: {user['dn']}" in r.output, error()
|
||||
|
||||
with When("I delete user from LDAP"):
|
||||
delete_user_from_ldap(user)
|
||||
|
||||
with And("I search LDAP database again"):
|
||||
r = self.context.ldap_node.command(
|
||||
"ldapsearch -x -H ldap://localhost -b \"dc=company,dc=com\" -D \"cn=admin,dc=company,dc=com\" -w admin")
|
||||
assert r.exitcode == 0, error()
|
||||
|
||||
with Then("I should not find an entry for the deleted user"):
|
||||
assert f"dn: {user['dn']}" not in r.output, error()
|
263
tests/testflows/ldap/tests/server_config.py
Normal file
263
tests/testflows/ldap/tests/server_config.py
Normal file
@ -0,0 +1,263 @@
|
||||
from testflows.core import *
|
||||
|
||||
from ldap.tests.common import *
|
||||
from ldap.requirements import *
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Server_Configuration_Invalid("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Name("1.0")
|
||||
)
|
||||
def empty_server_name(self, timeout=20):
|
||||
"""Check that empty string as a server name is not allowed.
|
||||
"""
|
||||
servers = {"": {"host": "foo", "port": "389", "enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}}
|
||||
invalid_server_config(servers, timeout=timeout)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Server_Configuration_Invalid("1.0"),
|
||||
RQ_SRS_007_LDAP_UnreachableServer("1.0")
|
||||
)
|
||||
def invalid_host(self):
|
||||
"""Check that server returns an error when LDAP server
|
||||
host name is invalid.
|
||||
"""
|
||||
servers = {"foo": {"host": "foo", "port": "389", "enable_tls": "no"}}
|
||||
users = [{
|
||||
"server": "foo", "username": "user1", "password": "user1", "login": True,
|
||||
"exitcode": 20, "message": "DB::Exception: Can't contact LDAP server"
|
||||
}]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Server_Configuration_Invalid("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Host("1.0")
|
||||
)
|
||||
def empty_host(self):
|
||||
"""Check that server returns an error when LDAP server
|
||||
host value is empty.
|
||||
"""
|
||||
servers = {"foo": {"host": "", "port": "389", "enable_tls": "no"}}
|
||||
users = [{
|
||||
"server": "foo", "username": "user1", "password": "user1", "login": True,
|
||||
"exitcode": 36, "message": "DB::Exception: LDAP server 'foo' is not configured."
|
||||
}]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Server_Configuration_Invalid("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Host("1.0")
|
||||
)
|
||||
def missing_host(self):
|
||||
"""Check that server returns an error when LDAP server
|
||||
host is missing.
|
||||
"""
|
||||
servers = {"foo": {"port": "389", "enable_tls": "no"}}
|
||||
users = [{
|
||||
"server": "foo", "username": "user1", "password": "user1", "login": True,
|
||||
"exitcode": 36, "message": "DB::Exception: LDAP server 'foo' is not configured."
|
||||
}]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Server_Configuration_Invalid("1.0")
|
||||
)
|
||||
def invalid_port(self):
|
||||
"""Check that server returns an error when LDAP server
|
||||
port is not valid.
|
||||
"""
|
||||
servers = {"openldap1": {"host": "openldap1", "port": "3890", "enable_tls": "no"}}
|
||||
users = [{
|
||||
"server": "openldap1", "username": "user1", "password": "user1", "login": True,
|
||||
"exitcode": 20, "message": "DB::Exception: Can't contact LDAP server."
|
||||
}]
|
||||
login(servers, *users)
|
||||
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Server_Configuration_Invalid("1.0")
|
||||
)
|
||||
def invalid_auth_dn_prefix(self):
|
||||
"""Check that server returns an error when LDAP server
|
||||
port is not valid.
|
||||
"""
|
||||
servers = {"openldap1": {"host": "openldap1", "port": "389", "enable_tls": "no",
|
||||
"auth_dn_prefix": "foo=", "auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}}
|
||||
users = [{
|
||||
"server": "openldap1", "username": "user1", "password": "user1", "login": True,
|
||||
"exitcode": 20, "message": "DB::Exception: Invalid DN syntax: invalid DN"
|
||||
}]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Server_Configuration_Invalid("1.0")
|
||||
)
|
||||
def invalid_auth_dn_suffix(self):
|
||||
"""Check that server returns an error when LDAP server
|
||||
port is not valid.
|
||||
"""
|
||||
servers = {"openldap1": {"host": "openldap1", "port": "389", "enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",foo=users,dc=company,dc=com"
|
||||
}}
|
||||
users = [{
|
||||
"server": "openldap1", "username": "user1", "password": "user1", "login": True,
|
||||
"exitcode": 20, "message": "DB::Exception: Invalid DN syntax: invalid DN"
|
||||
}]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Server_Configuration_Invalid("1.0")
|
||||
)
|
||||
def invalid_enable_tls_value(self):
|
||||
"""Check that server returns an error when enable_tls
|
||||
option has invalid value.
|
||||
"""
|
||||
servers = {"openldap1": {"host": "openldap1", "port": "389", "enable_tls": "foo",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}}
|
||||
users = [{
|
||||
"server": "openldap1", "username": "user1", "password": "user1", "login": True,
|
||||
"exitcode": 36, "message": "DB::Exception: LDAP server 'openldap1' is not configured"
|
||||
}]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Server_Configuration_Invalid("1.0")
|
||||
)
|
||||
def invalid_tls_require_cert_value(self):
|
||||
"""Check that server returns an error when tls_require_cert
|
||||
option has invalid value.
|
||||
"""
|
||||
servers = {"openldap2": {
|
||||
"host": "openldap2", "port": "636", "enable_tls": "yes",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||||
"tls_require_cert": "foo",
|
||||
"ca_cert_dir": "/container/service/slapd/assets/certs/",
|
||||
"ca_cert_file": "/container/service/slapd/assets/certs/ca.crt"
|
||||
}}
|
||||
users = [{
|
||||
"server": "openldap2", "username": "user2", "password": "user2", "login": True,
|
||||
"exitcode": 36, "message": "DB::Exception: LDAP server 'openldap2' is not configured"
|
||||
}]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Server_Configuration_Invalid("1.0")
|
||||
)
|
||||
def empty_ca_cert_dir(self):
|
||||
"""Check that server returns an error when ca_cert_dir is empty.
|
||||
"""
|
||||
servers = {"openldap2": {"host": "openldap2", "port": "636", "enable_tls": "yes",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||||
"tls_require_cert": "demand",
|
||||
"ca_cert_dir": "",
|
||||
"ca_cert_file": "/container/service/slapd/assets/certs/ca.crt"
|
||||
}}
|
||||
users = [{
|
||||
"server": "openldap2", "username": "user2", "password": "user2", "login": True,
|
||||
"exitcode": 20,
|
||||
"message": "DB::Exception: Can't contact LDAP server: error:14000086:SSL routines::certificate verify failed (self signed certificate in certificate chain"
|
||||
}]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Server_Configuration_Invalid("1.0")
|
||||
)
|
||||
def empty_ca_cert_file(self):
|
||||
"""Check that server returns an error when ca_cert_file is empty.
|
||||
"""
|
||||
servers = {"openldap2": {"host": "openldap2", "port": "636", "enable_tls": "yes",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||||
"tls_require_cert": "demand",
|
||||
"ca_cert_dir": "/container/service/slapd/assets/certs/",
|
||||
"ca_cert_file": ""
|
||||
}}
|
||||
users = [{
|
||||
"server": "openldap2", "username": "user2", "password": "user2", "login": True,
|
||||
"exitcode": 20,
|
||||
"message": "Received from localhost:9000. DB::Exception: Can't contact LDAP server: error:14000086:SSL routines::certificate verify failed (self signed certificate in certificate chain)"
|
||||
}]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Configuration_Server_AuthDN_Value("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_AuthDN_Prefix("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_Server_AuthDN_Suffix("1.0")
|
||||
)
|
||||
def auth_dn_value(self):
|
||||
"""Check that server configuration can properly define the `dn` value of the user."""
|
||||
servers = {
|
||||
"openldap1": {
|
||||
"host": "openldap1", "port": "389", "enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}}
|
||||
user = {"server": "openldap1", "username": "user1", "password": "user1", "login": True}
|
||||
|
||||
login(servers, user)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Configuration_Server_Syntax("1.0")
|
||||
)
|
||||
def syntax(self):
|
||||
"""Check that server configuration with valid syntax can be loaded.
|
||||
```xml
|
||||
<yandex>
|
||||
<ldap_server>
|
||||
<host>localhost</host>
|
||||
<port>636</port>
|
||||
<auth_dn_prefix>cn=</auth_dn_prefix>
|
||||
<auth_dn_suffix>, ou=users, dc=example, dc=com</auth_dn_suffix>
|
||||
<enable_tls>yes</enable_tls>
|
||||
<tls_minimum_protocol_version>tls1.2</tls_minimum_protocol_version>
|
||||
<tls_require_cert>demand</tls_require_cert>
|
||||
<tls_cert_file>/path/to/tls_cert_file</tls_cert_file>
|
||||
<tls_key_file>/path/to/tls_key_file</tls_key_file>
|
||||
<tls_ca_cert_file>/path/to/tls_ca_cert_file</tls_ca_cert_file>
|
||||
<tls_ca_cert_dir>/path/to/tls_ca_cert_dir</tls_ca_cert_dir>
|
||||
<tls_cipher_suite>ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384</tls_cipher_suite>
|
||||
</ldap_server>
|
||||
</yandex>
|
||||
```
|
||||
"""
|
||||
servers = {
|
||||
"openldap2": {
|
||||
"host": "openldap2",
|
||||
"port": "389",
|
||||
"auth_dn_prefix": "cn=",
|
||||
"auth_dn_suffix": ",ou=users,dc=company,dc=com",
|
||||
"enable_tls": "yes",
|
||||
"tls_minimum_protocol_version": "tls1.2" ,
|
||||
"tls_require_cert": "demand",
|
||||
"tls_cert_file": "/container/service/slapd/assets/certs/ldap.crt",
|
||||
"tls_key_file": "/container/service/slapd/assets/certs/ldap.key",
|
||||
"tls_ca_cert_file": "/container/service/slapd/assets/certs/ca.crt",
|
||||
"tls_ca_cert_dir": "/container/service/slapd/assets/certs/",
|
||||
"tls_cipher_suite": "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384"
|
||||
}
|
||||
}
|
||||
with ldap_servers(servers):
|
||||
pass
|
||||
|
||||
@TestFeature
|
||||
@Name("server config")
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check that LDAP server configuration.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
for scenario in loads(current_module(), Scenario):
|
||||
scenario()
|
162
tests/testflows/ldap/tests/user_config.py
Normal file
162
tests/testflows/ldap/tests/user_config.py
Normal file
@ -0,0 +1,162 @@
|
||||
import xml.etree.ElementTree as xmltree
|
||||
|
||||
from testflows.core import *
|
||||
|
||||
from ldap.tests.common import *
|
||||
from ldap.requirements import *
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_User_Configuration_Invalid("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_User_Name_Empty("1.0")
|
||||
)
|
||||
def empty_user_name(self, timeout=20):
|
||||
"""Check that empty string as a user name is not allowed.
|
||||
"""
|
||||
servers = {"openldap1": {
|
||||
"host": "openldap1", "port": "389", "enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}}
|
||||
users = [{"server": "openldap1", "username": "", "password": "user1", "login": True}]
|
||||
config = create_ldap_users_config_content(*users)
|
||||
invalid_user_config(servers, config, timeout=timeout)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_User_Configuration_Invalid("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_User_LDAP_InvalidServerName_Empty("1.0")
|
||||
)
|
||||
def empty_server_name(self, timeout=20):
|
||||
"""Check that if server name is an empty string then login is not allowed.
|
||||
"""
|
||||
servers = {"openldap1": {
|
||||
"host": "openldap1", "port": "389", "enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}}
|
||||
users = [{"server": "", "username": "user1", "password": "user1", "login": True,
|
||||
"errorcode": 4,
|
||||
"message": "DB::Exception: user1: Authentication failed: password is incorrect or there is no user with such name"
|
||||
}]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_User_Configuration_Invalid("1.0"),
|
||||
RQ_SRS_007_LDAP_Configuration_User_LDAP_InvalidServerName_NotDefined("1.0")
|
||||
)
|
||||
def empty_server_not_defined(self, timeout=20):
|
||||
"""Check that if server is not defined then login is not allowed.
|
||||
"""
|
||||
servers = {"openldap1": {
|
||||
"host": "openldap1", "port": "389", "enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}}
|
||||
users = [{"server": "foo", "username": "user1", "password": "user1", "login": True,
|
||||
"errorcode": 36,
|
||||
"message": "DB::Exception: LDAP server 'foo' is not configured"
|
||||
}]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Configuration_User_Syntax("1.0")
|
||||
)
|
||||
def valid_user_config(self):
|
||||
"""Check syntax of valid user configuration of LDAP authenticated user."""
|
||||
servers = {"openldap1": {
|
||||
"host": "openldap1", "port": "389", "enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
}}
|
||||
users = [{"server": "openldap1", "username": "user1", "password": "user1", "login": True}]
|
||||
login(servers, *users)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Configuration_User_OnlyOneServer("1.0")
|
||||
)
|
||||
def multiple_servers(self, timeout=20):
|
||||
"""Check that user configuration allows to specify only one LDAP server for a given user
|
||||
and if multiple servers are specified then the first one is used."""
|
||||
servers = {
|
||||
"openldap1": {
|
||||
"host": "openldap1", "port": "389", "enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
},
|
||||
"openldap2": {
|
||||
"host": "openldap2", "enable_tls": "yes", "tls_require_cert": "never",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
},
|
||||
}
|
||||
user = {"server": "openldap1", "username": "user1", "password": "user1", "login": True}
|
||||
|
||||
with When("I first create regular user configuration file"):
|
||||
config = create_ldap_users_config_content(user)
|
||||
|
||||
with And("I modify it to add another server"):
|
||||
root = xmltree.fromstring(config.content)
|
||||
xml_users = root.find("users")
|
||||
xml_users.append(xmltree.Comment(text=f"LDAP users {config.uid}"))
|
||||
xml_user_ldap = xml_users.find(user["username"]).find("ldap")
|
||||
xml_append(xml_user_ldap, "server", "openldap2")
|
||||
xml_indent(root)
|
||||
content = xml_with_utf8 + str(xmltree.tostring(root, short_empty_elements=False, encoding="utf-8"), "utf-8")
|
||||
|
||||
new_config = Config(content, config.path, config.name, config.uid, config.preprocessed_name)
|
||||
|
||||
with Then("I login and expect it to work as the first server shall be used"):
|
||||
login(servers, user, config=new_config)
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_007_LDAP_Configuration_User_BothPasswordAndLDAP("1.0")
|
||||
)
|
||||
def ldap_and_password(self):
|
||||
"""Check that user can't be authenticated if both `ldap` and `password`
|
||||
is specified for the same user. We expect an error message to be present in the log
|
||||
and login attempt to fail.
|
||||
"""
|
||||
node = self.context.node
|
||||
servers = {
|
||||
"openldap1": {
|
||||
"host": "openldap1", "port": "389", "enable_tls": "no",
|
||||
"auth_dn_prefix": "cn=", "auth_dn_suffix": ",ou=users,dc=company,dc=com"
|
||||
},
|
||||
}
|
||||
user = {
|
||||
"server": "openldap1", "username": "user1", "password": "user1", "login": True,
|
||||
"errorcode": 4,
|
||||
"message": "DB::Exception: user1: Authentication failed: password is incorrect or there is no user with such name"
|
||||
}
|
||||
|
||||
with When("I first create regular user configuration file"):
|
||||
config = create_ldap_users_config_content(user)
|
||||
|
||||
with And("I modify it to add explicit password"):
|
||||
root = xmltree.fromstring(config.content)
|
||||
xml_users = root.find("users")
|
||||
xml_users.append(xmltree.Comment(text=f"LDAP users {config.uid}"))
|
||||
xml_user = xml_users.find(user["username"])
|
||||
xml_append(xml_user, "password", "hellothere")
|
||||
xml_indent(root)
|
||||
content = xml_with_utf8 + str(xmltree.tostring(root, short_empty_elements=False, encoding="utf-8"), "utf-8")
|
||||
|
||||
new_config = Config(content, config.path, config.name, config.uid, config.preprocessed_name)
|
||||
|
||||
error_message = "DB::Exception: More than one field of 'password'"
|
||||
|
||||
with Then("I expect an error when I try to load the configuration file", description=error_message):
|
||||
invalid_user_config(servers, new_config, message=error_message, tail=16)
|
||||
|
||||
with And("I expect the authentication to fail when I try to login"):
|
||||
login(servers, user, config=new_config)
|
||||
|
||||
@TestFeature
|
||||
@Name("user config")
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check that server returns an error and prohibits
|
||||
user login if LDAP users configuration is not valid.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
for scenario in loads(current_module(), Scenario):
|
||||
scenario()
|
@ -12,8 +12,10 @@ from helpers.argparser import argparser
|
||||
def regression(self, local, clickhouse_binary_path):
|
||||
"""ClickHouse regression.
|
||||
"""
|
||||
Feature(test=load("example.regression", "regression"))(
|
||||
local=local, clickhouse_binary_path=clickhouse_binary_path)
|
||||
args = {"local": local, "clickhouse_binary_path": clickhouse_binary_path}
|
||||
|
||||
Feature(test=load("example.regression", "regression"))(**args)
|
||||
Feature(test=load("ldap.regression", "regression"))(**args)
|
||||
|
||||
if main():
|
||||
regression()
|
||||
|
@ -21,7 +21,7 @@ BUILD_TARGETS=clickhouse
|
||||
BUILD_TYPE=Debug
|
||||
ENABLE_EMBEDDED_COMPILER=0
|
||||
|
||||
CMAKE_FLAGS="-D CMAKE_C_FLAGS_ADD=-g0 -D CMAKE_CXX_FLAGS_ADD=-g0 -D ENABLE_JEMALLOC=0 -D ENABLE_CAPNP=0 -D ENABLE_RDKAFKA=0 -D ENABLE_UNWIND=0 -D ENABLE_ICU=0 -D ENABLE_POCO_MONGODB=0 -D ENABLE_POCO_REDIS=0 -D ENABLE_POCO_NETSSL=0 -D ENABLE_ODBC=0 -D ENABLE_MYSQL=0 -D ENABLE_SSL=0 -D ENABLE_POCO_NETSSL=0 -D ENABLE_CASSANDRA=0"
|
||||
CMAKE_FLAGS="-D CMAKE_C_FLAGS_ADD=-g0 -D CMAKE_CXX_FLAGS_ADD=-g0 -D ENABLE_JEMALLOC=0 -D ENABLE_CAPNP=0 -D ENABLE_RDKAFKA=0 -D ENABLE_UNWIND=0 -D ENABLE_ICU=0 -D ENABLE_POCO_MONGODB=0 -D ENABLE_POCO_REDIS=0 -D ENABLE_POCO_NETSSL=0 -D ENABLE_ODBC=0 -D ENABLE_MYSQL=0 -D ENABLE_SSL=0 -D ENABLE_POCO_NETSSL=0 -D ENABLE_CASSANDRA=0 -D ENABLE_LDAP=0"
|
||||
|
||||
[[ $(uname) == "FreeBSD" ]] && COMPILER_PACKAGE_VERSION=devel && export COMPILER_PATH=/usr/local/bin
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user