mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-20 13:15:42 +00:00
250 lines
7.7 KiB
C++
250 lines
7.7 KiB
C++
#include <Access/SettingsProfilesCache.h>
|
|
#include <Access/AccessControl.h>
|
|
#include <Access/SettingsProfile.h>
|
|
#include <Access/SettingsProfilesInfo.h>
|
|
#include <Common/quoteString.h>
|
|
|
|
|
|
namespace DB
|
|
{
|
|
namespace ErrorCodes
|
|
{
|
|
extern const int THERE_IS_NO_PROFILE;
|
|
}
|
|
|
|
SettingsProfilesCache::SettingsProfilesCache(const AccessControl & access_control_)
|
|
: access_control(access_control_) {}
|
|
|
|
SettingsProfilesCache::~SettingsProfilesCache() = default;
|
|
|
|
|
|
void SettingsProfilesCache::ensureAllProfilesRead()
|
|
{
|
|
/// `mutex` is already locked.
|
|
if (all_profiles_read)
|
|
return;
|
|
all_profiles_read = true;
|
|
|
|
subscription = access_control.subscribeForChanges<SettingsProfile>(
|
|
[&](const UUID & id, const AccessEntityPtr & entity)
|
|
{
|
|
if (entity)
|
|
profileAddedOrChanged(id, typeid_cast<SettingsProfilePtr>(entity));
|
|
else
|
|
profileRemoved(id);
|
|
});
|
|
|
|
for (const UUID & id : access_control.findAll<SettingsProfile>())
|
|
{
|
|
auto profile = access_control.tryRead<SettingsProfile>(id);
|
|
if (profile)
|
|
{
|
|
all_profiles.emplace(id, profile);
|
|
profiles_by_name[profile->getName()] = id;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SettingsProfilesCache::profileAddedOrChanged(const UUID & profile_id, const SettingsProfilePtr & new_profile)
|
|
{
|
|
std::lock_guard lock{mutex};
|
|
auto it = all_profiles.find(profile_id);
|
|
if (it == all_profiles.end())
|
|
{
|
|
all_profiles.emplace(profile_id, new_profile);
|
|
profiles_by_name[new_profile->getName()] = profile_id;
|
|
}
|
|
else
|
|
{
|
|
auto old_profile = it->second;
|
|
it->second = new_profile;
|
|
if (old_profile->getName() != new_profile->getName())
|
|
profiles_by_name.erase(old_profile->getName());
|
|
profiles_by_name[new_profile->getName()] = profile_id;
|
|
}
|
|
profile_infos_cache.clear();
|
|
mergeSettingsAndConstraints();
|
|
}
|
|
|
|
|
|
void SettingsProfilesCache::profileRemoved(const UUID & profile_id)
|
|
{
|
|
std::lock_guard lock{mutex};
|
|
auto it = all_profiles.find(profile_id);
|
|
if (it == all_profiles.end())
|
|
return;
|
|
profiles_by_name.erase(it->second->getName());
|
|
all_profiles.erase(it);
|
|
profile_infos_cache.clear();
|
|
mergeSettingsAndConstraints();
|
|
}
|
|
|
|
|
|
void SettingsProfilesCache::setDefaultProfileName(const String & default_profile_name)
|
|
{
|
|
std::lock_guard lock{mutex};
|
|
ensureAllProfilesRead();
|
|
|
|
if (default_profile_name.empty())
|
|
{
|
|
default_profile_id = {};
|
|
return;
|
|
}
|
|
|
|
auto it = profiles_by_name.find(default_profile_name);
|
|
if (it == profiles_by_name.end())
|
|
throw Exception(ErrorCodes::THERE_IS_NO_PROFILE, "Settings profile {} not found", backQuote(default_profile_name));
|
|
|
|
default_profile_id = it->second;
|
|
}
|
|
|
|
|
|
void SettingsProfilesCache::mergeSettingsAndConstraints()
|
|
{
|
|
/// `mutex` is already locked.
|
|
for (auto i = enabled_settings.begin(), e = enabled_settings.end(); i != e;)
|
|
{
|
|
auto enabled = i->second.lock();
|
|
if (!enabled)
|
|
i = enabled_settings.erase(i);
|
|
else
|
|
{
|
|
mergeSettingsAndConstraintsFor(*enabled);
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SettingsProfilesCache::mergeSettingsAndConstraintsFor(EnabledSettings & enabled) const
|
|
{
|
|
SettingsProfileElements merged_settings;
|
|
if (default_profile_id)
|
|
{
|
|
SettingsProfileElement new_element;
|
|
new_element.parent_profile = *default_profile_id;
|
|
merged_settings.emplace_back(new_element);
|
|
}
|
|
|
|
for (const auto & [profile_id, profile] : all_profiles)
|
|
if (profile->to_roles.match(enabled.params.user_id, enabled.params.enabled_roles))
|
|
{
|
|
SettingsProfileElement new_element;
|
|
new_element.parent_profile = profile_id;
|
|
merged_settings.emplace_back(new_element);
|
|
}
|
|
|
|
merged_settings.merge(enabled.params.settings_from_enabled_roles);
|
|
merged_settings.merge(enabled.params.settings_from_user);
|
|
|
|
auto info = std::make_shared<SettingsProfilesInfo>(access_control);
|
|
|
|
substituteProfiles(merged_settings, info->profiles, info->profiles_with_implicit, info->names_of_profiles);
|
|
|
|
info->settings = merged_settings.toSettingsChanges();
|
|
info->constraints = merged_settings.toSettingsConstraints(access_control);
|
|
|
|
enabled.setInfo(std::move(info));
|
|
}
|
|
|
|
|
|
void SettingsProfilesCache::substituteProfiles(
|
|
SettingsProfileElements & elements,
|
|
std::vector<UUID> & profiles,
|
|
std::vector<UUID> & substituted_profiles,
|
|
std::unordered_map<UUID, String> & names_of_substituted_profiles) const
|
|
{
|
|
profiles = elements.toProfileIDs();
|
|
|
|
/// We should substitute profiles in reversive order because the same profile can occur
|
|
/// in `elements` multiple times (with some other settings in between) and in this case
|
|
/// the last occurrence should override all the previous ones.
|
|
boost::container::flat_set<UUID> substituted_profiles_set;
|
|
size_t i = elements.size();
|
|
while (i != 0)
|
|
{
|
|
auto & element = elements[--i];
|
|
if (!element.parent_profile)
|
|
continue;
|
|
|
|
auto profile_id = *element.parent_profile;
|
|
element.parent_profile.reset();
|
|
if (substituted_profiles_set.count(profile_id))
|
|
continue;
|
|
|
|
auto profile_it = all_profiles.find(profile_id);
|
|
if (profile_it == all_profiles.end())
|
|
continue;
|
|
|
|
const auto & profile = profile_it->second;
|
|
const auto & profile_elements = profile->elements;
|
|
elements.insert(elements.begin() + i, profile_elements.begin(), profile_elements.end());
|
|
i += profile_elements.size();
|
|
substituted_profiles.push_back(profile_id);
|
|
substituted_profiles_set.insert(profile_id);
|
|
names_of_substituted_profiles.emplace(profile_id, profile->getName());
|
|
}
|
|
std::reverse(substituted_profiles.begin(), substituted_profiles.end());
|
|
|
|
std::erase_if(profiles, [&substituted_profiles_set](const UUID & profile_id)
|
|
{
|
|
return !substituted_profiles_set.contains(profile_id);
|
|
});
|
|
}
|
|
|
|
std::shared_ptr<const EnabledSettings> SettingsProfilesCache::getEnabledSettings(
|
|
const UUID & user_id,
|
|
const SettingsProfileElements & settings_from_user,
|
|
const boost::container::flat_set<UUID> & enabled_roles,
|
|
const SettingsProfileElements & settings_from_enabled_roles)
|
|
{
|
|
std::lock_guard lock{mutex};
|
|
ensureAllProfilesRead();
|
|
|
|
EnabledSettings::Params params;
|
|
params.user_id = user_id;
|
|
params.settings_from_user = settings_from_user;
|
|
params.enabled_roles = enabled_roles;
|
|
params.settings_from_enabled_roles = settings_from_enabled_roles;
|
|
|
|
auto it = enabled_settings.find(params);
|
|
if (it != enabled_settings.end())
|
|
{
|
|
auto from_cache = it->second.lock();
|
|
if (from_cache)
|
|
return from_cache;
|
|
enabled_settings.erase(it);
|
|
}
|
|
|
|
std::shared_ptr<EnabledSettings> res(new EnabledSettings(params));
|
|
enabled_settings.emplace(std::move(params), res);
|
|
mergeSettingsAndConstraintsFor(*res);
|
|
return res;
|
|
}
|
|
|
|
|
|
std::shared_ptr<const SettingsProfilesInfo> SettingsProfilesCache::getSettingsProfileInfo(const UUID & profile_id)
|
|
{
|
|
std::lock_guard lock{mutex};
|
|
ensureAllProfilesRead();
|
|
|
|
if (auto pos = this->profile_infos_cache.get(profile_id))
|
|
return *pos;
|
|
|
|
SettingsProfileElements elements;
|
|
auto & element = elements.emplace_back();
|
|
element.parent_profile = profile_id;
|
|
|
|
auto info = std::make_shared<SettingsProfilesInfo>(access_control);
|
|
|
|
substituteProfiles(elements, info->profiles, info->profiles_with_implicit, info->names_of_profiles);
|
|
info->settings = elements.toSettingsChanges();
|
|
info->constraints.merge(elements.toSettingsConstraints(access_control));
|
|
|
|
profile_infos_cache.add(profile_id, info);
|
|
return info;
|
|
}
|
|
|
|
}
|