2020-07-23 17:55:24 +00:00
|
|
|
#include <Access/LDAPAccessStorage.h>
|
2020-08-20 07:39:27 +00:00
|
|
|
#include <Access/AccessControlManager.h>
|
2020-07-23 17:55:24 +00:00
|
|
|
#include <Access/User.h>
|
2020-08-20 07:39:27 +00:00
|
|
|
#include <Access/Role.h>
|
2020-08-15 12:17:07 +00:00
|
|
|
#include <Common/Exception.h>
|
2020-07-23 17:55:24 +00:00
|
|
|
#include <common/logger_useful.h>
|
|
|
|
#include <ext/scope_guard.h>
|
|
|
|
#include <Poco/Util/AbstractConfiguration.h>
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int BAD_ARGUMENTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-18 10:54:02 +00:00
|
|
|
LDAPAccessStorage::LDAPAccessStorage(const String & storage_name_)
|
|
|
|
: IAccessStorage(storage_name_)
|
2020-07-23 17:55:24 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-20 07:39:27 +00:00
|
|
|
void LDAPAccessStorage::setConfiguration(AccessControlManager * access_control_manager_, const Poco::Util::AbstractConfiguration & config, const String & prefix)
|
2020-07-23 17:55:24 +00:00
|
|
|
{
|
2020-08-20 07:39:27 +00:00
|
|
|
// TODO: switch to passing config as a ConfigurationView and remove this extra prefix once a version of Poco with proper implementation is available.
|
2020-08-18 10:54:02 +00:00
|
|
|
const String prefix_str = (prefix.empty() ? "" : prefix + ".");
|
|
|
|
|
2020-07-23 17:55:24 +00:00
|
|
|
std::scoped_lock lock(mutex);
|
|
|
|
|
2020-08-18 10:54:02 +00:00
|
|
|
const bool has_server = config.has(prefix_str + "server");
|
2020-08-20 07:39:27 +00:00
|
|
|
const bool has_roles = config.has(prefix_str + "roles");
|
2020-07-23 17:55:24 +00:00
|
|
|
|
|
|
|
if (!has_server)
|
|
|
|
throw Exception("Missing 'server' field for LDAP user directory.", ErrorCodes::BAD_ARGUMENTS);
|
|
|
|
|
2020-08-18 10:54:02 +00:00
|
|
|
const auto ldap_server_cfg = config.getString(prefix_str + "server");
|
2020-07-24 09:52:03 +00:00
|
|
|
if (ldap_server_cfg.empty())
|
2020-07-23 17:55:24 +00:00
|
|
|
throw Exception("Empty 'server' field for LDAP user directory.", ErrorCodes::BAD_ARGUMENTS);
|
|
|
|
|
2020-08-20 07:39:27 +00:00
|
|
|
std::set<String> roles_cfg;
|
|
|
|
if (has_roles)
|
|
|
|
{
|
|
|
|
Poco::Util::AbstractConfiguration::Keys role_names;
|
|
|
|
config.keys(prefix_str + "roles", role_names);
|
2020-08-15 12:17:07 +00:00
|
|
|
|
2020-08-20 07:39:27 +00:00
|
|
|
// Currently, we only extract names of roles from the section names and assign them directly and unconditionally.
|
|
|
|
roles_cfg.insert(role_names.begin(), role_names.end());
|
|
|
|
}
|
2020-07-23 17:55:24 +00:00
|
|
|
|
2020-08-20 07:39:27 +00:00
|
|
|
access_control_manager = access_control_manager_;
|
2020-08-28 08:06:06 +00:00
|
|
|
ldap_server = ldap_server_cfg;
|
|
|
|
default_role_names.swap(roles_cfg);
|
|
|
|
roles_of_interest.clear();
|
2020-08-26 20:34:33 +00:00
|
|
|
role_change_subscription = access_control_manager->subscribeForChanges<Role>(
|
|
|
|
[this] (const UUID & id, const AccessEntityPtr & entity)
|
|
|
|
{
|
|
|
|
return this->processRoleChange(id, entity);
|
|
|
|
}
|
|
|
|
);
|
2020-07-23 17:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool LDAPAccessStorage::isConfiguredNoLock() const
|
|
|
|
{
|
2020-08-20 07:39:27 +00:00
|
|
|
return !ldap_server.empty() &&/* !roles.empty() &&*/ access_control_manager;
|
2020-07-23 17:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-26 20:34:33 +00:00
|
|
|
void LDAPAccessStorage::processRoleChange(const UUID & id, const AccessEntityPtr & entity)
|
|
|
|
{
|
|
|
|
auto role_ptr = typeid_cast<std::shared_ptr<const Role>>(entity);
|
|
|
|
if (role_ptr)
|
|
|
|
{
|
2020-08-28 08:06:06 +00:00
|
|
|
if (default_role_names.find(role_ptr->getName()) != default_role_names.end())
|
2020-08-26 20:34:33 +00:00
|
|
|
{
|
|
|
|
auto update_func = [&id](const AccessEntityPtr & cached_entity) -> AccessEntityPtr
|
|
|
|
{
|
|
|
|
auto user_ptr = typeid_cast<std::shared_ptr<const User>>(cached_entity);
|
2020-08-27 08:36:31 +00:00
|
|
|
if (user_ptr && user_ptr->granted_roles.roles.find(id) == user_ptr->granted_roles.roles.end())
|
2020-08-26 20:34:33 +00:00
|
|
|
{
|
|
|
|
auto clone = user_ptr->clone();
|
|
|
|
auto user_clone_ptr = typeid_cast<std::shared_ptr<User>>(clone);
|
|
|
|
user_clone_ptr->granted_roles.grant(id);
|
|
|
|
return user_clone_ptr;
|
|
|
|
}
|
|
|
|
return cached_entity;
|
|
|
|
};
|
|
|
|
|
|
|
|
memory_storage.update(memory_storage.findAll<User>(), update_func);
|
|
|
|
roles_of_interest.insert(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (roles_of_interest.find(id) != roles_of_interest.end())
|
|
|
|
{
|
|
|
|
auto update_func = [&id](const AccessEntityPtr & cached_entity) -> AccessEntityPtr
|
|
|
|
{
|
|
|
|
auto user_ptr = typeid_cast<std::shared_ptr<const User>>(cached_entity);
|
2020-08-27 08:36:31 +00:00
|
|
|
if (user_ptr && user_ptr->granted_roles.roles.find(id) != user_ptr->granted_roles.roles.end())
|
2020-08-26 20:34:33 +00:00
|
|
|
{
|
|
|
|
auto clone = user_ptr->clone();
|
|
|
|
auto user_clone_ptr = typeid_cast<std::shared_ptr<User>>(clone);
|
|
|
|
user_clone_ptr->granted_roles.revoke(id);
|
|
|
|
return user_clone_ptr;
|
|
|
|
}
|
|
|
|
return cached_entity;
|
|
|
|
};
|
|
|
|
|
|
|
|
memory_storage.update(memory_storage.findAll<User>(), update_func);
|
|
|
|
roles_of_interest.erase(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-18 10:54:02 +00:00
|
|
|
const char * LDAPAccessStorage::getStorageType() const
|
|
|
|
{
|
|
|
|
return STORAGE_TYPE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool LDAPAccessStorage::isStorageReadOnly() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-07-23 17:55:24 +00:00
|
|
|
std::optional<UUID> LDAPAccessStorage::findImpl(EntityType type, const String & name) const
|
2020-08-26 18:09:26 +00:00
|
|
|
{
|
|
|
|
return memory_storage.find(type, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::optional<UUID> LDAPAccessStorage::findOrGenerateImpl(EntityType type, const String & name) const
|
2020-07-23 17:55:24 +00:00
|
|
|
{
|
|
|
|
if (type == EntityType::USER)
|
|
|
|
{
|
|
|
|
std::scoped_lock lock(mutex);
|
|
|
|
|
|
|
|
// Return the id immediately if we already have it.
|
|
|
|
const auto id = memory_storage.find(type, name);
|
|
|
|
if (id.has_value())
|
|
|
|
return id;
|
|
|
|
|
|
|
|
if (!isConfiguredNoLock())
|
|
|
|
return {};
|
|
|
|
|
2020-08-20 07:39:27 +00:00
|
|
|
// Stop if entity exists anywhere else, to avoid generating duplicates.
|
2020-08-20 08:46:42 +00:00
|
|
|
const auto * this_base = dynamic_cast<const IAccessStorage *>(this);
|
2020-08-20 07:39:27 +00:00
|
|
|
const auto storages = access_control_manager->getStoragesPtr();
|
|
|
|
for (const auto & storage : *storages)
|
2020-07-23 17:55:24 +00:00
|
|
|
{
|
2020-08-20 07:39:27 +00:00
|
|
|
if (storage.get() != this_base && storage->find(type, name))
|
|
|
|
return {};
|
2020-07-23 17:55:24 +00:00
|
|
|
}
|
|
|
|
|
2020-08-20 07:39:27 +00:00
|
|
|
// Entity doesn't exist. We are going to create one.
|
|
|
|
const auto user = std::make_shared<User>();
|
2020-07-23 17:55:24 +00:00
|
|
|
user->setName(name);
|
|
|
|
user->authentication = Authentication(Authentication::Type::LDAP_SERVER);
|
|
|
|
user->authentication.setServerName(ldap_server);
|
|
|
|
|
2020-08-28 08:06:06 +00:00
|
|
|
for (const auto& role_name : default_role_names)
|
2020-08-20 08:46:42 +00:00
|
|
|
{
|
2020-08-20 07:39:27 +00:00
|
|
|
std::optional<UUID> role_id;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
role_id = access_control_manager->find<Role>(role_name);
|
|
|
|
if (!role_id)
|
|
|
|
throw Exception("Retrieved role info is empty", IAccessEntity::TypeInfo::get(IAccessEntity::Type::ROLE).not_found_error_code);
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
tryLogCurrentException(getLogger(), "Unable to retrieve role '" + role_name + "' info from access storage '" + access_control_manager->getStorageName() + "'");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-08-26 20:34:33 +00:00
|
|
|
roles_of_interest.insert(role_id.value());
|
2020-08-20 07:39:27 +00:00
|
|
|
user->granted_roles.grant(role_id.value());
|
|
|
|
}
|
|
|
|
|
2020-07-23 17:55:24 +00:00
|
|
|
return memory_storage.insert(user);
|
|
|
|
}
|
|
|
|
|
|
|
|
return memory_storage.find(type, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<UUID> LDAPAccessStorage::findAllImpl(EntityType type) const
|
|
|
|
{
|
|
|
|
return memory_storage.findAll(type);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool LDAPAccessStorage::existsImpl(const UUID & id) const
|
|
|
|
{
|
|
|
|
return memory_storage.exists(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AccessEntityPtr LDAPAccessStorage::readImpl(const UUID & id) const
|
|
|
|
{
|
|
|
|
return memory_storage.read(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
String LDAPAccessStorage::readNameImpl(const UUID & id) const
|
|
|
|
{
|
|
|
|
return memory_storage.readName(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool LDAPAccessStorage::canInsertImpl(const AccessEntityPtr &) const
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UUID LDAPAccessStorage::insertImpl(const AccessEntityPtr & entity, bool)
|
|
|
|
{
|
|
|
|
throwReadonlyCannotInsert(entity->getType(), entity->getName());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LDAPAccessStorage::removeImpl(const UUID & id)
|
|
|
|
{
|
|
|
|
auto entity = read(id);
|
|
|
|
throwReadonlyCannotRemove(entity->getType(), entity->getName());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LDAPAccessStorage::updateImpl(const UUID & id, const UpdateFunc &)
|
|
|
|
{
|
|
|
|
auto entity = read(id);
|
|
|
|
throwReadonlyCannotUpdate(entity->getType(), entity->getName());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ext::scope_guard LDAPAccessStorage::subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const
|
|
|
|
{
|
|
|
|
return memory_storage.subscribeForChanges(id, handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ext::scope_guard LDAPAccessStorage::subscribeForChangesImpl(EntityType type, const OnChangedHandler & handler) const
|
|
|
|
{
|
|
|
|
return memory_storage.subscribeForChanges(type, handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool LDAPAccessStorage::hasSubscriptionImpl(const UUID & id) const
|
|
|
|
{
|
|
|
|
return memory_storage.hasSubscription(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool LDAPAccessStorage::hasSubscriptionImpl(EntityType type) const
|
|
|
|
{
|
|
|
|
return memory_storage.hasSubscription(type);
|
|
|
|
}
|
|
|
|
}
|