diff --git a/src/Access/AccessControl.cpp b/src/Access/AccessControl.cpp index 9cad53e667b6..3487043309c6 100644 --- a/src/Access/AccessControl.cpp +++ b/src/Access/AccessControl.cpp @@ -75,6 +75,7 @@ class AccessControl::ContextAccessCache cache.remove(params); } auto res = std::shared_ptr(new ContextAccess(access_control, params)); + res->initialize(); cache.add(params, res); return res; } @@ -136,10 +137,10 @@ class AccessControl::CustomSettingsPrefixes AccessControl::AccessControl() : MultipleAccessStorage("user directories"), context_access_cache(std::make_unique(*this)), - role_cache(std::make_unique(*this)), - row_policy_cache(std::make_unique(*this)), - quota_cache(std::make_unique(*this)), - settings_profiles_cache(std::make_unique(*this)), + role_cache(std::make_shared(*this)), + row_policy_cache(std::make_shared(*this)), + quota_cache(std::make_shared(*this)), + settings_profiles_cache(std::make_shared(*this)), external_authenticators(std::make_unique()), custom_settings_prefixes(std::make_unique()) { diff --git a/src/Access/AccessControl.h b/src/Access/AccessControl.h index 77709313d3e0..edf94bc2b3fd 100644 --- a/src/Access/AccessControl.h +++ b/src/Access/AccessControl.h @@ -161,10 +161,10 @@ class AccessControl : public MultipleAccessStorage class CustomSettingsPrefixes; std::unique_ptr context_access_cache; - std::unique_ptr role_cache; - std::unique_ptr row_policy_cache; - std::unique_ptr quota_cache; - std::unique_ptr settings_profiles_cache; + std::shared_ptr role_cache; + std::shared_ptr row_policy_cache; + std::shared_ptr quota_cache; + std::shared_ptr settings_profiles_cache; std::unique_ptr external_authenticators; std::unique_ptr custom_settings_prefixes; }; diff --git a/src/Access/ContextAccess.cpp b/src/Access/ContextAccess.cpp index b254a59376db..ad4901208e66 100644 --- a/src/Access/ContextAccess.cpp +++ b/src/Access/ContextAccess.cpp @@ -143,25 +143,47 @@ namespace ContextAccess::ContextAccess(const AccessControl & access_control_, const Params & params_) - : access_control(&access_control_) - , params(params_) + : access_control(&access_control_), params(params_) +{} + + +void ContextAccess::initialize() { std::lock_guard lock{mutex}; + if (is_full_access) + { + access = std::make_shared(AccessRights::getFullAccess()); + access_with_implicit = std::make_shared(addImplicitAccessRights(*access)); + return; + } + + auto weak_ptr = weak_from_this(); + assert(weak_ptr); subscription_for_user_change = access_control->subscribeForChanges( - *params.user_id, [this](const UUID &, const AccessEntityPtr & entity) + *params.user_id, [weak_ptr](const UUID &, const AccessEntityPtr & entity) { - UserPtr changed_user = entity ? typeid_cast(entity) : nullptr; - std::lock_guard lock2{mutex}; - setUser(changed_user); + auto ptr = weak_ptr.lock(); + if (!ptr) + return; + std::lock_guard lock2{ptr->mutex}; + ptr->setUser(typeid_cast(entity)); }); setUser(access_control->read(*params.user_id)); } +ContextAccess::~ContextAccess() +{ + std::lock_guard lock{mutex}; + setUser(nullptr); +} + + void ContextAccess::setUser(const UserPtr & user_) const { + /// `mutex` is already locked. user = user_; if (!user) { @@ -195,10 +217,14 @@ void ContextAccess::setUser(const UserPtr & user_) const subscription_for_roles_changes.reset(); enabled_roles = access_control->getEnabledRoles(current_roles, current_roles_with_admin_option); - subscription_for_roles_changes = enabled_roles->subscribeForChanges([this](const std::shared_ptr & roles_info_) + subscription_for_roles_changes = enabled_roles->subscribeForChanges( + [weak_ptr = weak_from_this()](const std::shared_ptr & roles_info_) { - std::lock_guard lock{mutex}; - setRolesInfo(roles_info_); + auto ptr = weak_ptr.lock(); + if (!ptr) + return; + std::lock_guard lock{ptr->mutex}; + ptr->setRolesInfo(roles_info_); }); setRolesInfo(enabled_roles->getRolesInfo()); @@ -207,6 +233,7 @@ void ContextAccess::setUser(const UserPtr & user_) const void ContextAccess::setRolesInfo(const std::shared_ptr & roles_info_) const { + /// `mutex` is already locked. assert(roles_info_); roles_info = roles_info_; enabled_row_policies = access_control->getEnabledRowPolicies( @@ -221,6 +248,7 @@ void ContextAccess::setRolesInfo(const std::shared_ptr & void ContextAccess::calculateAccessRights() const { + /// `mutex` is already locked. access = std::make_shared(mixAccessRightsFromUserAndRoles(*user, *roles_info)); access_with_implicit = std::make_shared(addImplicitAccessRights(*access)); @@ -302,8 +330,7 @@ std::shared_ptr ContextAccess::getFullAccess() { auto full_access = std::shared_ptr(new ContextAccess); full_access->is_full_access = true; - full_access->access = std::make_shared(AccessRights::getFullAccess()); - full_access->access_with_implicit = std::make_shared(addImplicitAccessRights(*full_access->access)); + full_access->initialize(); return full_access; }(); return res; diff --git a/src/Access/ContextAccess.h b/src/Access/ContextAccess.h index d7cc999e95fc..5f9706ca30e1 100644 --- a/src/Access/ContextAccess.h +++ b/src/Access/ContextAccess.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -63,7 +64,7 @@ struct ContextAccessParams }; -class ContextAccess +class ContextAccess : public std::enable_shared_from_this { public: using Params = ContextAccessParams; @@ -156,11 +157,15 @@ class ContextAccess /// without any limitations. This is used for the global context. static std::shared_ptr getFullAccess(); + ~ContextAccess(); + private: friend class AccessControl; ContextAccess() {} ContextAccess(const AccessControl & access_control_, const Params & params_); + void initialize(); + void setUser(const UserPtr & user_) const; void setRolesInfo(const std::shared_ptr & roles_info_) const; void setSettingsAndConstraints() const; diff --git a/src/Access/QuotaCache.cpp b/src/Access/QuotaCache.cpp index 43ab4268b0c7..637ee931b24f 100644 --- a/src/Access/QuotaCache.cpp +++ b/src/Access/QuotaCache.cpp @@ -163,13 +163,17 @@ QuotaCache::QuotaCache(const AccessControl & access_control_) { } -QuotaCache::~QuotaCache() = default; +QuotaCache::~QuotaCache() +{ + std::lock_guard lock{mutex}; + unloadAllQuotas(); +} std::shared_ptr QuotaCache::getEnabledQuota(const UUID & user_id, const String & user_name, const boost::container::flat_set & enabled_roles, const Poco::Net::IPAddress & client_address, const String & forwarded_address, const String & client_key) { std::lock_guard lock{mutex}; - ensureAllQuotasRead(); + loadAllQuotas(); EnabledQuota::Params params; params.user_id = user_id; @@ -194,21 +198,24 @@ std::shared_ptr QuotaCache::getEnabledQuota(const UUID & use } -void QuotaCache::ensureAllQuotasRead() +void QuotaCache::loadAllQuotas() { /// `mutex` is already locked. - if (all_quotas_read) + if (all_quotas_loaded) return; - all_quotas_read = true; + all_quotas_loaded = true; subscription = access_control.subscribeForChanges( - [&](const UUID & id, const AccessEntityPtr & entity) - { - if (entity) - quotaAddedOrChanged(id, typeid_cast(entity)); - else - quotaRemoved(id); - }); + [weak_ptr = weak_from_this()](const UUID & id, const AccessEntityPtr & entity) + { + auto ptr = weak_ptr.lock(); + if (!ptr) + return; + if (auto quota = typeid_cast(entity)) + ptr->quotaAddedOrChanged(id, quota); + else + ptr->quotaRemoved(id); + }); for (const UUID & quota_id : access_control.findAll()) { @@ -219,6 +226,16 @@ void QuotaCache::ensureAllQuotasRead() } +void QuotaCache::unloadAllQuotas() +{ + /// `mutex` is already locked. + subscription.reset(); + all_quotas.clear(); + all_quotas_loaded = false; + enabled_quotas.clear(); +} + + void QuotaCache::quotaAddedOrChanged(const UUID & quota_id, const std::shared_ptr & new_quota) { std::lock_guard lock{mutex}; @@ -250,7 +267,6 @@ void QuotaCache::quotaRemoved(const UUID & quota_id) void QuotaCache::chooseQuotaToConsume() { /// `mutex` is already locked. - for (auto i = enabled_quotas.begin(), e = enabled_quotas.end(); i != e;) { auto elem = i->second.lock(); diff --git a/src/Access/QuotaCache.h b/src/Access/QuotaCache.h index 7298acad4153..f64875007388 100644 --- a/src/Access/QuotaCache.h +++ b/src/Access/QuotaCache.h @@ -16,7 +16,7 @@ using QuotaPtr = std::shared_ptr; struct RolesOrUsersSet; /// Stores information how much amount of resources have been consumed and how much are left. -class QuotaCache +class QuotaCache : public std::enable_shared_from_this { public: QuotaCache(const AccessControl & access_control_); @@ -52,7 +52,8 @@ class QuotaCache std::unordered_map> key_to_intervals; }; - void ensureAllQuotasRead(); + void loadAllQuotas(); + void unloadAllQuotas(); void quotaAddedOrChanged(const UUID & quota_id, const std::shared_ptr & new_quota); void quotaRemoved(const UUID & quota_id); void chooseQuotaToConsume(); @@ -61,7 +62,7 @@ class QuotaCache const AccessControl & access_control; mutable std::mutex mutex; std::unordered_map all_quotas; - bool all_quotas_read = false; + bool all_quotas_loaded = false; scope_guard subscription; std::map> enabled_quotas; }; diff --git a/src/Access/RoleCache.cpp b/src/Access/RoleCache.cpp index f0e1435e299a..e652a701e89b 100644 --- a/src/Access/RoleCache.cpp +++ b/src/Access/RoleCache.cpp @@ -60,7 +60,18 @@ RoleCache::RoleCache(const AccessControl & access_control_) : access_control(access_control_), cache(600000 /* 10 minutes */) {} -RoleCache::~RoleCache() = default; +RoleCache::~RoleCache() +{ + unloadAllRoles(); +} + + +void RoleCache::unloadAllRoles() +{ + std::lock_guard lock{mutex}; + cache.clear(); + enabled_roles.clear(); +} std::shared_ptr @@ -92,7 +103,6 @@ RoleCache::getEnabledRoles(const std::vector & roles, const std::vectorsecond.lock(); @@ -131,19 +141,21 @@ void RoleCache::collectEnabledRoles(EnabledRoles & enabled, scope_guard & notifi RolePtr RoleCache::getRole(const UUID & role_id) { /// `mutex` is already locked. - auto role_from_cache = cache.get(role_id); if (role_from_cache) return role_from_cache->first; - auto subscription = access_control.subscribeForChanges(role_id, - [this, role_id](const UUID &, const AccessEntityPtr & entity) + auto subscription = access_control.subscribeForChanges( + role_id, + [weak_ptr = weak_from_this(), role_id](const UUID &, const AccessEntityPtr & entity) { - auto changed_role = entity ? typeid_cast(entity) : nullptr; - if (changed_role) - roleChanged(role_id, changed_role); + auto ptr = weak_ptr.lock(); + if (!ptr) + return; + if (auto role = typeid_cast(entity)) + ptr->roleChanged(role_id, role); else - roleRemoved(role_id); + ptr->roleRemoved(role_id); }); auto role = access_control.tryRead(role_id); diff --git a/src/Access/RoleCache.h b/src/Access/RoleCache.h index 42f4eec5b498..9c0764d52e05 100644 --- a/src/Access/RoleCache.h +++ b/src/Access/RoleCache.h @@ -13,7 +13,7 @@ class AccessControl; struct Role; using RolePtr = std::shared_ptr; -class RoleCache +class RoleCache : public std::enable_shared_from_this { public: RoleCache(const AccessControl & access_control_); @@ -24,6 +24,7 @@ class RoleCache const std::vector & current_roles_with_admin_option); private: + void unloadAllRoles(); void collectEnabledRoles(scope_guard & notifications); void collectEnabledRoles(EnabledRoles & enabled, scope_guard & notifications); RolePtr getRole(const UUID & role_id); diff --git a/src/Access/RowPolicyCache.cpp b/src/Access/RowPolicyCache.cpp index 55bec4271580..95502db2fadd 100644 --- a/src/Access/RowPolicyCache.cpp +++ b/src/Access/RowPolicyCache.cpp @@ -95,13 +95,17 @@ RowPolicyCache::RowPolicyCache(const AccessControl & access_control_) { } -RowPolicyCache::~RowPolicyCache() = default; +RowPolicyCache::~RowPolicyCache() +{ + std::lock_guard lock{mutex}; + unloadAllPolicies(); +} std::shared_ptr RowPolicyCache::getEnabledRowPolicies(const UUID & user_id, const boost::container::flat_set & enabled_roles) { std::lock_guard lock{mutex}; - ensureAllRowPoliciesRead(); + loadAllPolicies(); EnabledRowPolicies::Params params; params.user_id = user_id; @@ -122,20 +126,23 @@ std::shared_ptr RowPolicyCache::getEnabledRowPolicies( } -void RowPolicyCache::ensureAllRowPoliciesRead() +void RowPolicyCache::loadAllPolicies() { /// `mutex` is already locked. - if (all_policies_read) + if (all_policies_loaded) return; - all_policies_read = true; + all_policies_loaded = true; subscription = access_control.subscribeForChanges( - [&](const UUID & id, const AccessEntityPtr & entity) + [weak_ptr = weak_from_this()](const UUID & id, const AccessEntityPtr & entity) { - if (entity) - rowPolicyAddedOrChanged(id, typeid_cast(entity)); + auto ptr = weak_ptr.lock(); + if (!ptr) + return; + if (auto policy = typeid_cast(entity)) + ptr->policyAddedOrChanged(id, policy); else - rowPolicyRemoved(id); + ptr->policyRemoved(id); }); for (const UUID & id : access_control.findAll()) @@ -147,7 +154,17 @@ void RowPolicyCache::ensureAllRowPoliciesRead() } -void RowPolicyCache::rowPolicyAddedOrChanged(const UUID & policy_id, const RowPolicyPtr & new_policy) +void RowPolicyCache::unloadAllPolicies() +{ + /// `mutex` is already locked. + subscription.reset(); + all_policies.clear(); + all_policies_loaded = false; + enabled_row_policies.clear(); +} + + +void RowPolicyCache::policyAddedOrChanged(const UUID & policy_id, const RowPolicyPtr & new_policy) { std::lock_guard lock{mutex}; auto it = all_policies.find(policy_id); @@ -167,7 +184,7 @@ void RowPolicyCache::rowPolicyAddedOrChanged(const UUID & policy_id, const RowPo } -void RowPolicyCache::rowPolicyRemoved(const UUID & policy_id) +void RowPolicyCache::policyRemoved(const UUID & policy_id) { std::lock_guard lock{mutex}; all_policies.erase(policy_id); @@ -195,7 +212,6 @@ void RowPolicyCache::mixFilters() void RowPolicyCache::mixFiltersFor(EnabledRowPolicies & enabled) { /// `mutex` is already locked. - using MixedFiltersMap = EnabledRowPolicies::MixedFiltersMap; using MixedFiltersKey = EnabledRowPolicies::MixedFiltersKey; using Hash = EnabledRowPolicies::Hash; diff --git a/src/Access/RowPolicyCache.h b/src/Access/RowPolicyCache.h index dc416fe59f05..52324d9461c1 100644 --- a/src/Access/RowPolicyCache.h +++ b/src/Access/RowPolicyCache.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -15,7 +15,7 @@ struct RowPolicy; using RowPolicyPtr = std::shared_ptr; /// Stores read and parsed row policies. -class RowPolicyCache +class RowPolicyCache : public std::enable_shared_from_this { public: RowPolicyCache(const AccessControl & access_control_); @@ -35,15 +35,16 @@ class RowPolicyCache ASTPtr parsed_filters[static_cast(RowPolicyFilterType::MAX)]; }; - void ensureAllRowPoliciesRead(); - void rowPolicyAddedOrChanged(const UUID & policy_id, const RowPolicyPtr & new_policy); - void rowPolicyRemoved(const UUID & policy_id); + void loadAllPolicies(); + void unloadAllPolicies(); + void policyAddedOrChanged(const UUID & policy_id, const RowPolicyPtr & new_policy); + void policyRemoved(const UUID & policy_id); void mixFilters(); void mixFiltersFor(EnabledRowPolicies & enabled); const AccessControl & access_control; std::unordered_map all_policies; - bool all_policies_read = false; + bool all_policies_loaded = false; scope_guard subscription; std::map> enabled_row_policies; std::mutex mutex; diff --git a/src/Access/SettingsProfilesCache.cpp b/src/Access/SettingsProfilesCache.cpp index 2a3dedbbd7a4..8a5d0e9caae3 100644 --- a/src/Access/SettingsProfilesCache.cpp +++ b/src/Access/SettingsProfilesCache.cpp @@ -15,24 +15,32 @@ namespace ErrorCodes SettingsProfilesCache::SettingsProfilesCache(const AccessControl & access_control_) : access_control(access_control_) {} -SettingsProfilesCache::~SettingsProfilesCache() = default; + +SettingsProfilesCache::~SettingsProfilesCache() +{ + std::lock_guard lock{mutex}; + unloadAllProfiles(); +} -void SettingsProfilesCache::ensureAllProfilesRead() +void SettingsProfilesCache::loadAllProfiles() { /// `mutex` is already locked. - if (all_profiles_read) + if (all_profiles_loaded) return; - all_profiles_read = true; + all_profiles_loaded = true; subscription = access_control.subscribeForChanges( - [&](const UUID & id, const AccessEntityPtr & entity) - { - if (entity) - profileAddedOrChanged(id, typeid_cast(entity)); - else - profileRemoved(id); - }); + [weak_ptr = weak_from_this()](const UUID & id, const AccessEntityPtr & entity) + { + auto ptr = weak_ptr.lock(); + if (!ptr) + return; + if (auto profile = typeid_cast(entity)) + ptr->profileAddedOrChanged(id, profile); + else + ptr->profileRemoved(id); + }); for (const UUID & id : access_control.findAll()) { @@ -46,6 +54,19 @@ void SettingsProfilesCache::ensureAllProfilesRead() } +void SettingsProfilesCache::unloadAllProfiles() +{ + /// `mutex` is already locked. + subscription.reset(); + all_profiles.clear(); + profiles_by_name.clear(); + all_profiles_loaded = false; + enabled_settings.clear(); + default_profile_id.reset(); + profile_infos_cache.clear(); +} + + void SettingsProfilesCache::profileAddedOrChanged(const UUID & profile_id, const SettingsProfilePtr & new_profile) { std::lock_guard lock{mutex}; @@ -81,22 +102,13 @@ void SettingsProfilesCache::profileRemoved(const UUID & profile_id) } -void SettingsProfilesCache::setDefaultProfileName(const String & default_profile_name) +void SettingsProfilesCache::setDefaultProfileName(const String & default_profile_name_) { std::lock_guard lock{mutex}; - ensureAllProfilesRead(); - - if (default_profile_name.empty()) - { - default_profile_id = {}; + if (default_profile_name == default_profile_name_) 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; + default_profile_name = default_profile_name_; + default_profile_id = {}; } @@ -117,14 +129,28 @@ void SettingsProfilesCache::mergeSettingsAndConstraints() } -void SettingsProfilesCache::mergeSettingsAndConstraintsFor(EnabledSettings & enabled) const +void SettingsProfilesCache::mergeSettingsAndConstraintsFor(EnabledSettings & enabled) { + /// `mutex` is already locked. SettingsProfileElements merged_settings; - if (default_profile_id) + + if (!default_profile_name.empty()) { - SettingsProfileElement new_element; - new_element.parent_profile = *default_profile_id; - merged_settings.emplace_back(new_element); + if (!default_profile_id) + { + 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; + } + + 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) @@ -153,6 +179,8 @@ void SettingsProfilesCache::substituteProfiles( std::vector & substituted_profiles, std::unordered_map & names_of_substituted_profiles) const { + /// `mutex` is already locked. + /// 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. @@ -191,7 +219,7 @@ std::shared_ptr SettingsProfilesCache::getEnabledSettings const SettingsProfileElements & settings_from_enabled_roles) { std::lock_guard lock{mutex}; - ensureAllProfilesRead(); + loadAllProfiles(); EnabledSettings::Params params; params.user_id = user_id; @@ -218,7 +246,7 @@ std::shared_ptr SettingsProfilesCache::getEnabledSettings std::shared_ptr SettingsProfilesCache::getSettingsProfileInfo(const UUID & profile_id) { std::lock_guard lock{mutex}; - ensureAllProfilesRead(); + loadAllProfiles(); if (auto pos = this->profile_infos_cache.get(profile_id)) return *pos; diff --git a/src/Access/SettingsProfilesCache.h b/src/Access/SettingsProfilesCache.h index da852275ff58..96647d497380 100644 --- a/src/Access/SettingsProfilesCache.h +++ b/src/Access/SettingsProfilesCache.h @@ -15,13 +15,13 @@ using SettingsProfilePtr = std::shared_ptr; struct SettingsProfilesInfo; /// Reads and caches all the settings profiles. -class SettingsProfilesCache +class SettingsProfilesCache : public std::enable_shared_from_this { public: SettingsProfilesCache(const AccessControl & access_control_); ~SettingsProfilesCache(); - void setDefaultProfileName(const String & default_profile_name); + void setDefaultProfileName(const String & default_profile_name_); std::shared_ptr getEnabledSettings( const UUID & user_id, @@ -32,19 +32,21 @@ class SettingsProfilesCache std::shared_ptr getSettingsProfileInfo(const UUID & profile_id); private: - void ensureAllProfilesRead(); + void loadAllProfiles(); + void unloadAllProfiles(); void profileAddedOrChanged(const UUID & profile_id, const SettingsProfilePtr & new_profile); void profileRemoved(const UUID & profile_id); void mergeSettingsAndConstraints(); - void mergeSettingsAndConstraintsFor(EnabledSettings & enabled) const; + void mergeSettingsAndConstraintsFor(EnabledSettings & enabled); void substituteProfiles(SettingsProfileElements & elements, std::vector & substituted_profiles, std::unordered_map & names_of_substituted_profiles) const; const AccessControl & access_control; std::unordered_map all_profiles; std::unordered_map profiles_by_name; - bool all_profiles_read = false; + bool all_profiles_loaded = false; scope_guard subscription; std::map> enabled_settings; + String default_profile_name; std::optional default_profile_id; Poco::LRUCache> profile_infos_cache; mutable std::mutex mutex;