2020-03-07 17:37:38 +00:00
|
|
|
#include <Access/RoleCache.h>
|
|
|
|
#include <Access/Role.h>
|
|
|
|
#include <Access/EnabledRolesInfo.h>
|
2021-11-02 11:06:20 +00:00
|
|
|
#include <Access/AccessControl.h>
|
2020-04-29 19:35:56 +00:00
|
|
|
#include <boost/container/flat_set.hpp>
|
2021-10-02 19:47:35 +00:00
|
|
|
#include <base/FnTraits.h>
|
2020-03-07 17:37:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
namespace
|
|
|
|
{
|
2020-04-29 19:35:56 +00:00
|
|
|
void collectRoles(EnabledRolesInfo & roles_info,
|
|
|
|
boost::container::flat_set<UUID> & skip_ids,
|
2021-09-28 21:01:16 +00:00
|
|
|
Fn<RolePtr(const UUID &)> auto && get_role_function,
|
2020-03-07 17:37:38 +00:00
|
|
|
const UUID & role_id,
|
|
|
|
bool is_current_role,
|
|
|
|
bool with_admin_option)
|
|
|
|
{
|
2020-04-29 19:35:56 +00:00
|
|
|
if (roles_info.enabled_roles.count(role_id))
|
2020-03-07 17:37:38 +00:00
|
|
|
{
|
2020-04-29 19:35:56 +00:00
|
|
|
if (is_current_role)
|
|
|
|
roles_info.current_roles.emplace(role_id);
|
|
|
|
if (with_admin_option)
|
|
|
|
roles_info.enabled_roles_with_admin_option.emplace(role_id);
|
2020-03-07 17:37:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-29 19:35:56 +00:00
|
|
|
if (skip_ids.count(role_id))
|
|
|
|
return;
|
|
|
|
|
2020-03-07 17:37:38 +00:00
|
|
|
auto role = get_role_function(role_id);
|
|
|
|
|
|
|
|
if (!role)
|
2020-04-29 19:35:56 +00:00
|
|
|
{
|
|
|
|
skip_ids.emplace(role_id);
|
2020-03-07 17:37:38 +00:00
|
|
|
return;
|
2020-04-29 19:35:56 +00:00
|
|
|
}
|
2020-03-07 17:37:38 +00:00
|
|
|
|
2020-04-29 19:35:56 +00:00
|
|
|
roles_info.enabled_roles.emplace(role_id);
|
|
|
|
if (is_current_role)
|
|
|
|
roles_info.current_roles.emplace(role_id);
|
|
|
|
if (with_admin_option)
|
|
|
|
roles_info.enabled_roles_with_admin_option.emplace(role_id);
|
2020-03-07 17:37:38 +00:00
|
|
|
|
2020-04-29 19:35:56 +00:00
|
|
|
roles_info.names_of_roles[role_id] = role->getName();
|
2020-07-02 00:09:57 +00:00
|
|
|
roles_info.access.makeUnion(role->access);
|
2020-04-29 19:35:56 +00:00
|
|
|
roles_info.settings_from_enabled_roles.merge(role->settings);
|
2020-03-07 17:37:38 +00:00
|
|
|
|
2021-02-26 22:37:00 +00:00
|
|
|
for (const auto & granted_role : role->granted_roles.getGranted())
|
2020-04-29 19:35:56 +00:00
|
|
|
collectRoles(roles_info, skip_ids, get_role_function, granted_role, false, false);
|
2020-03-07 17:37:38 +00:00
|
|
|
|
2021-02-26 22:37:00 +00:00
|
|
|
for (const auto & granted_role : role->granted_roles.getGrantedWithAdminOption())
|
2020-04-29 19:35:56 +00:00
|
|
|
collectRoles(roles_info, skip_ids, get_role_function, granted_role, false, true);
|
2020-03-07 17:37:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-02 11:06:20 +00:00
|
|
|
RoleCache::RoleCache(const AccessControl & access_control_)
|
|
|
|
: access_control(access_control_), cache(600000 /* 10 minutes */) {}
|
2020-03-07 17:37:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
RoleCache::~RoleCache() = default;
|
|
|
|
|
|
|
|
|
2020-04-29 19:35:56 +00:00
|
|
|
std::shared_ptr<const EnabledRoles>
|
2021-02-26 22:37:00 +00:00
|
|
|
RoleCache::getEnabledRoles(const std::vector<UUID> & roles, const std::vector<UUID> & roles_with_admin_option)
|
2020-03-07 17:37:38 +00:00
|
|
|
{
|
2020-10-12 15:39:39 +00:00
|
|
|
std::lock_guard lock{mutex};
|
2020-03-07 17:37:38 +00:00
|
|
|
EnabledRoles::Params params;
|
2021-02-26 22:37:00 +00:00
|
|
|
params.current_roles.insert(roles.begin(), roles.end());
|
|
|
|
params.current_roles_with_admin_option.insert(roles_with_admin_option.begin(), roles_with_admin_option.end());
|
2020-03-07 17:37:38 +00:00
|
|
|
auto it = enabled_roles.find(params);
|
|
|
|
if (it != enabled_roles.end())
|
|
|
|
{
|
|
|
|
auto from_cache = it->second.lock();
|
|
|
|
if (from_cache)
|
|
|
|
return from_cache;
|
|
|
|
enabled_roles.erase(it);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto res = std::shared_ptr<EnabledRoles>(new EnabledRoles(params));
|
2022-05-16 18:43:55 +00:00
|
|
|
collectEnabledRoles(*res, nullptr);
|
2020-03-07 17:37:38 +00:00
|
|
|
enabled_roles.emplace(std::move(params), res);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-05-16 18:43:55 +00:00
|
|
|
void RoleCache::collectEnabledRoles(scope_guard * notifications)
|
2020-03-07 17:37:38 +00:00
|
|
|
{
|
|
|
|
/// `mutex` is already locked.
|
|
|
|
|
2020-04-16 12:31:57 +00:00
|
|
|
for (auto i = enabled_roles.begin(), e = enabled_roles.end(); i != e;)
|
|
|
|
{
|
|
|
|
auto elem = i->second.lock();
|
|
|
|
if (!elem)
|
|
|
|
i = enabled_roles.erase(i);
|
|
|
|
else
|
2020-03-07 17:37:38 +00:00
|
|
|
{
|
2020-10-12 15:39:39 +00:00
|
|
|
collectEnabledRoles(*elem, notifications);
|
2020-04-16 12:31:57 +00:00
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
2020-03-07 17:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-05-16 18:43:55 +00:00
|
|
|
void RoleCache::collectEnabledRoles(EnabledRoles & enabled, scope_guard * notifications)
|
2020-03-07 17:37:38 +00:00
|
|
|
{
|
|
|
|
/// `mutex` is already locked.
|
|
|
|
|
2020-04-29 19:35:56 +00:00
|
|
|
/// Collect enabled roles. That includes the current roles, the roles granted to the current roles, and so on.
|
|
|
|
auto new_info = std::make_shared<EnabledRolesInfo>();
|
|
|
|
boost::container::flat_set<UUID> skip_ids;
|
2021-09-28 21:01:16 +00:00
|
|
|
|
2020-03-07 17:37:38 +00:00
|
|
|
auto get_role_function = [this](const UUID & id) { return getRole(id); };
|
2021-09-28 21:01:16 +00:00
|
|
|
|
2020-03-07 17:37:38 +00:00
|
|
|
for (const auto & current_role : enabled.params.current_roles)
|
2020-04-29 19:35:56 +00:00
|
|
|
collectRoles(*new_info, skip_ids, get_role_function, current_role, true, false);
|
2020-03-07 17:37:38 +00:00
|
|
|
|
|
|
|
for (const auto & current_role : enabled.params.current_roles_with_admin_option)
|
2020-04-29 19:35:56 +00:00
|
|
|
collectRoles(*new_info, skip_ids, get_role_function, current_role, true, true);
|
2020-03-07 17:37:38 +00:00
|
|
|
|
|
|
|
/// Collect data from the collected roles.
|
2020-10-12 15:39:39 +00:00
|
|
|
enabled.setRolesInfo(new_info, notifications);
|
2020-03-07 17:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2021-11-02 11:06:20 +00:00
|
|
|
auto subscription = access_control.subscribeForChanges(role_id,
|
2020-03-07 17:37:38 +00:00
|
|
|
[this, role_id](const UUID &, const AccessEntityPtr & entity)
|
|
|
|
{
|
|
|
|
auto changed_role = entity ? typeid_cast<RolePtr>(entity) : nullptr;
|
|
|
|
if (changed_role)
|
|
|
|
roleChanged(role_id, changed_role);
|
|
|
|
else
|
|
|
|
roleRemoved(role_id);
|
|
|
|
});
|
|
|
|
|
2021-11-02 11:06:20 +00:00
|
|
|
auto role = access_control.tryRead<Role>(role_id);
|
2020-03-07 17:37:38 +00:00
|
|
|
if (role)
|
|
|
|
{
|
2021-06-15 19:55:21 +00:00
|
|
|
auto cache_value = Poco::SharedPtr<std::pair<RolePtr, scope_guard>>(
|
|
|
|
new std::pair<RolePtr, scope_guard>{role, std::move(subscription)});
|
2020-03-07 17:37:38 +00:00
|
|
|
cache.add(role_id, cache_value);
|
|
|
|
return role;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RoleCache::roleChanged(const UUID & role_id, const RolePtr & changed_role)
|
|
|
|
{
|
2020-10-12 15:39:39 +00:00
|
|
|
/// Declared before `lock` to send notifications after the mutex will be unlocked.
|
2021-06-15 19:55:21 +00:00
|
|
|
scope_guard notifications;
|
2020-10-12 15:39:39 +00:00
|
|
|
|
2020-03-07 17:37:38 +00:00
|
|
|
std::lock_guard lock{mutex};
|
|
|
|
auto role_from_cache = cache.get(role_id);
|
|
|
|
if (!role_from_cache)
|
|
|
|
return;
|
|
|
|
role_from_cache->first = changed_role;
|
|
|
|
cache.update(role_id, role_from_cache);
|
2022-05-16 18:43:55 +00:00
|
|
|
collectEnabledRoles(¬ifications);
|
2020-03-07 17:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RoleCache::roleRemoved(const UUID & role_id)
|
|
|
|
{
|
2020-10-12 15:39:39 +00:00
|
|
|
/// Declared before `lock` to send notifications after the mutex will be unlocked.
|
2021-06-15 19:55:21 +00:00
|
|
|
scope_guard notifications;
|
2020-10-12 15:39:39 +00:00
|
|
|
|
2020-03-07 17:37:38 +00:00
|
|
|
std::lock_guard lock{mutex};
|
|
|
|
cache.remove(role_id);
|
2022-05-16 18:43:55 +00:00
|
|
|
collectEnabledRoles(¬ifications);
|
2020-03-07 17:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|