Skip to content

Commit 5d6b510

Browse files
committed
Implement LDAPAccessStorage and integrate it into AccessControlManager
Rename ExternalAuthenticators::setConfig to setConfiguration Revisit LDAP servers config section comments Add user_directories config section with comments (only for ldap) Fix bug in MemoryAccessStorage::insertImpl
1 parent e7a4bc8 commit 5d6b510

10 files changed

+280
-12
lines changed

programs/server/Server.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,14 @@ int Server::main(const std::vector<std::string> & /*args*/)
595595
if (!access_control_local_path.empty())
596596
global_context->getAccessControlManager().setLocalDirectory(access_control_local_path);
597597

598+
/// Set LDAP user directory config.
599+
const bool has_ldap_directory_config = config().has("user_directories.ldap");
600+
if (has_ldap_directory_config) {
601+
auto ldap_directory_config = config().createView("user_directories.ldap");
602+
if (ldap_directory_config)
603+
global_context->getAccessControlManager().setLDAPConfig(*ldap_directory_config);
604+
}
605+
598606
/// Limit on total number of concurrently executed queries.
599607
global_context->getProcessList().setMaxSize(config().getInt("max_concurrent_queries", 0));
600608

programs/server/config.xml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,10 @@
215215
<!-- Path to folder where users and roles created by SQL commands are stored. -->
216216
<access_control_path>/var/lib/clickhouse/access/</access_control_path>
217217

218-
<!-- External user directories (LDAP). -->
218+
<!-- LDAP server definitions. -->
219219
<ldap_servers>
220220
<!-- List LDAP servers with their connection parameters here to later use them as authenticators for dedicated users,
221-
who have 'ldap' authentication mechanism specified instead of 'password'.
221+
who have 'ldap' authentication mechanism specified instead of 'password'.
222222
Parameters:
223223
host - LDAP server hostname or IP, this parameter is mandatory and cannot be empty.
224224
port - LDAP server port, default is 636 if enable_tls is set to true, 389 otherwise.
@@ -237,7 +237,7 @@
237237
tls_key_file - path to certificate key file.
238238
tls_ca_cert_file - path to CA certificate file.
239239
tls_ca_cert_dir - path to the directory containing CA certificates.
240-
tls_cipher_suite - allowed cipher suite.
240+
tls_cipher_suite - allowed cipher suite (in OpenSSL notation).
241241
Example:
242242
<my_ldap_server>
243243
<host>localhost</host>
@@ -256,6 +256,23 @@
256256
-->
257257
</ldap_servers>
258258

259+
<!-- User directories (LDAP, etc.) -->
260+
<user_directories>
261+
<!-- To add an LDAP server as a user directory, define a single 'ldap' section with the following parameters:
262+
server - one of LDAP server names defined in 'ldap_servers' config section above.
263+
This parameter is mandatory and cannot be empty.
264+
user_template - name of a user that will be used as a template for all users retrieved from the LDAP server.
265+
Each LDAP user will inherit all parameters from 'user_template' user, except for the authentication method,
266+
which will be set to 'ldap' and the 'server' defined above will be used as a LDAP server for authentication.
267+
This parameter is optional, if not specified, 'default' user will be used as a template.
268+
Example:
269+
<ldap>
270+
<server>my_ldap_server</server>
271+
<user_template>my_user</user_template>
272+
</ldap>
273+
-->
274+
</user_directories>
275+
259276
<!-- Path to configuration file with users, access rights, profiles of settings, quotas. -->
260277
<users_config>users.xml</users_config>
261278

src/Access/AccessControlManager.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <Access/MemoryAccessStorage.h>
44
#include <Access/UsersConfigAccessStorage.h>
55
#include <Access/DiskAccessStorage.h>
6+
#include <Access/LDAPAccessStorage.h>
67
#include <Access/ContextAccess.h>
78
#include <Access/RoleCache.h>
89
#include <Access/RowPolicyCache.h>
@@ -28,11 +29,14 @@ namespace
2829
#if 0 /// Memory access storage is disabled.
2930
list.emplace_back(std::make_unique<MemoryAccessStorage>());
3031
#endif
32+
33+
list.emplace_back(std::make_unique<LDAPAccessStorage>());
3134
return list;
3235
}
3336

3437
constexpr size_t DISK_ACCESS_STORAGE_INDEX = 0;
3538
constexpr size_t USERS_CONFIG_ACCESS_STORAGE_INDEX = 1;
39+
constexpr size_t LDAP_ACCESS_STORAGE_INDEX = 2;
3640
}
3741

3842

@@ -81,16 +85,23 @@ void AccessControlManager::setLocalDirectory(const String & directory_path)
8185
}
8286

8387

84-
void AccessControlManager::setExternalAuthenticatorsConfig(const Poco::Util::AbstractConfiguration & config)
88+
void AccessControlManager::setUsersConfig(const Poco::Util::AbstractConfiguration & users_config)
8589
{
86-
external_authenticators->setConfig(config, getLogger());
90+
auto & users_config_access_storage = dynamic_cast<UsersConfigAccessStorage &>(getStorageByIndex(USERS_CONFIG_ACCESS_STORAGE_INDEX));
91+
users_config_access_storage.setConfiguration(users_config);
8792
}
8893

8994

90-
void AccessControlManager::setUsersConfig(const Poco::Util::AbstractConfiguration & users_config)
95+
void AccessControlManager::setLDAPConfig(const Poco::Util::AbstractConfiguration & users_config)
9196
{
92-
auto & users_config_access_storage = dynamic_cast<UsersConfigAccessStorage &>(getStorageByIndex(USERS_CONFIG_ACCESS_STORAGE_INDEX));
93-
users_config_access_storage.setConfiguration(users_config);
97+
auto & ldap_access_storage = dynamic_cast<LDAPAccessStorage &>(getStorageByIndex(LDAP_ACCESS_STORAGE_INDEX));
98+
ldap_access_storage.setConfiguration(users_config, this);
99+
}
100+
101+
102+
void AccessControlManager::setExternalAuthenticatorsConfig(const Poco::Util::AbstractConfiguration & config)
103+
{
104+
external_authenticators->setConfiguration(config, getLogger());
94105
}
95106

96107

src/Access/AccessControlManager.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@ class AccessControlManager : public MultipleAccessStorage
4949
~AccessControlManager();
5050

5151
void setLocalDirectory(const String & directory);
52-
void setExternalAuthenticatorsConfig(const Poco::Util::AbstractConfiguration & config);
5352
void setUsersConfig(const Poco::Util::AbstractConfiguration & users_config);
53+
void setLDAPConfig(const Poco::Util::AbstractConfiguration & users_config);
54+
55+
void setExternalAuthenticatorsConfig(const Poco::Util::AbstractConfiguration & config);
5456
void setDefaultProfileName(const String & default_profile_name);
5557

5658
std::shared_ptr<const ContextAccess> getContextAccess(

src/Access/ExternalAuthenticators.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ void ExternalAuthenticators::reset()
156156
ldap_server_params.clear();
157157
}
158158

159-
void ExternalAuthenticators::setConfig(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log)
159+
void ExternalAuthenticators::setConfiguration(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log)
160160
{
161161
std::scoped_lock lock(mutex);
162162
reset();

src/Access/ExternalAuthenticators.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ExternalAuthenticators
2626
{
2727
public:
2828
void reset();
29-
void setConfig(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log);
29+
void setConfiguration(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log);
3030

3131
void setLDAPServerParams(const String & server, const LDAPServerParams & params);
3232
LDAPServerParams getLDAPServerParams(const String & server) const;

src/Access/LDAPAccessStorage.cpp

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#include <Access/LDAPAccessStorage.h>
2+
#include <Access/User.h>
3+
#include <common/logger_useful.h>
4+
#include <ext/scope_guard.h>
5+
#include <Poco/Util/AbstractConfiguration.h>
6+
7+
8+
namespace DB
9+
{
10+
namespace ErrorCodes
11+
{
12+
extern const int BAD_ARGUMENTS;
13+
extern const int UNKNOWN_ADDRESS_PATTERN_TYPE;
14+
extern const int NOT_IMPLEMENTED;
15+
}
16+
17+
18+
LDAPAccessStorage::LDAPAccessStorage() : IAccessStorage("ldap")
19+
{
20+
}
21+
22+
23+
void LDAPAccessStorage::setConfiguration(const Poco::Util::AbstractConfiguration & config, IAccessStorage * top_enclosing_storage_)
24+
{
25+
std::scoped_lock lock(mutex);
26+
27+
const bool has_server = config.has("server");
28+
const bool has_user_template = config.has("user_template");
29+
30+
if (!has_server)
31+
throw Exception("Missing 'server' field for LDAP user directory.", ErrorCodes::BAD_ARGUMENTS);
32+
33+
const auto ldap_server_ = config.getString("server");
34+
const auto user_template_ = (has_user_template ? config.getString("user_template") : "default");
35+
36+
if (ldap_server_.empty())
37+
throw Exception("Empty 'server' field for LDAP user directory.", ErrorCodes::BAD_ARGUMENTS);
38+
39+
if (user_template_.empty())
40+
throw Exception("Empty 'user_template' field for LDAP user directory.", ErrorCodes::BAD_ARGUMENTS);
41+
42+
ldap_server = ldap_server_;
43+
user_template = user_template_;
44+
top_enclosing_storage = top_enclosing_storage_;
45+
}
46+
47+
48+
bool LDAPAccessStorage::isConfiguredNoLock() const
49+
{
50+
return !ldap_server.empty() && !user_template.empty() && top_enclosing_storage;
51+
}
52+
53+
54+
std::optional<UUID> LDAPAccessStorage::findImpl(EntityType type, const String & name) const
55+
{
56+
if (type == EntityType::USER)
57+
{
58+
std::scoped_lock lock(mutex);
59+
60+
// Detect and avoid loops/duplicate creations.
61+
if (helper_lookup_in_progress)
62+
return {};
63+
64+
helper_lookup_in_progress = true;
65+
SCOPE_EXIT({ helper_lookup_in_progress = false; });
66+
67+
// Return the id immediately if we already have it.
68+
const auto id = memory_storage.find(type, name);
69+
if (id.has_value())
70+
return id;
71+
72+
if (!isConfiguredNoLock())
73+
{
74+
LOG_WARNING(getLogger(), "Access storage instance is not configured.");
75+
return {};
76+
}
77+
78+
// Stop if entity exists anywhere else, to avoid duplicates.
79+
if (top_enclosing_storage->find(type, name))
80+
return {};
81+
82+
// Entity doesn't exist. We are going to create it. Here we retrieve the template first.
83+
const auto user_tmp = top_enclosing_storage->read<User>(user_template);
84+
if (!user_tmp)
85+
{
86+
LOG_WARNING(getLogger(), "Unable to retrieve user template '{}': user does not exist in access storage '{}'.", user_template, top_enclosing_storage->getStorageName());
87+
return {};
88+
}
89+
90+
// Build the new entity based on the existing template.
91+
const auto user = std::make_shared<User>(*user_tmp);
92+
user->setName(name);
93+
user->authentication = Authentication(Authentication::Type::LDAP_SERVER);
94+
user->authentication.setServerName(ldap_server);
95+
96+
return memory_storage.insert(user);
97+
}
98+
99+
return memory_storage.find(type, name);
100+
}
101+
102+
103+
std::vector<UUID> LDAPAccessStorage::findAllImpl(EntityType type) const
104+
{
105+
return memory_storage.findAll(type);
106+
}
107+
108+
109+
bool LDAPAccessStorage::existsImpl(const UUID & id) const
110+
{
111+
return memory_storage.exists(id);
112+
}
113+
114+
115+
AccessEntityPtr LDAPAccessStorage::readImpl(const UUID & id) const
116+
{
117+
return memory_storage.read(id);
118+
}
119+
120+
121+
String LDAPAccessStorage::readNameImpl(const UUID & id) const
122+
{
123+
return memory_storage.readName(id);
124+
}
125+
126+
127+
bool LDAPAccessStorage::canInsertImpl(const AccessEntityPtr &) const
128+
{
129+
return false;
130+
}
131+
132+
133+
UUID LDAPAccessStorage::insertImpl(const AccessEntityPtr & entity, bool)
134+
{
135+
throwReadonlyCannotInsert(entity->getType(), entity->getName());
136+
}
137+
138+
139+
void LDAPAccessStorage::removeImpl(const UUID & id)
140+
{
141+
auto entity = read(id);
142+
throwReadonlyCannotRemove(entity->getType(), entity->getName());
143+
}
144+
145+
146+
void LDAPAccessStorage::updateImpl(const UUID & id, const UpdateFunc &)
147+
{
148+
auto entity = read(id);
149+
throwReadonlyCannotUpdate(entity->getType(), entity->getName());
150+
}
151+
152+
153+
ext::scope_guard LDAPAccessStorage::subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const
154+
{
155+
return memory_storage.subscribeForChanges(id, handler);
156+
}
157+
158+
159+
ext::scope_guard LDAPAccessStorage::subscribeForChangesImpl(EntityType type, const OnChangedHandler & handler) const
160+
{
161+
return memory_storage.subscribeForChanges(type, handler);
162+
}
163+
164+
165+
bool LDAPAccessStorage::hasSubscriptionImpl(const UUID & id) const
166+
{
167+
return memory_storage.hasSubscription(id);
168+
}
169+
170+
171+
bool LDAPAccessStorage::hasSubscriptionImpl(EntityType type) const
172+
{
173+
return memory_storage.hasSubscription(type);
174+
}
175+
}

src/Access/LDAPAccessStorage.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#pragma once
2+
3+
#include <Access/MemoryAccessStorage.h>
4+
#include <Core/Types.h>
5+
#include <mutex>
6+
7+
8+
namespace Poco
9+
{
10+
namespace Util
11+
{
12+
class AbstractConfiguration;
13+
}
14+
}
15+
16+
17+
namespace DB
18+
{
19+
/// Implementation of IAccessStorage which allows attaching users from a remote LDAP server.
20+
/// Currently, any user name will be treated as a name of an existing remote user,
21+
/// a user info entity will be created, with LDAP_SERVER authentication type.
22+
class LDAPAccessStorage : public IAccessStorage
23+
{
24+
public:
25+
LDAPAccessStorage();
26+
27+
void setConfiguration(const Poco::Util::AbstractConfiguration & config, IAccessStorage * top_enclosing_storage_);
28+
29+
private: // IAccessStorage implementations.
30+
std::optional<UUID> findImpl(EntityType type, const String & name) const override;
31+
std::vector<UUID> findAllImpl(EntityType type) const override;
32+
bool existsImpl(const UUID & id) const override;
33+
AccessEntityPtr readImpl(const UUID & id) const override;
34+
String readNameImpl(const UUID & id) const override;
35+
bool canInsertImpl(const AccessEntityPtr &) const override;
36+
UUID insertImpl(const AccessEntityPtr & entity, bool replace_if_exists) override;
37+
void removeImpl(const UUID & id) override;
38+
void updateImpl(const UUID & id, const UpdateFunc & update_func) override;
39+
ext::scope_guard subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const override;
40+
ext::scope_guard subscribeForChangesImpl(EntityType type, const OnChangedHandler & handler) const override;
41+
bool hasSubscriptionImpl(const UUID & id) const override;
42+
bool hasSubscriptionImpl(EntityType type) const override;
43+
44+
private:
45+
bool isConfiguredNoLock() const;
46+
47+
mutable std::recursive_mutex mutex;
48+
String ldap_server;
49+
String user_template;
50+
IAccessStorage * top_enclosing_storage = nullptr;
51+
mutable bool helper_lookup_in_progress = false;
52+
mutable MemoryAccessStorage memory_storage;
53+
};
54+
}

src/Access/MemoryAccessStorage.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ UUID MemoryAccessStorage::insertImpl(const AccessEntityPtr & new_entity, bool re
6969

7070
UUID id = generateRandomID();
7171
std::lock_guard lock{mutex};
72-
insertNoLock(generateRandomID(), new_entity, replace_if_exists, notifications);
72+
insertNoLock(id, new_entity, replace_if_exists, notifications);
7373
return id;
7474
}
7575

src/Access/ya.make

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ SRCS(
2121
GrantedRoles.cpp
2222
IAccessEntity.cpp
2323
IAccessStorage.cpp
24+
LDAPAccessStorage.cpp
2425
LDAPClient.cpp
2526
MemoryAccessStorage.cpp
2627
MultipleAccessStorage.cpp

0 commit comments

Comments
 (0)