#include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int THERE_IS_NO_PROFILE; } SettingsProfilesCache::SettingsProfilesCache(const AccessControlManager & manager_) : manager(manager_) {} SettingsProfilesCache::~SettingsProfilesCache() = default; void SettingsProfilesCache::ensureAllProfilesRead() { /// `mutex` is already locked. if (all_profiles_read) return; all_profiles_read = true; subscription = manager.subscribeForChanges( [&](const UUID & id, const AccessEntityPtr & entity) { if (entity) profileAddedOrChanged(id, typeid_cast(entity)); else profileRemoved(id); }); for (const UUID & id : manager.findAll()) { auto profile = manager.tryRead(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; } settings_for_profiles.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); settings_for_profiles.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("Settings profile " + backQuote(default_profile_name) + " not found", ErrorCodes::THERE_IS_NO_PROFILE); default_profile_id = it->second; } void SettingsProfilesCache::mergeSettingsAndConstraints() { /// `mutex` is already locked. std::erase_if( enabled_settings, [&](const std::pair> & pr) { auto enabled = pr.second.lock(); if (!enabled) return true; // remove from the `enabled_settings` list. mergeSettingsAndConstraintsFor(*enabled); return false; // keep in the `enabled_settings` list. }); } 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); substituteProfiles(merged_settings); enabled.setSettingsAndConstraints( std::make_shared(merged_settings.toSettings()), std::make_shared(merged_settings.toSettingsConstraints())); } void SettingsProfilesCache::substituteProfiles(SettingsProfileElements & elements) const { bool stop_substituting = false; boost::container::flat_set already_substituted; while (!stop_substituting) { stop_substituting = true; for (size_t i = 0; i != elements.size(); ++i) { auto & element = elements[i]; if (!element.parent_profile) continue; auto parent_profile_id = *element.parent_profile; element.parent_profile.reset(); if (already_substituted.contains(parent_profile_id)) continue; already_substituted.insert(parent_profile_id); auto parent_profile = all_profiles.find(parent_profile_id); if (parent_profile == all_profiles.end()) continue; const auto & parent_profile_elements = parent_profile->second->elements; elements.insert(elements.begin() + i + 1, parent_profile_elements.begin(), parent_profile_elements.end()); i += parent_profile_elements.size(); stop_substituting = false; } } } std::shared_ptr SettingsProfilesCache::getEnabledSettings( const UUID & user_id, const SettingsProfileElements & settings_from_user, const std::vector & 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 res(new EnabledSettings(params)); enabled_settings.emplace(std::move(params), res); mergeSettingsAndConstraintsFor(*res); return res; } std::shared_ptr SettingsProfilesCache::getProfileSettings(const String & profile_name) { std::lock_guard lock{mutex}; ensureAllProfilesRead(); auto it = profiles_by_name.find(profile_name); if (it == profiles_by_name.end()) throw Exception("Settings profile " + backQuote(profile_name) + " not found", ErrorCodes::THERE_IS_NO_PROFILE); const UUID profile_id = it->second; auto it2 = settings_for_profiles.find(profile_id); if (it2 != settings_for_profiles.end()) return it2->second; SettingsProfileElements elements = all_profiles[profile_id]->elements; substituteProfiles(elements); auto res = std::make_shared(elements.toSettingsChanges()); settings_for_profiles.emplace(profile_id, res); return res; } }