mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-23 16:12:01 +00:00
Fix partial revokes (complex cases).
This commit is contained in:
parent
40942a2164
commit
c39eb8f71b
@ -40,27 +40,8 @@ class AccessControlManager::ContextAccessCache
|
||||
public:
|
||||
explicit ContextAccessCache(const AccessControlManager & manager_) : manager(manager_) {}
|
||||
|
||||
std::shared_ptr<const ContextAccess> getContextAccess(
|
||||
const UUID & user_id,
|
||||
const boost::container::flat_set<UUID> & current_roles,
|
||||
bool use_default_roles,
|
||||
const Settings & settings,
|
||||
const String & current_database,
|
||||
const ClientInfo & client_info)
|
||||
std::shared_ptr<const ContextAccess> getContextAccess(const ContextAccessParams & params)
|
||||
{
|
||||
ContextAccess::Params params;
|
||||
params.user_id = user_id;
|
||||
params.current_roles = current_roles;
|
||||
params.use_default_roles = use_default_roles;
|
||||
params.current_database = current_database;
|
||||
params.readonly = settings.readonly;
|
||||
params.allow_ddl = settings.allow_ddl;
|
||||
params.allow_introspection = settings.allow_introspection_functions;
|
||||
params.interface = client_info.interface;
|
||||
params.http_method = client_info.http_method;
|
||||
params.address = client_info.current_address.host();
|
||||
params.quota_key = client_info.quota_key;
|
||||
|
||||
std::lock_guard lock{mutex};
|
||||
auto x = cache.get(params);
|
||||
if (x)
|
||||
@ -119,7 +100,25 @@ std::shared_ptr<const ContextAccess> AccessControlManager::getContextAccess(
|
||||
const String & current_database,
|
||||
const ClientInfo & client_info) const
|
||||
{
|
||||
return context_access_cache->getContextAccess(user_id, current_roles, use_default_roles, settings, current_database, client_info);
|
||||
ContextAccessParams params;
|
||||
params.user_id = user_id;
|
||||
params.current_roles = current_roles;
|
||||
params.use_default_roles = use_default_roles;
|
||||
params.current_database = current_database;
|
||||
params.readonly = settings.readonly;
|
||||
params.allow_ddl = settings.allow_ddl;
|
||||
params.allow_introspection = settings.allow_introspection_functions;
|
||||
params.interface = client_info.interface;
|
||||
params.http_method = client_info.http_method;
|
||||
params.address = client_info.current_address.host();
|
||||
params.quota_key = client_info.quota_key;
|
||||
return getContextAccess(params);
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<const ContextAccess> AccessControlManager::getContextAccess(const ContextAccessParams & params) const
|
||||
{
|
||||
return context_access_cache->getContextAccess(params);
|
||||
}
|
||||
|
||||
|
||||
|
@ -21,6 +21,7 @@ namespace Poco
|
||||
namespace DB
|
||||
{
|
||||
class ContextAccess;
|
||||
struct ContextAccessParams;
|
||||
struct User;
|
||||
using UserPtr = std::shared_ptr<const User>;
|
||||
class EnabledRoles;
|
||||
@ -58,6 +59,8 @@ public:
|
||||
const String & current_database,
|
||||
const ClientInfo & client_info) const;
|
||||
|
||||
std::shared_ptr<const ContextAccess> getContextAccess(const ContextAccessParams & params) const;
|
||||
|
||||
std::shared_ptr<const EnabledRoles> getEnabledRoles(
|
||||
const boost::container::flat_set<UUID> & current_roles,
|
||||
const boost::container::flat_set<UUID> & current_roles_with_admin_option) const;
|
||||
|
@ -1,7 +1,9 @@
|
||||
#include <Access/AccessRights.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
#include <boost/range/algorithm/sort.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace DB
|
||||
@ -9,7 +11,6 @@ namespace DB
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int INVALID_GRANT;
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@ -58,12 +59,194 @@ namespace
|
||||
const AccessFlags system_reload_embedded_dictionaries = AccessType::SYSTEM_RELOAD_EMBEDDED_DICTIONARIES;
|
||||
};
|
||||
|
||||
std::string_view checkCurrentDatabase(const std::string_view & current_database)
|
||||
using Kind = AccessRightsElementWithOptions::Kind;
|
||||
|
||||
struct ProtoElement
|
||||
{
|
||||
if (current_database.empty())
|
||||
throw Exception("No current database", ErrorCodes::LOGICAL_ERROR);
|
||||
return current_database;
|
||||
}
|
||||
AccessFlags access_flags;
|
||||
boost::container::small_vector<std::string_view, 3> full_name;
|
||||
bool grant_option = false;
|
||||
Kind kind = Kind::GRANT;
|
||||
|
||||
friend bool operator<(const ProtoElement & left, const ProtoElement & right)
|
||||
{
|
||||
static constexpr auto compare_name = [](const boost::container::small_vector<std::string_view, 3> & left_name,
|
||||
const boost::container::small_vector<std::string_view, 3> & right_name,
|
||||
size_t i)
|
||||
{
|
||||
if (i < left_name.size())
|
||||
{
|
||||
if (i < right_name.size())
|
||||
return left_name[i].compare(right_name[i]);
|
||||
else
|
||||
return 1; /// left_name is longer => left_name > right_name
|
||||
}
|
||||
else if (i < right_name.size())
|
||||
return 1; /// right_name is longer => left < right
|
||||
else
|
||||
return 0; /// left_name == right_name
|
||||
};
|
||||
|
||||
if (int cmp = compare_name(left.full_name, right.full_name, 0))
|
||||
return cmp < 0;
|
||||
|
||||
if (int cmp = compare_name(left.full_name, right.full_name, 1))
|
||||
return cmp < 0;
|
||||
|
||||
if (left.kind != right.kind)
|
||||
return (left.kind == Kind::GRANT);
|
||||
|
||||
if (left.grant_option != right.grant_option)
|
||||
return right.grant_option;
|
||||
|
||||
if (int cmp = compare_name(left.full_name, right.full_name, 2))
|
||||
return cmp < 0;
|
||||
|
||||
return (left.access_flags < right.access_flags);
|
||||
}
|
||||
|
||||
AccessRightsElementWithOptions getResult() const
|
||||
{
|
||||
AccessRightsElementWithOptions res;
|
||||
res.access_flags = access_flags;
|
||||
res.grant_option = grant_option;
|
||||
res.kind = kind;
|
||||
switch (full_name.size())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
res.any_database = true;
|
||||
res.any_table = true;
|
||||
res.any_column = true;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
res.any_database = false;
|
||||
res.database = full_name[0];
|
||||
res.any_table = true;
|
||||
res.any_column = true;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
res.any_database = false;
|
||||
res.database = full_name[0];
|
||||
res.any_table = false;
|
||||
res.table = full_name[1];
|
||||
res.any_column = true;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
res.any_database = false;
|
||||
res.database = full_name[0];
|
||||
res.any_table = false;
|
||||
res.table = full_name[1];
|
||||
res.any_column = false;
|
||||
res.columns.emplace_back(full_name[2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
class ProtoElements : public std::vector<ProtoElement>
|
||||
{
|
||||
public:
|
||||
AccessRightsElementsWithOptions getResult() const
|
||||
{
|
||||
ProtoElements sorted = *this;
|
||||
boost::range::sort(sorted);
|
||||
AccessRightsElementsWithOptions res;
|
||||
res.reserve(sorted.size());
|
||||
|
||||
for (size_t i = 0; i != sorted.size();)
|
||||
{
|
||||
size_t count_elements_with_diff_columns = sorted.countElementsWithDifferenceInColumnOnly(i);
|
||||
if (count_elements_with_diff_columns == 1)
|
||||
{
|
||||
/// Easy case: one Element is converted to one AccessRightsElement.
|
||||
const auto & element = sorted[i];
|
||||
if (element.access_flags)
|
||||
res.emplace_back(element.getResult());
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Difficult case: multiple Elements are converted to one or multiple AccessRightsElements.
|
||||
sorted.appendResultWithElementsWithDifferenceInColumnOnly(i, count_elements_with_diff_columns, res);
|
||||
i += count_elements_with_diff_columns;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t countElementsWithDifferenceInColumnOnly(size_t start) const
|
||||
{
|
||||
const auto & start_element = (*this)[start];
|
||||
if ((start_element.full_name.size() != 3) || (start == size() - 1))
|
||||
return 1;
|
||||
|
||||
auto it = std::find_if(begin() + start + 1, end(), [&](const ProtoElement & element)
|
||||
{
|
||||
return (element.full_name.size() != 3) || (element.full_name[0] != start_element.full_name[0])
|
||||
|| (element.full_name[1] != start_element.full_name[1]) || (element.grant_option != start_element.grant_option)
|
||||
|| (element.kind != start_element.kind);
|
||||
});
|
||||
|
||||
return it - (begin() + start);
|
||||
}
|
||||
|
||||
/// Collects columns together to write multiple columns into one AccessRightsElement.
|
||||
/// That procedure allows to output access rights in more compact way,
|
||||
/// e.g. "SELECT(x, y)" instead of "SELECT(x), SELECT(y)".
|
||||
void appendResultWithElementsWithDifferenceInColumnOnly(size_t start, size_t count, AccessRightsElementsWithOptions & res) const
|
||||
{
|
||||
const auto * pbegin = data() + start;
|
||||
const auto * pend = pbegin + count;
|
||||
AccessFlags handled_flags;
|
||||
|
||||
while (pbegin < pend)
|
||||
{
|
||||
while (pbegin < pend && !(pbegin->access_flags - handled_flags))
|
||||
++pbegin;
|
||||
|
||||
while (pbegin < pend && !((pend - 1)->access_flags - handled_flags))
|
||||
--pend;
|
||||
|
||||
if (pbegin >= pend)
|
||||
break;
|
||||
|
||||
AccessFlags common_flags = (pbegin->access_flags - handled_flags);
|
||||
for (const auto * element = pbegin + 1; element != pend; ++element)
|
||||
{
|
||||
if (auto new_common_flags = (element->access_flags - handled_flags) & common_flags)
|
||||
common_flags = new_common_flags;
|
||||
}
|
||||
|
||||
res.emplace_back();
|
||||
auto & back = res.back();
|
||||
back.grant_option = pbegin->grant_option;
|
||||
back.kind = pbegin->kind;
|
||||
back.any_database = false;
|
||||
back.database = pbegin->full_name[0];
|
||||
back.any_table = false;
|
||||
back.table = pbegin->full_name[1];
|
||||
back.any_column = false;
|
||||
back.access_flags = common_flags;
|
||||
for (const auto * element = pbegin; element != pend; ++element)
|
||||
{
|
||||
if (((element->access_flags - handled_flags) & common_flags) == common_flags)
|
||||
back.columns.emplace_back(element->full_name[2]);
|
||||
}
|
||||
|
||||
handled_flags |= common_flags;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -249,17 +432,32 @@ public:
|
||||
calculateFinalAccessRec(helper);
|
||||
}
|
||||
|
||||
void logTree(Poco::Logger * log) const
|
||||
|
||||
ProtoElements getElements() const
|
||||
{
|
||||
LOG_TRACE(log, "Tree({}): name={}, access={}, final_access={}, min_access={}, max_access={}, num_children={}",
|
||||
level, node_name ? *node_name : "NULL", access.toString(),
|
||||
ProtoElements res;
|
||||
getElementsRec(res, {}, *this, {});
|
||||
return res;
|
||||
}
|
||||
|
||||
static ProtoElements getElements(const Node * node, const Node * node_with_grant_option)
|
||||
{
|
||||
ProtoElements res;
|
||||
getElementsRec(res, {}, node, {}, node_with_grant_option, {});
|
||||
return res;
|
||||
}
|
||||
|
||||
void logTree(Poco::Logger * log, const String & title) const
|
||||
{
|
||||
LOG_TRACE(log, "Tree({}): level={}, name={}, access={}, final_access={}, min_access={}, max_access={}, num_children={}",
|
||||
title, level, node_name ? *node_name : "NULL", access.toString(),
|
||||
final_access.toString(), min_access.toString(), max_access.toString(),
|
||||
(children ? children->size() : 0));
|
||||
|
||||
if (children)
|
||||
{
|
||||
for (auto & child : *children | boost::adaptors::map_values)
|
||||
child.logTree(log);
|
||||
child.logTree(log, title);
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,6 +540,93 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
static void getElementsRec(
|
||||
ProtoElements & res,
|
||||
const boost::container::small_vector<std::string_view, 3> & full_name,
|
||||
const Node & node,
|
||||
const AccessFlags & parent_access)
|
||||
{
|
||||
auto access = node.access;
|
||||
auto revokes = parent_access - access;
|
||||
auto grants = access - parent_access;
|
||||
|
||||
if (revokes)
|
||||
res.push_back(ProtoElement{revokes, full_name, false, Kind::REVOKE});
|
||||
|
||||
if (grants)
|
||||
res.push_back(ProtoElement{grants, full_name, false, Kind::GRANT});
|
||||
|
||||
if (node.children)
|
||||
{
|
||||
for (const auto & [child_name, child] : *node.children)
|
||||
{
|
||||
boost::container::small_vector<std::string_view, 3> child_full_name = full_name;
|
||||
child_full_name.push_back(child_name);
|
||||
getElementsRec(res, child_full_name, child, access);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void getElementsRec(
|
||||
ProtoElements & res,
|
||||
const boost::container::small_vector<std::string_view, 3> & full_name,
|
||||
const Node * node,
|
||||
const AccessFlags & parent_access,
|
||||
const Node * node_go,
|
||||
const AccessFlags & parent_access_go)
|
||||
{
|
||||
auto access = node ? node->access : parent_access;
|
||||
auto access_go = node_go ? node_go->access : parent_access_go;
|
||||
auto revokes = parent_access - access;
|
||||
auto revokes_go = parent_access_go - access_go - revokes;
|
||||
auto grants_go = access_go - parent_access_go;
|
||||
auto grants = access - parent_access - grants_go;
|
||||
|
||||
if (revokes)
|
||||
res.push_back(ProtoElement{revokes, full_name, false, Kind::REVOKE});
|
||||
|
||||
if (revokes_go)
|
||||
res.push_back(ProtoElement{revokes_go, full_name, true, Kind::REVOKE});
|
||||
|
||||
if (grants)
|
||||
res.push_back(ProtoElement{grants, full_name, false, Kind::GRANT});
|
||||
|
||||
if (grants_go)
|
||||
res.push_back(ProtoElement{grants_go, full_name, true, Kind::GRANT});
|
||||
|
||||
if (node && node->children)
|
||||
{
|
||||
for (const auto & [child_name, child] : *node->children)
|
||||
{
|
||||
boost::container::small_vector<std::string_view, 3> child_full_name = full_name;
|
||||
child_full_name.push_back(child_name);
|
||||
const Node * child_node = &child;
|
||||
const Node * child_node_go = nullptr;
|
||||
if (node_go && node_go->children)
|
||||
{
|
||||
auto it = node_go->children->find(child_name);
|
||||
if (it != node_go->children->end())
|
||||
child_node_go = &it->second;
|
||||
}
|
||||
getElementsRec(res, child_full_name, child_node, access, child_node_go, access_go);
|
||||
}
|
||||
|
||||
}
|
||||
if (node_go && node_go->children)
|
||||
{
|
||||
for (const auto & [child_name, child] : *node_go->children)
|
||||
{
|
||||
if (node && node->children && node->children->count(child_name))
|
||||
continue; /// already processed
|
||||
boost::container::small_vector<std::string_view, 3> child_full_name = full_name;
|
||||
child_full_name.push_back(child_name);
|
||||
const Node * child_node = nullptr;
|
||||
const Node * child_node_go = &child;
|
||||
getElementsRec(res, child_full_name, child_node, access, child_node_go, access_go);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void calculateFinalAccessRec(const Helper & helper)
|
||||
{
|
||||
/// Traverse tree.
|
||||
@ -476,6 +761,10 @@ AccessRights & AccessRights::operator =(const AccessRights & src)
|
||||
root = std::make_unique<Node>(*src.root);
|
||||
else
|
||||
root = nullptr;
|
||||
if (src.root_with_grant_option)
|
||||
root_with_grant_option = std::make_unique<Node>(*src.root_with_grant_option);
|
||||
else
|
||||
root_with_grant_option = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -488,302 +777,245 @@ AccessRights::AccessRights(const AccessFlags & access)
|
||||
|
||||
bool AccessRights::isEmpty() const
|
||||
{
|
||||
return !root;
|
||||
return !root && !root_with_grant_option;
|
||||
}
|
||||
|
||||
|
||||
void AccessRights::clear()
|
||||
{
|
||||
root = nullptr;
|
||||
root_with_grant_option = nullptr;
|
||||
}
|
||||
|
||||
|
||||
template <typename... Args>
|
||||
template <bool with_grant_option, typename... Args>
|
||||
void AccessRights::grantImpl(const AccessFlags & flags, const Args &... args)
|
||||
{
|
||||
if (!root)
|
||||
root = std::make_unique<Node>();
|
||||
root->grant(flags, Helper::instance(), args...);
|
||||
if (!root->access && !root->children)
|
||||
root = nullptr;
|
||||
auto helper = [&](std::unique_ptr<Node> & root_node)
|
||||
{
|
||||
if (!root_node)
|
||||
root_node = std::make_unique<Node>();
|
||||
root_node->grant(flags, Helper::instance(), args...);
|
||||
if (!root_node->access && !root_node->children)
|
||||
root_node = nullptr;
|
||||
};
|
||||
helper(root);
|
||||
|
||||
if constexpr (with_grant_option)
|
||||
helper(root_with_grant_option);
|
||||
}
|
||||
|
||||
void AccessRights::grant(const AccessFlags & flags) { grantImpl(flags); }
|
||||
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database) { grantImpl(flags, database); }
|
||||
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) { grantImpl(flags, database, table); }
|
||||
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) { grantImpl(flags, database, table, column); }
|
||||
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { grantImpl(flags, database, table, columns); }
|
||||
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) { grantImpl(flags, database, table, columns); }
|
||||
|
||||
void AccessRights::grant(const AccessRightsElement & element, std::string_view current_database)
|
||||
template <bool with_grant_option>
|
||||
void AccessRights::grantImpl(const AccessRightsElement & element)
|
||||
{
|
||||
if (element.any_database)
|
||||
{
|
||||
grant(element.access_flags);
|
||||
}
|
||||
grantImpl<with_grant_option>(element.access_flags);
|
||||
else if (element.any_table)
|
||||
{
|
||||
if (element.database.empty())
|
||||
grant(element.access_flags, checkCurrentDatabase(current_database));
|
||||
else
|
||||
grant(element.access_flags, element.database);
|
||||
}
|
||||
grantImpl<with_grant_option>(element.access_flags, element.database);
|
||||
else if (element.any_column)
|
||||
{
|
||||
if (element.database.empty())
|
||||
grant(element.access_flags, checkCurrentDatabase(current_database), element.table);
|
||||
else
|
||||
grant(element.access_flags, element.database, element.table);
|
||||
}
|
||||
grantImpl<with_grant_option>(element.access_flags, element.database, element.table);
|
||||
else
|
||||
{
|
||||
if (element.database.empty())
|
||||
grant(element.access_flags, checkCurrentDatabase(current_database), element.table, element.columns);
|
||||
else
|
||||
grant(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
grantImpl<with_grant_option>(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
|
||||
void AccessRights::grant(const AccessRightsElements & elements, std::string_view current_database)
|
||||
template <bool with_grant_option>
|
||||
void AccessRights::grantImpl(const AccessRightsElements & elements)
|
||||
{
|
||||
for (const auto & element : elements)
|
||||
grant(element, current_database);
|
||||
grantImpl<with_grant_option>(element);
|
||||
}
|
||||
|
||||
void AccessRights::grant(const AccessFlags & flags) { grantImpl<false>(flags); }
|
||||
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database) { grantImpl<false>(flags, database); }
|
||||
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) { grantImpl<false>(flags, database, table); }
|
||||
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) { grantImpl<false>(flags, database, table, column); }
|
||||
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { grantImpl<false>(flags, database, table, columns); }
|
||||
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) { grantImpl<false>(flags, database, table, columns); }
|
||||
void AccessRights::grant(const AccessRightsElement & element) { grantImpl<false>(element); }
|
||||
void AccessRights::grant(const AccessRightsElements & elements) { grantImpl<false>(elements); }
|
||||
|
||||
template <typename... Args>
|
||||
void AccessRights::grantWithGrantOption(const AccessFlags & flags) { grantImpl<true>(flags); }
|
||||
void AccessRights::grantWithGrantOption(const AccessFlags & flags, const std::string_view & database) { grantImpl<true>(flags, database); }
|
||||
void AccessRights::grantWithGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) { grantImpl<true>(flags, database, table); }
|
||||
void AccessRights::grantWithGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) { grantImpl<true>(flags, database, table, column); }
|
||||
void AccessRights::grantWithGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { grantImpl<true>(flags, database, table, columns); }
|
||||
void AccessRights::grantWithGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) { grantImpl<true>(flags, database, table, columns); }
|
||||
void AccessRights::grantWithGrantOption(const AccessRightsElement & element) { grantImpl<true>(element); }
|
||||
void AccessRights::grantWithGrantOption(const AccessRightsElements & elements) { grantImpl<true>(elements); }
|
||||
|
||||
|
||||
template <bool grant_option, typename... Args>
|
||||
void AccessRights::revokeImpl(const AccessFlags & flags, const Args &... args)
|
||||
{
|
||||
if (!root)
|
||||
return;
|
||||
root->revoke(flags, Helper::instance(), args...);
|
||||
if (!root->access && !root->children)
|
||||
root = nullptr;
|
||||
auto helper = [&](std::unique_ptr<Node> & root_node)
|
||||
{
|
||||
if (!root_node)
|
||||
return;
|
||||
root_node->revoke(flags, Helper::instance(), args...);
|
||||
if (!root_node->access && !root_node->children)
|
||||
root_node = nullptr;
|
||||
};
|
||||
helper(root_with_grant_option);
|
||||
|
||||
if constexpr (!grant_option)
|
||||
helper(root);
|
||||
}
|
||||
|
||||
void AccessRights::revoke(const AccessFlags & flags) { revokeImpl(flags); }
|
||||
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database) { revokeImpl(flags, database); }
|
||||
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) { revokeImpl(flags, database, table); }
|
||||
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) { revokeImpl(flags, database, table, column); }
|
||||
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { revokeImpl(flags, database, table, columns); }
|
||||
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) { revokeImpl(flags, database, table, columns); }
|
||||
|
||||
|
||||
void AccessRights::revoke(const AccessRightsElement & element, std::string_view current_database)
|
||||
template <bool grant_option>
|
||||
void AccessRights::revokeImpl(const AccessRightsElement & element)
|
||||
{
|
||||
if (element.any_database)
|
||||
{
|
||||
revoke(element.access_flags);
|
||||
}
|
||||
revokeImpl<grant_option>(element.access_flags);
|
||||
else if (element.any_table)
|
||||
{
|
||||
if (element.database.empty())
|
||||
revoke(element.access_flags, checkCurrentDatabase(current_database));
|
||||
else
|
||||
revoke(element.access_flags, element.database);
|
||||
}
|
||||
revokeImpl<grant_option>(element.access_flags, element.database);
|
||||
else if (element.any_column)
|
||||
{
|
||||
if (element.database.empty())
|
||||
revoke(element.access_flags, checkCurrentDatabase(current_database), element.table);
|
||||
else
|
||||
revoke(element.access_flags, element.database, element.table);
|
||||
}
|
||||
revokeImpl<grant_option>(element.access_flags, element.database, element.table);
|
||||
else
|
||||
{
|
||||
if (element.database.empty())
|
||||
revoke(element.access_flags, checkCurrentDatabase(current_database), element.table, element.columns);
|
||||
else
|
||||
revoke(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
revokeImpl<grant_option>(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
|
||||
void AccessRights::revoke(const AccessRightsElements & elements, std::string_view current_database)
|
||||
template <bool grant_option>
|
||||
void AccessRights::revokeImpl(const AccessRightsElements & elements)
|
||||
{
|
||||
for (const auto & element : elements)
|
||||
revoke(element, current_database);
|
||||
revokeImpl<grant_option>(element);
|
||||
}
|
||||
|
||||
void AccessRights::revoke(const AccessFlags & flags) { revokeImpl<false>(flags); }
|
||||
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database) { revokeImpl<false>(flags, database); }
|
||||
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) { revokeImpl<false>(flags, database, table); }
|
||||
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) { revokeImpl<false>(flags, database, table, column); }
|
||||
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { revokeImpl<false>(flags, database, table, columns); }
|
||||
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) { revokeImpl<false>(flags, database, table, columns); }
|
||||
void AccessRights::revoke(const AccessRightsElement & element) { revokeImpl<false>(element); }
|
||||
void AccessRights::revoke(const AccessRightsElements & elements) { revokeImpl<false>(elements); }
|
||||
|
||||
AccessRightsElements AccessRights::getGrants() const
|
||||
{
|
||||
AccessRightsElements grants;
|
||||
getGrantsAndPartialRevokesImpl(&grants, nullptr);
|
||||
return grants;
|
||||
}
|
||||
|
||||
AccessRightsElements AccessRights::getPartialRevokes() const
|
||||
{
|
||||
AccessRightsElements partial_revokes;
|
||||
getGrantsAndPartialRevokesImpl(nullptr, &partial_revokes);
|
||||
return partial_revokes;
|
||||
}
|
||||
|
||||
AccessRights::GrantsAndPartialRevokes AccessRights::getGrantsAndPartialRevokes() const
|
||||
{
|
||||
GrantsAndPartialRevokes res;
|
||||
getGrantsAndPartialRevokesImpl(&res.grants, &res.revokes);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void AccessRights::getGrantsAndPartialRevokesImpl(AccessRightsElements * out_grants, AccessRightsElements * out_partial_revokes) const
|
||||
void AccessRights::revokeGrantOption(const AccessFlags & flags) { revokeImpl<true>(flags); }
|
||||
void AccessRights::revokeGrantOption(const AccessFlags & flags, const std::string_view & database) { revokeImpl<true>(flags, database); }
|
||||
void AccessRights::revokeGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) { revokeImpl<true>(flags, database, table); }
|
||||
void AccessRights::revokeGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) { revokeImpl<true>(flags, database, table, column); }
|
||||
void AccessRights::revokeGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { revokeImpl<true>(flags, database, table, columns); }
|
||||
void AccessRights::revokeGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) { revokeImpl<true>(flags, database, table, columns); }
|
||||
void AccessRights::revokeGrantOption(const AccessRightsElement & element) { revokeImpl<true>(element); }
|
||||
void AccessRights::revokeGrantOption(const AccessRightsElements & elements) { revokeImpl<true>(elements); }
|
||||
|
||||
|
||||
AccessRightsElementsWithOptions AccessRights::getElements() const
|
||||
{
|
||||
#if 0
|
||||
logTree();
|
||||
#endif
|
||||
if (!root)
|
||||
return;
|
||||
auto global_access = root->access;
|
||||
if (out_grants && global_access)
|
||||
out_grants->push_back({global_access});
|
||||
if (root->children)
|
||||
{
|
||||
for (const auto & [db_name, db_node] : *root->children)
|
||||
{
|
||||
if (out_grants)
|
||||
{
|
||||
if (auto db_grants = db_node.access - global_access)
|
||||
out_grants->push_back({db_grants, db_name});
|
||||
}
|
||||
if (out_partial_revokes)
|
||||
{
|
||||
if (auto db_partial_revokes = global_access - db_node.access)
|
||||
out_partial_revokes->push_back({db_partial_revokes, db_name});
|
||||
}
|
||||
if (db_node.children)
|
||||
{
|
||||
for (const auto & [table_name, table_node] : *db_node.children)
|
||||
{
|
||||
if (out_grants)
|
||||
{
|
||||
if (auto table_grants = table_node.access - db_node.access)
|
||||
out_grants->push_back({table_grants, db_name, table_name});
|
||||
}
|
||||
if (out_partial_revokes)
|
||||
{
|
||||
if (auto table_partial_revokes = db_node.access - table_node.access)
|
||||
out_partial_revokes->push_back({table_partial_revokes, db_name, table_name});
|
||||
}
|
||||
if (table_node.children)
|
||||
{
|
||||
for (const auto & [column_name, column_node] : *table_node.children)
|
||||
{
|
||||
if (out_grants)
|
||||
{
|
||||
if (auto column_grants = column_node.access - table_node.access)
|
||||
out_grants->push_back({column_grants, db_name, table_name, column_name});
|
||||
}
|
||||
if (out_partial_revokes)
|
||||
{
|
||||
if (auto column_partial_revokes = table_node.access - column_node.access)
|
||||
out_partial_revokes->push_back({column_partial_revokes, db_name, table_name, column_name});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
if (!root_with_grant_option)
|
||||
return root->getElements().getResult();
|
||||
return Node::getElements(root.get(), root_with_grant_option.get()).getResult();
|
||||
}
|
||||
|
||||
|
||||
String AccessRights::toString() const
|
||||
{
|
||||
String res;
|
||||
auto gr = getGrantsAndPartialRevokes();
|
||||
if (!gr.grants.empty())
|
||||
{
|
||||
res += "GRANT ";
|
||||
res += gr.grants.toString();
|
||||
}
|
||||
if (!gr.revokes.empty())
|
||||
{
|
||||
if (!res.empty())
|
||||
res += ", ";
|
||||
res += "REVOKE ";
|
||||
res += gr.revokes.toString();
|
||||
}
|
||||
if (res.empty())
|
||||
res = "GRANT USAGE ON *.*";
|
||||
return res;
|
||||
return getElements().toString();
|
||||
}
|
||||
|
||||
|
||||
template <typename... Args>
|
||||
template <bool grant_option, typename... Args>
|
||||
bool AccessRights::isGrantedImpl(const AccessFlags & flags, const Args &... args) const
|
||||
{
|
||||
if (!root)
|
||||
return flags.isEmpty();
|
||||
return root->isGranted(flags, args...);
|
||||
auto helper = [&](const std::unique_ptr<Node> & root_node) -> bool
|
||||
{
|
||||
if (!root_node)
|
||||
return flags.isEmpty();
|
||||
return root_node->isGranted(flags, args...);
|
||||
};
|
||||
if constexpr (grant_option)
|
||||
return helper(root_with_grant_option);
|
||||
else
|
||||
return helper(root);
|
||||
}
|
||||
|
||||
bool AccessRights::isGranted(const AccessFlags & flags) const { return isGrantedImpl(flags); }
|
||||
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database) const { return isGrantedImpl(flags, database); }
|
||||
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const { return isGrantedImpl(flags, database, table); }
|
||||
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return isGrantedImpl(flags, database, table, column); }
|
||||
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return isGrantedImpl(flags, database, table, columns); }
|
||||
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return isGrantedImpl(flags, database, table, columns); }
|
||||
|
||||
bool AccessRights::isGranted(const AccessRightsElement & element, std::string_view current_database) const
|
||||
template <bool grant_option>
|
||||
bool AccessRights::isGrantedImpl(const AccessRightsElement & element) const
|
||||
{
|
||||
if (element.any_database)
|
||||
{
|
||||
return isGranted(element.access_flags);
|
||||
}
|
||||
return isGrantedImpl<grant_option>(element.access_flags);
|
||||
else if (element.any_table)
|
||||
{
|
||||
if (element.database.empty())
|
||||
return isGranted(element.access_flags, checkCurrentDatabase(current_database));
|
||||
else
|
||||
return isGranted(element.access_flags, element.database);
|
||||
}
|
||||
return isGrantedImpl<grant_option>(element.access_flags, element.database);
|
||||
else if (element.any_column)
|
||||
{
|
||||
if (element.database.empty())
|
||||
return isGranted(element.access_flags, checkCurrentDatabase(current_database), element.table);
|
||||
else
|
||||
return isGranted(element.access_flags, element.database, element.table);
|
||||
}
|
||||
return isGrantedImpl<grant_option>(element.access_flags, element.database, element.table);
|
||||
else
|
||||
{
|
||||
if (element.database.empty())
|
||||
return isGranted(element.access_flags, checkCurrentDatabase(current_database), element.table, element.columns);
|
||||
else
|
||||
return isGranted(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
return isGrantedImpl<grant_option>(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
|
||||
bool AccessRights::isGranted(const AccessRightsElements & elements, std::string_view current_database) const
|
||||
template <bool grant_option>
|
||||
bool AccessRights::isGrantedImpl(const AccessRightsElements & elements) const
|
||||
{
|
||||
for (const auto & element : elements)
|
||||
if (!isGranted(element, current_database))
|
||||
if (!isGrantedImpl<grant_option>(element))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AccessRights::isGranted(const AccessFlags & flags) const { return isGrantedImpl<false>(flags); }
|
||||
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database) const { return isGrantedImpl<false>(flags, database); }
|
||||
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const { return isGrantedImpl<false>(flags, database, table); }
|
||||
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return isGrantedImpl<false>(flags, database, table, column); }
|
||||
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return isGrantedImpl<false>(flags, database, table, columns); }
|
||||
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return isGrantedImpl<false>(flags, database, table, columns); }
|
||||
bool AccessRights::isGranted(const AccessRightsElement & element) const { return isGrantedImpl<false>(element); }
|
||||
bool AccessRights::isGranted(const AccessRightsElements & elements) const { return isGrantedImpl<false>(elements); }
|
||||
|
||||
bool AccessRights::hasGrantOption(const AccessFlags & flags) const { return isGrantedImpl<true>(flags); }
|
||||
bool AccessRights::hasGrantOption(const AccessFlags & flags, const std::string_view & database) const { return isGrantedImpl<true>(flags, database); }
|
||||
bool AccessRights::hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const { return isGrantedImpl<true>(flags, database, table); }
|
||||
bool AccessRights::hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return isGrantedImpl<true>(flags, database, table, column); }
|
||||
bool AccessRights::hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return isGrantedImpl<true>(flags, database, table, columns); }
|
||||
bool AccessRights::hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return isGrantedImpl<true>(flags, database, table, columns); }
|
||||
bool AccessRights::hasGrantOption(const AccessRightsElement & element) const { return isGrantedImpl<true>(element); }
|
||||
bool AccessRights::hasGrantOption(const AccessRightsElements & elements) const { return isGrantedImpl<true>(elements); }
|
||||
|
||||
|
||||
bool operator ==(const AccessRights & left, const AccessRights & right)
|
||||
{
|
||||
if (!left.root)
|
||||
return !right.root;
|
||||
if (!right.root)
|
||||
return false;
|
||||
return *left.root == *right.root;
|
||||
auto helper = [](const std::unique_ptr<AccessRights::Node> & left_node, const std::unique_ptr<AccessRights::Node> & right_node)
|
||||
{
|
||||
if (!left_node)
|
||||
return !right_node;
|
||||
if (!right_node)
|
||||
return false;
|
||||
return *left_node == *right_node;
|
||||
};
|
||||
return helper(left.root, right.root) && helper(left.root_with_grant_option, right.root_with_grant_option);
|
||||
}
|
||||
|
||||
|
||||
void AccessRights::merge(const AccessRights & other)
|
||||
{
|
||||
if (!root)
|
||||
auto helper = [](std::unique_ptr<Node> & root_node, const std::unique_ptr<Node> & other_root_node)
|
||||
{
|
||||
*this = other;
|
||||
return;
|
||||
}
|
||||
if (other.root)
|
||||
{
|
||||
root->merge(*other.root, Helper::instance());
|
||||
if (!root->access && !root->children)
|
||||
root = nullptr;
|
||||
}
|
||||
if (!root_node)
|
||||
{
|
||||
if (other_root_node)
|
||||
root_node = std::make_unique<Node>(*other_root_node);
|
||||
return;
|
||||
}
|
||||
if (other_root_node)
|
||||
{
|
||||
root_node->merge(*other_root_node, Helper::instance());
|
||||
if (!root_node->access && !root_node->children)
|
||||
root_node = nullptr;
|
||||
}
|
||||
};
|
||||
helper(root, other.root);
|
||||
helper(root_with_grant_option, other.root_with_grant_option);
|
||||
}
|
||||
|
||||
|
||||
AccessRights AccessRights::getFullAccess()
|
||||
{
|
||||
AccessRights res;
|
||||
res.grantWithGrantOption(AccessType::ALL);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -791,7 +1023,11 @@ void AccessRights::logTree() const
|
||||
{
|
||||
auto * log = &Poco::Logger::get("AccessRights");
|
||||
if (root)
|
||||
root->logTree(log);
|
||||
{
|
||||
root->logTree(log, "");
|
||||
if (root_with_grant_option)
|
||||
root->logTree(log, "go");
|
||||
}
|
||||
else
|
||||
LOG_TRACE(log, "Tree: NULL");
|
||||
}
|
||||
|
@ -26,6 +26,12 @@ public:
|
||||
/// Revokes everything. It's the same as revoke(AccessType::ALL).
|
||||
void clear();
|
||||
|
||||
/// Returns the information about all the access granted as a string.
|
||||
String toString() const;
|
||||
|
||||
/// Returns the information about all the access granted.
|
||||
AccessRightsElementsWithOptions getElements() const;
|
||||
|
||||
/// Grants access on a specified database/table/column.
|
||||
/// Does nothing if the specified access has been already granted.
|
||||
void grant(const AccessFlags & flags);
|
||||
@ -34,8 +40,17 @@ public:
|
||||
void grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column);
|
||||
void grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns);
|
||||
void grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns);
|
||||
void grant(const AccessRightsElement & element, std::string_view current_database = {});
|
||||
void grant(const AccessRightsElements & elements, std::string_view current_database = {});
|
||||
void grant(const AccessRightsElement & element);
|
||||
void grant(const AccessRightsElements & elements);
|
||||
|
||||
void grantWithGrantOption(const AccessFlags & flags);
|
||||
void grantWithGrantOption(const AccessFlags & flags, const std::string_view & database);
|
||||
void grantWithGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table);
|
||||
void grantWithGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column);
|
||||
void grantWithGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns);
|
||||
void grantWithGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns);
|
||||
void grantWithGrantOption(const AccessRightsElement & element);
|
||||
void grantWithGrantOption(const AccessRightsElements & elements);
|
||||
|
||||
/// Revokes a specified access granted earlier on a specified database/table/column.
|
||||
/// For example, revoke(AccessType::ALL) revokes all grants at all, just like clear();
|
||||
@ -45,21 +60,17 @@ public:
|
||||
void revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column);
|
||||
void revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns);
|
||||
void revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns);
|
||||
void revoke(const AccessRightsElement & element, std::string_view current_database = {});
|
||||
void revoke(const AccessRightsElements & elements, std::string_view current_database = {});
|
||||
void revoke(const AccessRightsElement & element);
|
||||
void revoke(const AccessRightsElements & elements);
|
||||
|
||||
/// Returns the information about all the access granted.
|
||||
struct GrantsAndPartialRevokes
|
||||
{
|
||||
AccessRightsElements grants;
|
||||
AccessRightsElements revokes;
|
||||
};
|
||||
AccessRightsElements getGrants() const;
|
||||
AccessRightsElements getPartialRevokes() const;
|
||||
GrantsAndPartialRevokes getGrantsAndPartialRevokes() const;
|
||||
|
||||
/// Returns the information about all the access granted as a string.
|
||||
String toString() const;
|
||||
void revokeGrantOption(const AccessFlags & flags);
|
||||
void revokeGrantOption(const AccessFlags & flags, const std::string_view & database);
|
||||
void revokeGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table);
|
||||
void revokeGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column);
|
||||
void revokeGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns);
|
||||
void revokeGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns);
|
||||
void revokeGrantOption(const AccessRightsElement & element);
|
||||
void revokeGrantOption(const AccessRightsElements & elements);
|
||||
|
||||
/// Whether a specified access granted.
|
||||
bool isGranted(const AccessFlags & flags) const;
|
||||
@ -68,38 +79,60 @@ public:
|
||||
bool isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
|
||||
bool isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
|
||||
bool isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
|
||||
bool isGranted(const AccessRightsElement & element, std::string_view current_database = {}) const;
|
||||
bool isGranted(const AccessRightsElements & elements, std::string_view current_database = {}) const;
|
||||
bool isGranted(const AccessRightsElement & element) const;
|
||||
bool isGranted(const AccessRightsElements & elements) const;
|
||||
|
||||
friend bool operator ==(const AccessRights & left, const AccessRights & right);
|
||||
friend bool operator !=(const AccessRights & left, const AccessRights & right) { return !(left == right); }
|
||||
bool hasGrantOption(const AccessFlags & flags) const;
|
||||
bool hasGrantOption(const AccessFlags & flags, const std::string_view & database) const;
|
||||
bool hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const;
|
||||
bool hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
|
||||
bool hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
|
||||
bool hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
|
||||
bool hasGrantOption(const AccessRightsElement & element) const;
|
||||
bool hasGrantOption(const AccessRightsElements & elements) const;
|
||||
|
||||
/// Merges two sets of access rights together.
|
||||
/// It's used to combine access rights from multiple roles.
|
||||
void merge(const AccessRights & other);
|
||||
|
||||
friend bool operator ==(const AccessRights & left, const AccessRights & right);
|
||||
friend bool operator !=(const AccessRights & left, const AccessRights & right) { return !(left == right); }
|
||||
|
||||
static AccessRights getFullAccess();
|
||||
|
||||
private:
|
||||
template <typename... Args>
|
||||
template <bool with_grant_option, typename... Args>
|
||||
void grantImpl(const AccessFlags & flags, const Args &... args);
|
||||
|
||||
template <typename... Args>
|
||||
template <bool with_grant_options>
|
||||
void grantImpl(const AccessRightsElement & element);
|
||||
|
||||
template <bool with_grant_options>
|
||||
void grantImpl(const AccessRightsElements & elements);
|
||||
|
||||
template <bool grant_option, typename... Args>
|
||||
void revokeImpl(const AccessFlags & flags, const Args &... args);
|
||||
|
||||
template <typename... Args>
|
||||
template <bool grant_option>
|
||||
void revokeImpl(const AccessRightsElement & element);
|
||||
|
||||
template <bool grant_option>
|
||||
void revokeImpl(const AccessRightsElements & elements);
|
||||
|
||||
template <bool grant_option, typename... Args>
|
||||
bool isGrantedImpl(const AccessFlags & flags, const Args &... args) const;
|
||||
|
||||
bool isGrantedImpl(const AccessRightsElement & element, std::string_view current_database) const;
|
||||
bool isGrantedImpl(const AccessRightsElements & elements, std::string_view current_database) const;
|
||||
template <bool grant_option>
|
||||
bool isGrantedImpl(const AccessRightsElement & element) const;
|
||||
|
||||
template <typename... Args>
|
||||
AccessFlags getAccessImpl(const Args &... args) const;
|
||||
|
||||
void getGrantsAndPartialRevokesImpl(AccessRightsElements * grants, AccessRightsElements * partial_revokes) const;
|
||||
template <bool grant_option>
|
||||
bool isGrantedImpl(const AccessRightsElements & elements) const;
|
||||
|
||||
void logTree() const;
|
||||
|
||||
struct Node;
|
||||
std::unique_ptr<Node> root;
|
||||
std::unique_ptr<Node> root_with_grant_option;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -12,222 +12,158 @@ namespace DB
|
||||
{
|
||||
namespace
|
||||
{
|
||||
size_t groupElements(AccessRightsElements & elements, size_t start)
|
||||
using Kind = AccessRightsElementWithOptions::Kind;
|
||||
|
||||
String formatOptions(bool grant_option, Kind kind, const String & inner_part)
|
||||
{
|
||||
auto & start_element = elements[start];
|
||||
auto it = std::find_if(elements.begin() + start + 1, elements.end(),
|
||||
[&](const AccessRightsElement & element)
|
||||
if (kind == Kind::REVOKE)
|
||||
{
|
||||
return (element.database != start_element.database) ||
|
||||
(element.any_database != start_element.any_database) ||
|
||||
(element.table != start_element.table) ||
|
||||
(element.any_table != start_element.any_table) ||
|
||||
(element.any_column != start_element.any_column);
|
||||
});
|
||||
size_t end = it - elements.begin();
|
||||
|
||||
/// All the elements at indices from start to end here specify
|
||||
/// the same database and table.
|
||||
|
||||
if (start_element.any_column)
|
||||
{
|
||||
/// Easy case: the elements don't specify columns.
|
||||
/// All we need is to combine the access flags.
|
||||
for (size_t i = start + 1; i != end; ++i)
|
||||
{
|
||||
start_element.access_flags |= elements[i].access_flags;
|
||||
elements[i].access_flags = {};
|
||||
}
|
||||
return end;
|
||||
if (grant_option)
|
||||
return "REVOKE GRANT OPTION " + inner_part;
|
||||
else
|
||||
return "REVOKE " + inner_part;
|
||||
}
|
||||
|
||||
/// Difficult case: the elements specify columns.
|
||||
/// We have to find groups of columns with common access flags.
|
||||
for (size_t i = start; i != end; ++i)
|
||||
else
|
||||
{
|
||||
if (!elements[i].access_flags)
|
||||
continue;
|
||||
|
||||
AccessFlags common_flags = elements[i].access_flags;
|
||||
size_t num_elements_with_common_flags = 1;
|
||||
for (size_t j = i + 1; j != end; ++j)
|
||||
{
|
||||
auto new_common_flags = common_flags & elements[j].access_flags;
|
||||
if (new_common_flags)
|
||||
{
|
||||
common_flags = new_common_flags;
|
||||
++num_elements_with_common_flags;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_elements_with_common_flags == 1)
|
||||
continue;
|
||||
|
||||
if (elements[i].access_flags != common_flags)
|
||||
{
|
||||
elements.insert(elements.begin() + i + 1, elements[i]);
|
||||
elements[i].access_flags = common_flags;
|
||||
elements[i].columns.clear();
|
||||
++end;
|
||||
}
|
||||
|
||||
for (size_t j = i + 1; j != end; ++j)
|
||||
{
|
||||
if ((elements[j].access_flags & common_flags) == common_flags)
|
||||
{
|
||||
boost::range::push_back(elements[i].columns, elements[j].columns);
|
||||
elements[j].access_flags -= common_flags;
|
||||
}
|
||||
}
|
||||
if (grant_option)
|
||||
return "GRANT " + inner_part + " WITH GRANT OPTION";
|
||||
else
|
||||
return "GRANT " + inner_part;
|
||||
}
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
/// Tries to combine elements to decrease their number.
|
||||
void groupElements(AccessRightsElements & elements)
|
||||
|
||||
String formatONClause(const String & database, bool any_database, const String & table, bool any_table)
|
||||
{
|
||||
if (!boost::range::is_sorted(elements))
|
||||
boost::range::sort(elements); /// Algorithm in groupElement() requires elements to be sorted.
|
||||
for (size_t start = 0; start != elements.size();)
|
||||
start = groupElements(elements, start);
|
||||
String msg = "ON ";
|
||||
|
||||
if (any_database)
|
||||
msg += "*.";
|
||||
else if (!database.empty())
|
||||
msg += backQuoteIfNeed(database) + ".";
|
||||
|
||||
if (any_table)
|
||||
msg += "*";
|
||||
else
|
||||
msg += backQuoteIfNeed(table);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/// Removes unnecessary elements, sorts elements and makes them unique.
|
||||
void sortElementsAndMakeUnique(AccessRightsElements & elements)
|
||||
|
||||
String formatAccessFlagsWithColumns(const AccessFlags & access_flags, const Strings & columns, bool any_column)
|
||||
{
|
||||
/// Remove empty elements.
|
||||
boost::range::remove_erase_if(elements, [](const AccessRightsElement & element)
|
||||
String columns_in_parentheses;
|
||||
if (!any_column)
|
||||
{
|
||||
return !element.access_flags || (!element.any_column && element.columns.empty());
|
||||
});
|
||||
|
||||
/// Sort columns and make them unique.
|
||||
for (auto & element : elements)
|
||||
{
|
||||
if (element.any_column)
|
||||
continue;
|
||||
|
||||
if (!boost::range::is_sorted(element.columns))
|
||||
boost::range::sort(element.columns);
|
||||
element.columns.erase(std::unique(element.columns.begin(), element.columns.end()), element.columns.end());
|
||||
if (columns.empty())
|
||||
return "USAGE";
|
||||
for (const auto & column : columns)
|
||||
{
|
||||
columns_in_parentheses += columns_in_parentheses.empty() ? "(" : ", ";
|
||||
columns_in_parentheses += backQuoteIfNeed(column);
|
||||
}
|
||||
columns_in_parentheses += ")";
|
||||
}
|
||||
|
||||
/// Sort elements themselves.
|
||||
boost::range::sort(elements);
|
||||
elements.erase(std::unique(elements.begin(), elements.end()), elements.end());
|
||||
auto keywords = access_flags.toKeywords();
|
||||
if (keywords.empty())
|
||||
return "USAGE";
|
||||
|
||||
String msg;
|
||||
for (const std::string_view & keyword : keywords)
|
||||
{
|
||||
if (!msg.empty())
|
||||
msg += ", ";
|
||||
msg += String{keyword} + columns_in_parentheses;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
void AccessRightsElement::setDatabase(const String & new_database)
|
||||
{
|
||||
database = new_database;
|
||||
any_database = false;
|
||||
}
|
||||
|
||||
|
||||
void AccessRightsElement::replaceEmptyDatabase(const String & new_database)
|
||||
{
|
||||
if (isEmptyDatabase())
|
||||
setDatabase(new_database);
|
||||
}
|
||||
|
||||
|
||||
bool AccessRightsElement::isEmptyDatabase() const
|
||||
{
|
||||
return !any_database && database.empty();
|
||||
}
|
||||
|
||||
|
||||
String AccessRightsElement::toString() const
|
||||
{
|
||||
String msg = toStringWithoutON();
|
||||
msg += " ON ";
|
||||
|
||||
if (any_database)
|
||||
msg += "*.";
|
||||
else if (!database.empty())
|
||||
msg += backQuoteIfNeed(database) + ".";
|
||||
|
||||
if (any_table)
|
||||
msg += "*";
|
||||
else
|
||||
msg += backQuoteIfNeed(table);
|
||||
return msg;
|
||||
return formatAccessFlagsWithColumns(access_flags, columns, any_column) + " " + formatONClause(database, any_database, table, any_table);
|
||||
}
|
||||
|
||||
String AccessRightsElement::toStringWithoutON() const
|
||||
String AccessRightsElementWithOptions::toString() const
|
||||
{
|
||||
String columns_in_parentheses;
|
||||
if (!any_column)
|
||||
{
|
||||
if (columns.empty())
|
||||
return "USAGE";
|
||||
for (const auto & column : columns)
|
||||
{
|
||||
columns_in_parentheses += columns_in_parentheses.empty() ? "(" : ", ";
|
||||
columns_in_parentheses += backQuoteIfNeed(column);
|
||||
}
|
||||
columns_in_parentheses += ")";
|
||||
}
|
||||
|
||||
auto keywords = access_flags.toKeywords();
|
||||
if (keywords.empty())
|
||||
return "USAGE";
|
||||
|
||||
String msg;
|
||||
for (const std::string_view & keyword : keywords)
|
||||
{
|
||||
if (!msg.empty())
|
||||
msg += ", ";
|
||||
msg += String{keyword} + columns_in_parentheses;
|
||||
}
|
||||
return msg;
|
||||
return formatOptions(grant_option, kind, AccessRightsElement::toString());
|
||||
}
|
||||
|
||||
|
||||
void AccessRightsElements::replaceEmptyDatabase(const String & new_database)
|
||||
String AccessRightsElements::toString() const
|
||||
{
|
||||
for (auto & element : *this)
|
||||
element.replaceEmptyDatabase(new_database);
|
||||
}
|
||||
|
||||
|
||||
String AccessRightsElements::toString()
|
||||
{
|
||||
normalize();
|
||||
|
||||
if (empty())
|
||||
return "USAGE ON *.*";
|
||||
|
||||
String msg;
|
||||
bool need_comma = false;
|
||||
String res;
|
||||
String inner_part;
|
||||
|
||||
for (size_t i = 0; i != size(); ++i)
|
||||
{
|
||||
const auto & element = (*this)[i];
|
||||
if (std::exchange(need_comma, true))
|
||||
msg += ", ";
|
||||
bool next_element_on_same_db_and_table = false;
|
||||
|
||||
if (!inner_part.empty())
|
||||
inner_part += ", ";
|
||||
inner_part += formatAccessFlagsWithColumns(element.access_flags, element.columns, element.any_column);
|
||||
|
||||
bool next_element_uses_same_table = false;
|
||||
if (i != size() - 1)
|
||||
{
|
||||
const auto & next_element = (*this)[i + 1];
|
||||
if ((element.database == next_element.database) && (element.any_database == next_element.any_database)
|
||||
&& (element.table == next_element.table) && (element.any_table == next_element.any_table))
|
||||
next_element_on_same_db_and_table = true;
|
||||
if (element.sameDatabaseAndTable(next_element))
|
||||
next_element_uses_same_table = true;
|
||||
}
|
||||
|
||||
if (!next_element_uses_same_table)
|
||||
{
|
||||
if (!res.empty())
|
||||
res += ", ";
|
||||
res += inner_part + " " + formatONClause(element.database, element.any_database, element.table, element.any_table);
|
||||
inner_part.clear();
|
||||
}
|
||||
if (next_element_on_same_db_and_table)
|
||||
msg += element.toStringWithoutON();
|
||||
else
|
||||
msg += element.toString();
|
||||
}
|
||||
return msg;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void AccessRightsElements::normalize()
|
||||
String AccessRightsElementsWithOptions::toString() const
|
||||
{
|
||||
groupElements(*this);
|
||||
sortElementsAndMakeUnique(*this);
|
||||
if (empty())
|
||||
return "GRANT USAGE ON *.*";
|
||||
|
||||
String res;
|
||||
String inner_part;
|
||||
|
||||
for (size_t i = 0; i != size(); ++i)
|
||||
{
|
||||
const auto & element = (*this)[i];
|
||||
|
||||
if (!inner_part.empty())
|
||||
inner_part += ", ";
|
||||
inner_part += formatAccessFlagsWithColumns(element.access_flags, element.columns, element.any_column);
|
||||
|
||||
bool next_element_uses_same_mode_and_table = false;
|
||||
if (i != size() - 1)
|
||||
{
|
||||
const auto & next_element = (*this)[i + 1];
|
||||
if (element.sameDatabaseAndTable(next_element) && element.sameOptions(next_element))
|
||||
next_element_uses_same_mode_and_table = true;
|
||||
}
|
||||
|
||||
if (!next_element_uses_same_mode_and_table)
|
||||
{
|
||||
if (!res.empty())
|
||||
res += ", ";
|
||||
res += formatOptions(
|
||||
element.grant_option,
|
||||
element.kind,
|
||||
inner_part + " " + formatONClause(element.database, element.any_database, element.table, element.any_table));
|
||||
inner_part.clear();
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -71,26 +71,48 @@ struct AccessRightsElement
|
||||
{
|
||||
}
|
||||
|
||||
auto toTuple() const { return std::tie(access_flags, database, any_database, table, any_table, columns, any_column); }
|
||||
auto toTuple() const { return std::tie(access_flags, any_database, database, any_table, table, any_column, columns); }
|
||||
friend bool operator==(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() == right.toTuple(); }
|
||||
friend bool operator!=(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() != right.toTuple(); }
|
||||
friend bool operator<(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() < right.toTuple(); }
|
||||
friend bool operator>(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() > right.toTuple(); }
|
||||
friend bool operator<=(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() <= right.toTuple(); }
|
||||
friend bool operator>=(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() >= right.toTuple(); }
|
||||
friend bool operator!=(const AccessRightsElement & left, const AccessRightsElement & right) { return !(left == right); }
|
||||
|
||||
/// Sets the database.
|
||||
void setDatabase(const String & new_database);
|
||||
bool sameDatabaseAndTable(const AccessRightsElement & other) const
|
||||
{
|
||||
return (database == other.database) && (any_database == other.any_database) && (table == other.table)
|
||||
&& (any_table == other.any_table);
|
||||
}
|
||||
|
||||
bool isEmptyDatabase() const { return !any_database && database.empty(); }
|
||||
|
||||
/// If the database is empty, replaces it with `new_database`. Otherwise does nothing.
|
||||
void replaceEmptyDatabase(const String & new_database);
|
||||
|
||||
bool isEmptyDatabase() const;
|
||||
|
||||
/// Returns a human-readable representation like "SELECT, UPDATE(x, y) ON db.table".
|
||||
/// The returned string isn't prefixed with the "GRANT" keyword.
|
||||
String toString() const;
|
||||
String toStringWithoutON() const;
|
||||
};
|
||||
|
||||
|
||||
struct AccessRightsElementWithOptions : public AccessRightsElement
|
||||
{
|
||||
bool grant_option = false;
|
||||
|
||||
enum class Kind
|
||||
{
|
||||
GRANT,
|
||||
REVOKE,
|
||||
};
|
||||
Kind kind = Kind::GRANT;
|
||||
|
||||
bool sameOptions(const AccessRightsElementWithOptions & other) const
|
||||
{
|
||||
return (grant_option == other.grant_option) && (kind == other.kind);
|
||||
}
|
||||
|
||||
auto toTuple() const { return std::tie(access_flags, any_database, database, any_table, table, any_column, columns, grant_option, kind); }
|
||||
friend bool operator==(const AccessRightsElementWithOptions & left, const AccessRightsElementWithOptions & right) { return left.toTuple() == right.toTuple(); }
|
||||
friend bool operator!=(const AccessRightsElementWithOptions & left, const AccessRightsElementWithOptions & right) { return !(left == right); }
|
||||
|
||||
/// Returns a human-readable representation like "GRANT SELECT, UPDATE(x, y) ON db.table".
|
||||
String toString() const;
|
||||
};
|
||||
|
||||
|
||||
@ -101,13 +123,38 @@ public:
|
||||
/// Replaces the empty database with `new_database`.
|
||||
void replaceEmptyDatabase(const String & new_database);
|
||||
|
||||
/// Returns a human-readable representation like "SELECT, UPDATE(x, y) ON db.table".
|
||||
/// The returned string isn't prefixed with the "GRANT" keyword.
|
||||
String toString() const { return AccessRightsElements(*this).toString(); }
|
||||
String toString();
|
||||
|
||||
/// Reorder and group elements to show them in more readable form.
|
||||
void normalize();
|
||||
/// Returns a human-readable representation like "GRANT SELECT, UPDATE(x, y) ON db.table".
|
||||
String toString() const;
|
||||
};
|
||||
|
||||
|
||||
class AccessRightsElementsWithOptions : public std::vector<AccessRightsElementWithOptions>
|
||||
{
|
||||
public:
|
||||
/// Replaces the empty database with `new_database`.
|
||||
void replaceEmptyDatabase(const String & new_database);
|
||||
|
||||
/// Returns a human-readable representation like "GRANT SELECT, UPDATE(x, y) ON db.table".
|
||||
String toString() const;
|
||||
};
|
||||
|
||||
|
||||
inline void AccessRightsElement::replaceEmptyDatabase(const String & new_database)
|
||||
{
|
||||
if (isEmptyDatabase())
|
||||
database = new_database;
|
||||
}
|
||||
|
||||
inline void AccessRightsElements::replaceEmptyDatabase(const String & new_database)
|
||||
{
|
||||
for (auto & element : *this)
|
||||
element.replaceEmptyDatabase(new_database);
|
||||
}
|
||||
|
||||
inline void AccessRightsElementsWithOptions::replaceEmptyDatabase(const String & new_database)
|
||||
{
|
||||
for (auto & element : *this)
|
||||
element.replaceEmptyDatabase(new_database);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,8 +15,6 @@
|
||||
#include <Poco/Logger.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/smart_ptr/make_shared_object.hpp>
|
||||
#include <boost/range/algorithm/fill.hpp>
|
||||
#include <boost/range/algorithm/set_algorithm.hpp>
|
||||
#include <assert.h>
|
||||
|
||||
@ -32,68 +30,6 @@ namespace ErrorCodes
|
||||
extern const int UNKNOWN_USER;
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
enum CheckAccessRightsMode
|
||||
{
|
||||
RETURN_FALSE_IF_ACCESS_DENIED,
|
||||
LOG_WARNING_IF_ACCESS_DENIED,
|
||||
THROW_IF_ACCESS_DENIED,
|
||||
};
|
||||
|
||||
|
||||
String formatSkippedMessage()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
String formatSkippedMessage(const std::string_view & database)
|
||||
{
|
||||
return ". Skipped database " + backQuoteIfNeed(database);
|
||||
}
|
||||
|
||||
String formatSkippedMessage(const std::string_view & database, const std::string_view & table)
|
||||
{
|
||||
String str = ". Skipped table ";
|
||||
if (!database.empty())
|
||||
str += backQuoteIfNeed(database) + ".";
|
||||
str += backQuoteIfNeed(table);
|
||||
return str;
|
||||
}
|
||||
|
||||
String formatSkippedMessage(const std::string_view & database, const std::string_view & table, const std::string_view & column)
|
||||
{
|
||||
String str = ". Skipped column " + backQuoteIfNeed(column) + " ON ";
|
||||
if (!database.empty())
|
||||
str += backQuoteIfNeed(database) + ".";
|
||||
str += backQuoteIfNeed(table);
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename StringT>
|
||||
String formatSkippedMessage(const std::string_view & database, const std::string_view & table, const std::vector<StringT> & columns)
|
||||
{
|
||||
if (columns.size() == 1)
|
||||
return formatSkippedMessage(database, table, columns[0]);
|
||||
|
||||
String str = ". Skipped columns ";
|
||||
bool need_comma = false;
|
||||
for (const auto & column : columns)
|
||||
{
|
||||
if (std::exchange(need_comma, true))
|
||||
str += ", ";
|
||||
str += backQuoteIfNeed(column);
|
||||
}
|
||||
str += " ON ";
|
||||
if (!database.empty())
|
||||
str += backQuoteIfNeed(database) + ".";
|
||||
str += backQuoteIfNeed(table);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ContextAccess::ContextAccess(const AccessControlManager & manager_, const Params & params_)
|
||||
: manager(&manager_)
|
||||
, params(params_)
|
||||
@ -116,8 +52,8 @@ void ContextAccess::setUser(const UserPtr & user_) const
|
||||
if (!user)
|
||||
{
|
||||
/// User has been dropped.
|
||||
auto nothing_granted = boost::make_shared<AccessRights>();
|
||||
boost::range::fill(result_access, nothing_granted);
|
||||
auto nothing_granted = std::make_shared<AccessRights>();
|
||||
access = nothing_granted;
|
||||
subscription_for_user_change = {};
|
||||
subscription_for_roles_changes = {};
|
||||
enabled_roles = nullptr;
|
||||
@ -169,10 +105,73 @@ void ContextAccess::setRolesInfo(const std::shared_ptr<const EnabledRolesInfo> &
|
||||
{
|
||||
assert(roles_info_);
|
||||
roles_info = roles_info_;
|
||||
boost::range::fill(result_access, nullptr /* need recalculate */);
|
||||
enabled_row_policies = manager->getEnabledRowPolicies(*params.user_id, roles_info->enabled_roles);
|
||||
enabled_quota = manager->getEnabledQuota(*params.user_id, user_name, roles_info->enabled_roles, params.address, params.quota_key);
|
||||
enabled_settings = manager->getEnabledSettings(*params.user_id, user->settings, roles_info->enabled_roles, roles_info->settings_from_enabled_roles);
|
||||
setFinalAccess();
|
||||
}
|
||||
|
||||
|
||||
void ContextAccess::setFinalAccess() const
|
||||
{
|
||||
auto final_access = std::make_shared<AccessRights>();
|
||||
*final_access = user->access;
|
||||
if (roles_info)
|
||||
final_access->merge(roles_info->access);
|
||||
|
||||
static const AccessFlags table_ddl = AccessType::CREATE_DATABASE | AccessType::CREATE_TABLE | AccessType::CREATE_VIEW
|
||||
| AccessType::ALTER_TABLE | AccessType::ALTER_VIEW | AccessType::DROP_DATABASE | AccessType::DROP_TABLE | AccessType::DROP_VIEW
|
||||
| AccessType::TRUNCATE;
|
||||
|
||||
static const AccessFlags dictionary_ddl = AccessType::CREATE_DICTIONARY | AccessType::DROP_DICTIONARY;
|
||||
static const AccessFlags table_and_dictionary_ddl = table_ddl | dictionary_ddl;
|
||||
static const AccessFlags write_table_access = AccessType::INSERT | AccessType::OPTIMIZE;
|
||||
static const AccessFlags write_dcl_access = AccessType::ACCESS_MANAGEMENT - AccessType::SHOW_ACCESS;
|
||||
|
||||
if (params.readonly)
|
||||
final_access->revoke(write_table_access | table_and_dictionary_ddl | write_dcl_access | AccessType::SYSTEM | AccessType::KILL_QUERY);
|
||||
|
||||
if (params.readonly == 1)
|
||||
{
|
||||
/// Table functions are forbidden in readonly mode.
|
||||
/// For example, for readonly = 2 - allowed.
|
||||
final_access->revoke(AccessType::CREATE_TEMPORARY_TABLE);
|
||||
}
|
||||
|
||||
if (!params.allow_ddl)
|
||||
final_access->revoke(table_and_dictionary_ddl);
|
||||
|
||||
if (!params.allow_introspection)
|
||||
final_access->revoke(AccessType::INTROSPECTION);
|
||||
|
||||
/// Anyone has access to the "system" database.
|
||||
final_access->grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE);
|
||||
|
||||
if (params.readonly != 1)
|
||||
{
|
||||
/// User has access to temporary or external table if such table was resolved in session or query context
|
||||
final_access->grant(AccessFlags::allTableFlags() | AccessFlags::allColumnFlags(), DatabaseCatalog::TEMPORARY_DATABASE);
|
||||
}
|
||||
|
||||
if (params.readonly)
|
||||
{
|
||||
/// No grant option in readonly mode.
|
||||
final_access->revokeGrantOption(AccessType::ALL);
|
||||
}
|
||||
|
||||
if (trace_log)
|
||||
{
|
||||
if (roles_info && !roles_info->getCurrentRolesNames().empty())
|
||||
{
|
||||
LOG_TRACE(trace_log, "Current_roles: {}, enabled_roles: {}",
|
||||
boost::algorithm::join(roles_info->getCurrentRolesNames(), ", "),
|
||||
boost::algorithm::join(roles_info->getEnabledRolesNames(), ", "));
|
||||
}
|
||||
LOG_TRACE(trace_log, "Settings: readonly={}, allow_ddl={}, allow_introspection_functions={}", params.readonly, params.allow_ddl, params.allow_introspection);
|
||||
LOG_TRACE(trace_log, "List of all grants: {}", final_access->toString());
|
||||
}
|
||||
|
||||
access = final_access;
|
||||
}
|
||||
|
||||
|
||||
@ -193,284 +192,6 @@ bool ContextAccess::isClientHostAllowed() const
|
||||
}
|
||||
|
||||
|
||||
template <int mode, bool grant_option, typename... Args>
|
||||
bool ContextAccess::calculateResultAccessAndCheck(Poco::Logger * log_, const AccessFlags & flags, const Args &... args) const
|
||||
{
|
||||
auto access = calculateResultAccess(grant_option);
|
||||
bool is_granted = access->isGranted(flags, args...);
|
||||
|
||||
if (trace_log)
|
||||
LOG_TRACE(trace_log, "Access {}: {}", (is_granted ? "granted" : "denied"), (AccessRightsElement{flags, args...}.toString()));
|
||||
|
||||
if (is_granted)
|
||||
return true;
|
||||
|
||||
if constexpr (mode == RETURN_FALSE_IF_ACCESS_DENIED)
|
||||
return false;
|
||||
|
||||
if constexpr (mode == LOG_WARNING_IF_ACCESS_DENIED)
|
||||
{
|
||||
if (!log_)
|
||||
return false;
|
||||
}
|
||||
|
||||
auto show_error = [&](const String & msg, [[maybe_unused]] int error_code)
|
||||
{
|
||||
if constexpr (mode == THROW_IF_ACCESS_DENIED)
|
||||
throw Exception(user_name + ": " + msg, error_code);
|
||||
else if constexpr (mode == LOG_WARNING_IF_ACCESS_DENIED)
|
||||
LOG_WARNING(log_, "{}: {}{}", user_name, msg, formatSkippedMessage(args...));
|
||||
};
|
||||
|
||||
if (!user)
|
||||
{
|
||||
show_error("User has been dropped", ErrorCodes::UNKNOWN_USER);
|
||||
}
|
||||
else if (grant_option && calculateResultAccess(false, params.readonly, params.allow_ddl, params.allow_introspection)->isGranted(flags, args...))
|
||||
{
|
||||
show_error(
|
||||
"Not enough privileges. "
|
||||
"The required privileges have been granted, but without grant option. "
|
||||
"To execute this query it's necessary to have the grant "
|
||||
+ AccessRightsElement{flags, args...}.toString() + " WITH GRANT OPTION",
|
||||
ErrorCodes::ACCESS_DENIED);
|
||||
}
|
||||
else if (params.readonly && calculateResultAccess(false, false, params.allow_ddl, params.allow_introspection)->isGranted(flags, args...))
|
||||
{
|
||||
if (params.interface == ClientInfo::Interface::HTTP && params.http_method == ClientInfo::HTTPMethod::GET)
|
||||
show_error(
|
||||
"Cannot execute query in readonly mode. "
|
||||
"For queries over HTTP, method GET implies readonly. You should use method POST for modifying queries",
|
||||
ErrorCodes::READONLY);
|
||||
else
|
||||
show_error("Cannot execute query in readonly mode", ErrorCodes::READONLY);
|
||||
}
|
||||
else if (!params.allow_ddl && calculateResultAccess(false, params.readonly, true, params.allow_introspection)->isGranted(flags, args...))
|
||||
{
|
||||
show_error("Cannot execute query. DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
|
||||
}
|
||||
else if (!params.allow_introspection && calculateResultAccess(false, params.readonly, params.allow_ddl, true)->isGranted(flags, args...))
|
||||
{
|
||||
show_error("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
|
||||
}
|
||||
else
|
||||
{
|
||||
show_error(
|
||||
"Not enough privileges. To execute this query it's necessary to have the grant "
|
||||
+ AccessRightsElement{flags, args...}.toString() + (grant_option ? " WITH GRANT OPTION" : ""),
|
||||
ErrorCodes::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <int mode, bool grant_option>
|
||||
bool ContextAccess::checkAccessImpl(Poco::Logger * log_, const AccessFlags & flags) const
|
||||
{
|
||||
return calculateResultAccessAndCheck<mode, grant_option>(log_, flags);
|
||||
}
|
||||
|
||||
template <int mode, bool grant_option, typename... Args>
|
||||
bool ContextAccess::checkAccessImpl(Poco::Logger * log_, const AccessFlags & flags, const std::string_view & database, const Args &... args) const
|
||||
{
|
||||
if (database.empty())
|
||||
return calculateResultAccessAndCheck<mode, grant_option>(log_, flags, params.current_database, args...);
|
||||
else
|
||||
return calculateResultAccessAndCheck<mode, grant_option>(log_, flags, database, args...);
|
||||
}
|
||||
|
||||
|
||||
template <int mode, bool grant_option>
|
||||
bool ContextAccess::checkAccessImpl(Poco::Logger * log_, const AccessRightsElement & element) const
|
||||
{
|
||||
if (element.any_database)
|
||||
{
|
||||
return checkAccessImpl<mode, grant_option>(log_, element.access_flags);
|
||||
}
|
||||
else if (element.any_table)
|
||||
{
|
||||
return checkAccessImpl<mode, grant_option>(log_, element.access_flags, element.database);
|
||||
}
|
||||
else if (element.any_column)
|
||||
{
|
||||
return checkAccessImpl<mode, grant_option>(log_, element.access_flags, element.database, element.table);
|
||||
}
|
||||
else
|
||||
{
|
||||
return checkAccessImpl<mode, grant_option>(log_, element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <int mode, bool grant_option>
|
||||
bool ContextAccess::checkAccessImpl(Poco::Logger * log_, const AccessRightsElements & elements) const
|
||||
{
|
||||
for (const auto & element : elements)
|
||||
if (!checkAccessImpl<mode, grant_option>(log_, element))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ContextAccess::checkAccess(const AccessFlags & flags) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, flags); }
|
||||
void ContextAccess::checkAccess(const AccessFlags & flags, const std::string_view & database) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, flags, database); }
|
||||
void ContextAccess::checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, flags, database, table); }
|
||||
void ContextAccess::checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, flags, database, table, column); }
|
||||
void ContextAccess::checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, flags, database, table, columns); }
|
||||
void ContextAccess::checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, flags, database, table, columns); }
|
||||
void ContextAccess::checkAccess(const AccessRightsElement & element) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, element); }
|
||||
void ContextAccess::checkAccess(const AccessRightsElements & elements) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, elements); }
|
||||
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, flags); }
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags, const std::string_view & database) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, flags, database); }
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, flags, database, table); }
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, flags, database, table, column); }
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, flags, database, table, columns); }
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, flags, database, table, columns); }
|
||||
bool ContextAccess::isGranted(const AccessRightsElement & element) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, element); }
|
||||
bool ContextAccess::isGranted(const AccessRightsElements & elements) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, elements); }
|
||||
|
||||
bool ContextAccess::isGranted(Poco::Logger * log_, const AccessFlags & flags) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, flags); }
|
||||
bool ContextAccess::isGranted(Poco::Logger * log_, const AccessFlags & flags, const std::string_view & database) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, flags, database); }
|
||||
bool ContextAccess::isGranted(Poco::Logger * log_, const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, flags, database, table); }
|
||||
bool ContextAccess::isGranted(Poco::Logger * log_, const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, flags, database, table, column); }
|
||||
bool ContextAccess::isGranted(Poco::Logger * log_, const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, flags, database, table, columns); }
|
||||
bool ContextAccess::isGranted(Poco::Logger * log_, const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, flags, database, table, columns); }
|
||||
bool ContextAccess::isGranted(Poco::Logger * log_, const AccessRightsElement & element) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, element); }
|
||||
bool ContextAccess::isGranted(Poco::Logger * log_, const AccessRightsElements & elements) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, elements); }
|
||||
|
||||
void ContextAccess::checkGrantOption(const AccessFlags & flags) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, flags); }
|
||||
void ContextAccess::checkGrantOption(const AccessFlags & flags, const std::string_view & database) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, flags, database); }
|
||||
void ContextAccess::checkGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, flags, database, table); }
|
||||
void ContextAccess::checkGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, flags, database, table, column); }
|
||||
void ContextAccess::checkGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, flags, database, table, columns); }
|
||||
void ContextAccess::checkGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, flags, database, table, columns); }
|
||||
void ContextAccess::checkGrantOption(const AccessRightsElement & element) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, element); }
|
||||
void ContextAccess::checkGrantOption(const AccessRightsElements & elements) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, elements); }
|
||||
|
||||
|
||||
void ContextAccess::checkAdminOption(const UUID & role_id) const
|
||||
{
|
||||
if (isGranted(AccessType::ROLE_ADMIN))
|
||||
return;
|
||||
|
||||
auto info = getRolesInfo();
|
||||
if (info && info->enabled_roles_with_admin_option.count(role_id))
|
||||
return;
|
||||
|
||||
if (!user)
|
||||
throw Exception(user_name + ": User has been dropped", ErrorCodes::UNKNOWN_USER);
|
||||
|
||||
std::optional<String> role_name = manager->readName(role_id);
|
||||
if (!role_name)
|
||||
role_name = "ID {" + toString(role_id) + "}";
|
||||
throw Exception(
|
||||
getUserName() + ": Not enough privileges. To execute this query it's necessary to have the grant " + backQuoteIfNeed(*role_name)
|
||||
+ " WITH ADMIN OPTION ",
|
||||
ErrorCodes::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<const AccessRights> ContextAccess::calculateResultAccess(bool grant_option) const
|
||||
{
|
||||
return calculateResultAccess(grant_option, params.readonly, params.allow_ddl, params.allow_introspection);
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<const AccessRights> ContextAccess::calculateResultAccess(bool grant_option, UInt64 readonly_, bool allow_ddl_, bool allow_introspection_) const
|
||||
{
|
||||
size_t index = static_cast<size_t>(readonly_ != params.readonly)
|
||||
+ static_cast<size_t>(allow_ddl_ != params.allow_ddl) * 2 +
|
||||
+ static_cast<size_t>(allow_introspection_ != params.allow_introspection) * 3
|
||||
+ static_cast<size_t>(grant_option) * 4;
|
||||
assert(index < std::size(result_access));
|
||||
auto res = result_access[index].load();
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
std::lock_guard lock{mutex};
|
||||
res = result_access[index].load();
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
auto merged_access = boost::make_shared<AccessRights>();
|
||||
|
||||
if (grant_option)
|
||||
{
|
||||
*merged_access = user->access.access_with_grant_option;
|
||||
if (roles_info)
|
||||
merged_access->merge(roles_info->access_with_grant_option);
|
||||
}
|
||||
else
|
||||
{
|
||||
*merged_access = user->access.access;
|
||||
if (roles_info)
|
||||
merged_access->merge(roles_info->access);
|
||||
}
|
||||
|
||||
static const AccessFlags table_ddl = AccessType::CREATE_DATABASE | AccessType::CREATE_TABLE | AccessType::CREATE_VIEW
|
||||
| AccessType::ALTER_TABLE | AccessType::ALTER_VIEW | AccessType::DROP_DATABASE | AccessType::DROP_TABLE | AccessType::DROP_VIEW
|
||||
| AccessType::TRUNCATE;
|
||||
|
||||
static const AccessFlags dictionary_ddl = AccessType::CREATE_DICTIONARY | AccessType::DROP_DICTIONARY;
|
||||
static const AccessFlags table_and_dictionary_ddl = table_ddl | dictionary_ddl;
|
||||
static const AccessFlags write_table_access = AccessType::INSERT | AccessType::OPTIMIZE;
|
||||
static const AccessFlags write_dcl_access = AccessType::ACCESS_MANAGEMENT - AccessType::SHOW_ACCESS;
|
||||
|
||||
if (readonly_)
|
||||
merged_access->revoke(write_table_access | table_and_dictionary_ddl | write_dcl_access | AccessType::SYSTEM | AccessType::KILL_QUERY);
|
||||
|
||||
if (readonly_ == 1)
|
||||
{
|
||||
/// Table functions are forbidden in readonly mode.
|
||||
/// For example, for readonly = 2 - allowed.
|
||||
merged_access->revoke(AccessType::CREATE_TEMPORARY_TABLE);
|
||||
}
|
||||
|
||||
if (!allow_ddl_)
|
||||
merged_access->revoke(table_and_dictionary_ddl);
|
||||
|
||||
if (!allow_introspection_)
|
||||
merged_access->revoke(AccessType::INTROSPECTION);
|
||||
|
||||
/// Anyone has access to the "system" database.
|
||||
merged_access->grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE);
|
||||
|
||||
if (readonly_ != 1)
|
||||
{
|
||||
/// User has access to temporary or external table if such table was resolved in session or query context
|
||||
merged_access->grant(AccessFlags::allTableFlags() | AccessFlags::allColumnFlags(), DatabaseCatalog::TEMPORARY_DATABASE);
|
||||
}
|
||||
|
||||
if (readonly_ && grant_option)
|
||||
{
|
||||
/// No grant option in readonly mode.
|
||||
merged_access->revoke(AccessType::ALL);
|
||||
}
|
||||
|
||||
if (trace_log && (params.readonly == readonly_) && (params.allow_ddl == allow_ddl_) && (params.allow_introspection == allow_introspection_))
|
||||
{
|
||||
if (grant_option)
|
||||
LOG_TRACE(trace_log, "List of all grants: {} WITH GRANT OPTION", merged_access->toString());
|
||||
else
|
||||
LOG_TRACE(trace_log, "List of all grants: {}", merged_access->toString());
|
||||
|
||||
if (roles_info && !roles_info->getCurrentRolesNames().empty())
|
||||
{
|
||||
LOG_TRACE(trace_log, "Current_roles: {}, enabled_roles: {}",
|
||||
boost::algorithm::join(roles_info->getCurrentRolesNames(), ", "),
|
||||
boost::algorithm::join(roles_info->getEnabledRolesNames(), ", "));
|
||||
}
|
||||
LOG_TRACE(trace_log, "Settings: readonly={}, allow_ddl={}, allow_introspection_functions={}", readonly_, allow_ddl_, allow_introspection_);
|
||||
}
|
||||
|
||||
res = std::move(merged_access);
|
||||
result_access[index].store(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
UserPtr ContextAccess::getUser() const
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
@ -520,9 +241,7 @@ std::shared_ptr<const ContextAccess> ContextAccess::getFullAccess()
|
||||
static const std::shared_ptr<const ContextAccess> res = []
|
||||
{
|
||||
auto full_access = std::shared_ptr<ContextAccess>(new ContextAccess);
|
||||
auto everything_granted = boost::make_shared<AccessRights>();
|
||||
everything_granted->grant(AccessType::ALL);
|
||||
boost::range::fill(full_access->result_access, everything_granted);
|
||||
full_access->access = std::make_shared<AccessRights>(AccessRights::getFullAccess());
|
||||
full_access->enabled_quota = EnabledQuota::getUnlimitedQuota();
|
||||
return full_access;
|
||||
}();
|
||||
@ -543,4 +262,246 @@ std::shared_ptr<const SettingsConstraints> ContextAccess::getSettingsConstraints
|
||||
return enabled_settings ? enabled_settings->getConstraints() : nullptr;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<const AccessRights> ContextAccess::getAccess() const
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
return access;
|
||||
}
|
||||
|
||||
|
||||
template <bool grant_option, typename... Args>
|
||||
bool ContextAccess::isGrantedImpl2(const AccessFlags & flags, const Args &... args) const
|
||||
{
|
||||
bool access_granted;
|
||||
if constexpr (grant_option)
|
||||
access_granted = getAccess()->hasGrantOption(flags, args...);
|
||||
else
|
||||
access_granted = getAccess()->isGranted(flags, args...);
|
||||
|
||||
if (trace_log)
|
||||
LOG_TRACE(trace_log, "Access {}: {}{}", (access_granted ? "granted" : "denied"), (AccessRightsElement{flags, args...}.toString()),
|
||||
(grant_option ? " WITH GRANT OPTION" : ""));
|
||||
|
||||
return access_granted;
|
||||
}
|
||||
|
||||
template <bool grant_option>
|
||||
bool ContextAccess::isGrantedImpl(const AccessFlags & flags) const
|
||||
{
|
||||
return isGrantedImpl2<grant_option>(flags);
|
||||
}
|
||||
|
||||
template <bool grant_option, typename... Args>
|
||||
bool ContextAccess::isGrantedImpl(const AccessFlags & flags, const std::string_view & database, const Args &... args) const
|
||||
{
|
||||
return isGrantedImpl2<grant_option>(flags, database.empty() ? params.current_database : database, args...);
|
||||
}
|
||||
|
||||
template <bool grant_option>
|
||||
bool ContextAccess::isGrantedImpl(const AccessRightsElement & element) const
|
||||
{
|
||||
if (element.any_database)
|
||||
return isGrantedImpl<grant_option>(element.access_flags);
|
||||
else if (element.any_table)
|
||||
return isGrantedImpl<grant_option>(element.access_flags, element.database);
|
||||
else if (element.any_column)
|
||||
return isGrantedImpl<grant_option>(element.access_flags, element.database, element.table);
|
||||
else
|
||||
return isGrantedImpl<grant_option>(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
|
||||
template <bool grant_option>
|
||||
bool ContextAccess::isGrantedImpl(const AccessRightsElements & elements) const
|
||||
{
|
||||
for (const auto & element : elements)
|
||||
if (!isGrantedImpl<grant_option>(element))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags) const { return isGrantedImpl<false>(flags); }
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags, const std::string_view & database) const { return isGrantedImpl<false>(flags, database); }
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const { return isGrantedImpl<false>(flags, database, table); }
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return isGrantedImpl<false>(flags, database, table, column); }
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return isGrantedImpl<false>(flags, database, table, columns); }
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return isGrantedImpl<false>(flags, database, table, columns); }
|
||||
bool ContextAccess::isGranted(const AccessRightsElement & element) const { return isGrantedImpl<false>(element); }
|
||||
bool ContextAccess::isGranted(const AccessRightsElements & elements) const { return isGrantedImpl<false>(elements); }
|
||||
|
||||
bool ContextAccess::hasGrantOption(const AccessFlags & flags) const { return isGrantedImpl<true>(flags); }
|
||||
bool ContextAccess::hasGrantOption(const AccessFlags & flags, const std::string_view & database) const { return isGrantedImpl<true>(flags, database); }
|
||||
bool ContextAccess::hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const { return isGrantedImpl<true>(flags, database, table); }
|
||||
bool ContextAccess::hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return isGrantedImpl<true>(flags, database, table, column); }
|
||||
bool ContextAccess::hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return isGrantedImpl<true>(flags, database, table, columns); }
|
||||
bool ContextAccess::hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return isGrantedImpl<true>(flags, database, table, columns); }
|
||||
bool ContextAccess::hasGrantOption(const AccessRightsElement & element) const { return isGrantedImpl<true>(element); }
|
||||
bool ContextAccess::hasGrantOption(const AccessRightsElements & elements) const { return isGrantedImpl<true>(elements); }
|
||||
|
||||
|
||||
template <bool grant_option, typename... Args>
|
||||
void ContextAccess::checkAccessImpl2(const AccessFlags & flags, const Args &... args) const
|
||||
{
|
||||
if constexpr (grant_option)
|
||||
{
|
||||
if (hasGrantOption(flags, args...))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isGranted(flags, args...))
|
||||
return;
|
||||
}
|
||||
|
||||
auto show_error = [&](const String & msg, int error_code)
|
||||
{
|
||||
throw Exception(user_name + ": " + msg, error_code);
|
||||
};
|
||||
|
||||
std::lock_guard lock{mutex};
|
||||
|
||||
if (!user)
|
||||
{
|
||||
show_error("User has been dropped", ErrorCodes::UNKNOWN_USER);
|
||||
}
|
||||
|
||||
if (grant_option && access->isGranted(flags, args...))
|
||||
{
|
||||
show_error(
|
||||
"Not enough privileges. "
|
||||
"The required privileges have been granted, but without grant option. "
|
||||
"To execute this query it's necessary to have the grant "
|
||||
+ AccessRightsElement{flags, args...}.toString() + " WITH GRANT OPTION",
|
||||
ErrorCodes::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
if (params.readonly)
|
||||
{
|
||||
if (!access_without_readonly)
|
||||
{
|
||||
Params changed_params = params;
|
||||
changed_params.readonly = 0;
|
||||
access_without_readonly = manager->getContextAccess(changed_params);
|
||||
}
|
||||
|
||||
if (access_without_readonly->isGranted(flags, args...))
|
||||
{
|
||||
if (params.interface == ClientInfo::Interface::HTTP && params.http_method == ClientInfo::HTTPMethod::GET)
|
||||
show_error(
|
||||
"Cannot execute query in readonly mode. "
|
||||
"For queries over HTTP, method GET implies readonly. You should use method POST for modifying queries",
|
||||
ErrorCodes::READONLY);
|
||||
else
|
||||
show_error("Cannot execute query in readonly mode", ErrorCodes::READONLY);
|
||||
}
|
||||
}
|
||||
|
||||
if (!params.allow_ddl)
|
||||
{
|
||||
if (!access_with_allow_ddl)
|
||||
{
|
||||
Params changed_params = params;
|
||||
changed_params.allow_ddl = true;
|
||||
access_with_allow_ddl = manager->getContextAccess(changed_params);
|
||||
}
|
||||
|
||||
if (access_with_allow_ddl->isGranted(flags, args...))
|
||||
{
|
||||
show_error("Cannot execute query. DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
|
||||
}
|
||||
}
|
||||
|
||||
if (!params.allow_introspection)
|
||||
{
|
||||
if (!access_with_allow_introspection)
|
||||
{
|
||||
Params changed_params = params;
|
||||
changed_params.allow_introspection = true;
|
||||
access_with_allow_introspection = manager->getContextAccess(changed_params);
|
||||
}
|
||||
|
||||
if (access_with_allow_introspection->isGranted(flags, args...))
|
||||
{
|
||||
show_error("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
|
||||
}
|
||||
}
|
||||
|
||||
show_error(
|
||||
"Not enough privileges. To execute this query it's necessary to have the grant "
|
||||
+ AccessRightsElement{flags, args...}.toString() + (grant_option ? " WITH GRANT OPTION" : ""),
|
||||
ErrorCodes::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
template <bool grant_option>
|
||||
void ContextAccess::checkAccessImpl(const AccessFlags & flags) const
|
||||
{
|
||||
checkAccessImpl2<grant_option>(flags);
|
||||
}
|
||||
|
||||
template <bool grant_option, typename... Args>
|
||||
void ContextAccess::checkAccessImpl(const AccessFlags & flags, const std::string_view & database, const Args &... args) const
|
||||
{
|
||||
checkAccessImpl2<grant_option>(flags, database.empty() ? params.current_database : database, args...);
|
||||
}
|
||||
|
||||
template <bool grant_option>
|
||||
void ContextAccess::checkAccessImpl(const AccessRightsElement & element) const
|
||||
{
|
||||
if (element.any_database)
|
||||
checkAccessImpl<grant_option>(element.access_flags);
|
||||
else if (element.any_table)
|
||||
checkAccessImpl<grant_option>(element.access_flags, element.database);
|
||||
else if (element.any_column)
|
||||
checkAccessImpl<grant_option>(element.access_flags, element.database, element.table);
|
||||
else
|
||||
checkAccessImpl<grant_option>(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
|
||||
template <bool grant_option>
|
||||
void ContextAccess::checkAccessImpl(const AccessRightsElements & elements) const
|
||||
{
|
||||
for (const auto & element : elements)
|
||||
checkAccessImpl<grant_option>(element);
|
||||
}
|
||||
|
||||
void ContextAccess::checkAccess(const AccessFlags & flags) const { checkAccessImpl<false>(flags); }
|
||||
void ContextAccess::checkAccess(const AccessFlags & flags, const std::string_view & database) const { checkAccessImpl<false>(flags, database); }
|
||||
void ContextAccess::checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const { checkAccessImpl<false>(flags, database, table); }
|
||||
void ContextAccess::checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { checkAccessImpl<false>(flags, database, table, column); }
|
||||
void ContextAccess::checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { checkAccessImpl<false>(flags, database, table, columns); }
|
||||
void ContextAccess::checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const { checkAccessImpl<false>(flags, database, table, columns); }
|
||||
void ContextAccess::checkAccess(const AccessRightsElement & element) const { checkAccessImpl<false>(element); }
|
||||
void ContextAccess::checkAccess(const AccessRightsElements & elements) const { checkAccessImpl<false>(elements); }
|
||||
|
||||
void ContextAccess::checkGrantOption(const AccessFlags & flags) const { checkAccessImpl<true>(flags); }
|
||||
void ContextAccess::checkGrantOption(const AccessFlags & flags, const std::string_view & database) const { checkAccessImpl<true>(flags, database); }
|
||||
void ContextAccess::checkGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const { checkAccessImpl<true>(flags, database, table); }
|
||||
void ContextAccess::checkGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { checkAccessImpl<true>(flags, database, table, column); }
|
||||
void ContextAccess::checkGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { checkAccessImpl<true>(flags, database, table, columns); }
|
||||
void ContextAccess::checkGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const { checkAccessImpl<true>(flags, database, table, columns); }
|
||||
void ContextAccess::checkGrantOption(const AccessRightsElement & element) const { checkAccessImpl<true>(element); }
|
||||
void ContextAccess::checkGrantOption(const AccessRightsElements & elements) const { checkAccessImpl<true>(elements); }
|
||||
|
||||
|
||||
void ContextAccess::checkAdminOption(const UUID & role_id) const
|
||||
{
|
||||
if (isGranted(AccessType::ROLE_ADMIN))
|
||||
return;
|
||||
|
||||
auto info = getRolesInfo();
|
||||
if (info && info->enabled_roles_with_admin_option.count(role_id))
|
||||
return;
|
||||
|
||||
if (!user)
|
||||
throw Exception(user_name + ": User has been dropped", ErrorCodes::UNKNOWN_USER);
|
||||
|
||||
std::optional<String> role_name = manager->readName(role_id);
|
||||
if (!role_name)
|
||||
role_name = "ID {" + toString(role_id) + "}";
|
||||
throw Exception(
|
||||
getUserName() + ": Not enough privileges. To execute this query it's necessary to have the grant " + backQuoteIfNeed(*role_name)
|
||||
+ " WITH ADMIN OPTION ",
|
||||
ErrorCodes::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include <Core/UUID.h>
|
||||
#include <ext/scope_guard.h>
|
||||
#include <ext/shared_ptr_helper.h>
|
||||
#include <boost/smart_ptr/atomic_shared_ptr.hpp>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <mutex>
|
||||
|
||||
@ -30,32 +29,34 @@ class IAST;
|
||||
using ASTPtr = std::shared_ptr<IAST>;
|
||||
|
||||
|
||||
struct ContextAccessParams
|
||||
{
|
||||
std::optional<UUID> user_id;
|
||||
boost::container::flat_set<UUID> current_roles;
|
||||
bool use_default_roles = false;
|
||||
UInt64 readonly = 0;
|
||||
bool allow_ddl = false;
|
||||
bool allow_introspection = false;
|
||||
String current_database;
|
||||
ClientInfo::Interface interface = ClientInfo::Interface::TCP;
|
||||
ClientInfo::HTTPMethod http_method = ClientInfo::HTTPMethod::UNKNOWN;
|
||||
Poco::Net::IPAddress address;
|
||||
String quota_key;
|
||||
|
||||
auto toTuple() const { return std::tie(user_id, current_roles, use_default_roles, readonly, allow_ddl, allow_introspection, current_database, interface, http_method, address, quota_key); }
|
||||
friend bool operator ==(const ContextAccessParams & lhs, const ContextAccessParams & rhs) { return lhs.toTuple() == rhs.toTuple(); }
|
||||
friend bool operator !=(const ContextAccessParams & lhs, const ContextAccessParams & rhs) { return !(lhs == rhs); }
|
||||
friend bool operator <(const ContextAccessParams & lhs, const ContextAccessParams & rhs) { return lhs.toTuple() < rhs.toTuple(); }
|
||||
friend bool operator >(const ContextAccessParams & lhs, const ContextAccessParams & rhs) { return rhs < lhs; }
|
||||
friend bool operator <=(const ContextAccessParams & lhs, const ContextAccessParams & rhs) { return !(rhs < lhs); }
|
||||
friend bool operator >=(const ContextAccessParams & lhs, const ContextAccessParams & rhs) { return !(lhs < rhs); }
|
||||
};
|
||||
|
||||
|
||||
class ContextAccess
|
||||
{
|
||||
public:
|
||||
struct Params
|
||||
{
|
||||
std::optional<UUID> user_id;
|
||||
boost::container::flat_set<UUID> current_roles;
|
||||
bool use_default_roles = false;
|
||||
UInt64 readonly = 0;
|
||||
bool allow_ddl = false;
|
||||
bool allow_introspection = false;
|
||||
String current_database;
|
||||
ClientInfo::Interface interface = ClientInfo::Interface::TCP;
|
||||
ClientInfo::HTTPMethod http_method = ClientInfo::HTTPMethod::UNKNOWN;
|
||||
Poco::Net::IPAddress address;
|
||||
String quota_key;
|
||||
|
||||
auto toTuple() const { return std::tie(user_id, current_roles, use_default_roles, readonly, allow_ddl, allow_introspection, current_database, interface, http_method, address, quota_key); }
|
||||
friend bool operator ==(const Params & lhs, const Params & rhs) { return lhs.toTuple() == rhs.toTuple(); }
|
||||
friend bool operator !=(const Params & lhs, const Params & rhs) { return !(lhs == rhs); }
|
||||
friend bool operator <(const Params & lhs, const Params & rhs) { return lhs.toTuple() < rhs.toTuple(); }
|
||||
friend bool operator >(const Params & lhs, const Params & rhs) { return rhs < lhs; }
|
||||
friend bool operator <=(const Params & lhs, const Params & rhs) { return !(rhs < lhs); }
|
||||
friend bool operator >=(const Params & lhs, const Params & rhs) { return !(lhs < rhs); }
|
||||
};
|
||||
|
||||
using Params = ContextAccessParams;
|
||||
const Params & getParams() const { return params; }
|
||||
|
||||
/// Returns the current user. The function can return nullptr.
|
||||
@ -90,16 +91,8 @@ public:
|
||||
/// The function returns nullptr if there are no constraints.
|
||||
std::shared_ptr<const SettingsConstraints> getSettingsConstraints() const;
|
||||
|
||||
/// Checks if a specified access is granted, and throws an exception if not.
|
||||
/// Empty database means the current database.
|
||||
void checkAccess(const AccessFlags & flags) const;
|
||||
void checkAccess(const AccessFlags & flags, const std::string_view & database) const;
|
||||
void checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const;
|
||||
void checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
|
||||
void checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
|
||||
void checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
|
||||
void checkAccess(const AccessRightsElement & element) const;
|
||||
void checkAccess(const AccessRightsElements & elements) const;
|
||||
/// Returns the current access rights.
|
||||
std::shared_ptr<const AccessRights> getAccess() const;
|
||||
|
||||
/// Checks if a specified access is granted.
|
||||
bool isGranted(const AccessFlags & flags) const;
|
||||
@ -111,17 +104,26 @@ public:
|
||||
bool isGranted(const AccessRightsElement & element) const;
|
||||
bool isGranted(const AccessRightsElements & elements) const;
|
||||
|
||||
/// Checks if a specified access is granted, and logs a warning if not.
|
||||
bool isGranted(Poco::Logger * log_, const AccessFlags & flags) const;
|
||||
bool isGranted(Poco::Logger * log_, const AccessFlags & flags, const std::string_view & database) const;
|
||||
bool isGranted(Poco::Logger * log_, const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const;
|
||||
bool isGranted(Poco::Logger * log_, const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
|
||||
bool isGranted(Poco::Logger * log_, const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
|
||||
bool isGranted(Poco::Logger * log_, const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
|
||||
bool isGranted(Poco::Logger * log_, const AccessRightsElement & element) const;
|
||||
bool isGranted(Poco::Logger * log_, const AccessRightsElements & elements) const;
|
||||
bool hasGrantOption(const AccessFlags & flags) const;
|
||||
bool hasGrantOption(const AccessFlags & flags, const std::string_view & database) const;
|
||||
bool hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const;
|
||||
bool hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
|
||||
bool hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
|
||||
bool hasGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
|
||||
bool hasGrantOption(const AccessRightsElement & element) const;
|
||||
bool hasGrantOption(const AccessRightsElements & elements) const;
|
||||
|
||||
/// Checks if a specified access is granted, and throws an exception if not.
|
||||
/// Empty database means the current database.
|
||||
void checkAccess(const AccessFlags & flags) const;
|
||||
void checkAccess(const AccessFlags & flags, const std::string_view & database) const;
|
||||
void checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const;
|
||||
void checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
|
||||
void checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
|
||||
void checkAccess(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
|
||||
void checkAccess(const AccessRightsElement & element) const;
|
||||
void checkAccess(const AccessRightsElements & elements) const;
|
||||
|
||||
/// Checks if a specified access is granted with grant option, and throws an exception if not.
|
||||
void checkGrantOption(const AccessFlags & flags) const;
|
||||
void checkGrantOption(const AccessFlags & flags, const std::string_view & database) const;
|
||||
void checkGrantOption(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const;
|
||||
@ -146,24 +148,37 @@ private:
|
||||
void setUser(const UserPtr & user_) const;
|
||||
void setRolesInfo(const std::shared_ptr<const EnabledRolesInfo> & roles_info_) const;
|
||||
void setSettingsAndConstraints() const;
|
||||
void setFinalAccess() const;
|
||||
|
||||
template <int mode, bool grant_option>
|
||||
bool checkAccessImpl(Poco::Logger * log_, const AccessFlags & flags) const;
|
||||
template <bool grant_option>
|
||||
bool isGrantedImpl(const AccessFlags & flags) const;
|
||||
|
||||
template <int mode, bool grant_option, typename... Args>
|
||||
bool checkAccessImpl(Poco::Logger * log_, const AccessFlags & flags, const std::string_view & database, const Args &... args) const;
|
||||
template <bool grant_option, typename... Args>
|
||||
bool isGrantedImpl(const AccessFlags & flags, const std::string_view & database, const Args &... args) const;
|
||||
|
||||
template <int mode, bool grant_option>
|
||||
bool checkAccessImpl(Poco::Logger * log_, const AccessRightsElement & element) const;
|
||||
template <bool grant_option>
|
||||
bool isGrantedImpl(const AccessRightsElement & element) const;
|
||||
|
||||
template <int mode, bool grant_option>
|
||||
bool checkAccessImpl(Poco::Logger * log_, const AccessRightsElements & elements) const;
|
||||
template <bool grant_option>
|
||||
bool isGrantedImpl(const AccessRightsElements & elements) const;
|
||||
|
||||
template <int mode, bool grant_option, typename... Args>
|
||||
bool calculateResultAccessAndCheck(Poco::Logger * log_, const AccessFlags & flags, const Args &... args) const;
|
||||
template <bool grant_option, typename... Args>
|
||||
bool isGrantedImpl2(const AccessFlags & flags, const Args &... args) const;
|
||||
|
||||
boost::shared_ptr<const AccessRights> calculateResultAccess(bool grant_option) const;
|
||||
boost::shared_ptr<const AccessRights> calculateResultAccess(bool grant_option, UInt64 readonly_, bool allow_ddl_, bool allow_introspection_) const;
|
||||
template <bool grant_option>
|
||||
void checkAccessImpl(const AccessFlags & flags) const;
|
||||
|
||||
template <bool grant_option, typename... Args>
|
||||
void checkAccessImpl(const AccessFlags & flags, const std::string_view & database, const Args &... args) const;
|
||||
|
||||
template <bool grant_option>
|
||||
void checkAccessImpl(const AccessRightsElement & element) const;
|
||||
|
||||
template <bool grant_option>
|
||||
void checkAccessImpl(const AccessRightsElements & elements) const;
|
||||
|
||||
template <bool grant_option, typename... Args>
|
||||
void checkAccessImpl2(const AccessFlags & flags, const Args &... args) const;
|
||||
|
||||
const AccessControlManager * manager = nullptr;
|
||||
const Params params;
|
||||
@ -174,10 +189,13 @@ private:
|
||||
mutable std::shared_ptr<const EnabledRoles> enabled_roles;
|
||||
mutable ext::scope_guard subscription_for_roles_changes;
|
||||
mutable std::shared_ptr<const EnabledRolesInfo> roles_info;
|
||||
mutable boost::atomic_shared_ptr<const AccessRights> result_access[7];
|
||||
mutable std::shared_ptr<const AccessRights> access;
|
||||
mutable std::shared_ptr<const EnabledRowPolicies> enabled_row_policies;
|
||||
mutable std::shared_ptr<const EnabledQuota> enabled_quota;
|
||||
mutable std::shared_ptr<const EnabledSettings> enabled_settings;
|
||||
mutable std::shared_ptr<const ContextAccess> access_without_readonly;
|
||||
mutable std::shared_ptr<const ContextAccess> access_with_allow_ddl;
|
||||
mutable std::shared_ptr<const ContextAccess> access_with_allow_introspection;
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
|
@ -28,8 +28,7 @@ bool operator==(const EnabledRolesInfo & lhs, const EnabledRolesInfo & rhs)
|
||||
{
|
||||
return (lhs.current_roles == rhs.current_roles) && (lhs.enabled_roles == rhs.enabled_roles)
|
||||
&& (lhs.enabled_roles_with_admin_option == rhs.enabled_roles_with_admin_option) && (lhs.names_of_roles == rhs.names_of_roles)
|
||||
&& (lhs.access == rhs.access) && (lhs.access_with_grant_option == rhs.access_with_grant_option)
|
||||
&& (lhs.settings_from_enabled_roles == rhs.settings_from_enabled_roles);
|
||||
&& (lhs.access == rhs.access) && (lhs.settings_from_enabled_roles == rhs.settings_from_enabled_roles);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ struct EnabledRolesInfo
|
||||
boost::container::flat_set<UUID> enabled_roles_with_admin_option;
|
||||
std::unordered_map<UUID, String> names_of_roles;
|
||||
AccessRights access;
|
||||
AccessRights access_with_grant_option;
|
||||
SettingsProfileElements settings_from_enabled_roles;
|
||||
|
||||
Strings getCurrentRolesNames() const;
|
||||
|
@ -1,22 +0,0 @@
|
||||
#include <Access/GrantedAccess.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
GrantedAccess::GrantsAndPartialRevokes GrantedAccess::getGrantsAndPartialRevokes() const
|
||||
{
|
||||
GrantsAndPartialRevokes res;
|
||||
res.grants_with_grant_option = access_with_grant_option.getGrants();
|
||||
AccessRights access_without_gg = access;
|
||||
access_without_gg.revoke(res.grants_with_grant_option);
|
||||
auto gr = access_without_gg.getGrantsAndPartialRevokes();
|
||||
res.grants = std::move(gr.grants);
|
||||
res.revokes = std::move(gr.revokes);
|
||||
AccessRights access_with_grant_options_without_r = access_with_grant_option;
|
||||
access_with_grant_options_without_r.grant(res.revokes);
|
||||
res.revokes_grant_option = access_with_grant_options_without_r.getPartialRevokes();
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Access/AccessRights.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
/// Access rights as they are granted to a role or user.
|
||||
/// Stores both the access rights themselves and the access rights with grant option.
|
||||
struct GrantedAccess
|
||||
{
|
||||
AccessRights access;
|
||||
AccessRights access_with_grant_option;
|
||||
|
||||
template <typename... Args>
|
||||
void grant(const Args &... args)
|
||||
{
|
||||
access.grant(args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void grantWithGrantOption(const Args &... args)
|
||||
{
|
||||
access.grant(args...);
|
||||
access_with_grant_option.grant(args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void revoke(const Args &... args)
|
||||
{
|
||||
access.revoke(args...);
|
||||
access_with_grant_option.revoke(args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void revokeGrantOption(const Args &... args)
|
||||
{
|
||||
access_with_grant_option.revoke(args...);
|
||||
}
|
||||
|
||||
struct GrantsAndPartialRevokes
|
||||
{
|
||||
AccessRightsElements grants;
|
||||
AccessRightsElements revokes;
|
||||
AccessRightsElements grants_with_grant_option;
|
||||
AccessRightsElements revokes_grant_option;
|
||||
};
|
||||
|
||||
/// Retrieves the information about grants and partial revokes.
|
||||
GrantsAndPartialRevokes getGrantsAndPartialRevokes() const;
|
||||
|
||||
friend bool operator ==(const GrantedAccess & left, const GrantedAccess & right) { return (left.access == right.access) && (left.access_with_grant_option == right.access_with_grant_option); }
|
||||
friend bool operator !=(const GrantedAccess & left, const GrantedAccess & right) { return !(left == right); }
|
||||
};
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Access/IAccessEntity.h>
|
||||
#include <Access/GrantedAccess.h>
|
||||
#include <Access/AccessRights.h>
|
||||
#include <Access/GrantedRoles.h>
|
||||
#include <Access/SettingsProfileElement.h>
|
||||
|
||||
@ -11,7 +11,7 @@ namespace DB
|
||||
|
||||
struct Role : public IAccessEntity
|
||||
{
|
||||
GrantedAccess access;
|
||||
AccessRights access;
|
||||
GrantedRoles granted_roles;
|
||||
SettingsProfileElements settings;
|
||||
|
||||
|
@ -43,8 +43,7 @@ namespace
|
||||
roles_info.enabled_roles_with_admin_option.emplace(role_id);
|
||||
|
||||
roles_info.names_of_roles[role_id] = role->getName();
|
||||
roles_info.access.merge(role->access.access);
|
||||
roles_info.access_with_grant_option.merge(role->access.access_with_grant_option);
|
||||
roles_info.access.merge(role->access);
|
||||
roles_info.settings_from_enabled_roles.merge(role->settings);
|
||||
|
||||
for (const auto & granted_role : role->granted_roles.roles)
|
||||
|
@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <Access/IAccessEntity.h>
|
||||
#include <Access/AccessRights.h>
|
||||
#include <Access/Authentication.h>
|
||||
#include <Access/AllowedClientHosts.h>
|
||||
#include <Access/GrantedAccess.h>
|
||||
#include <Access/GrantedRoles.h>
|
||||
#include <Access/RolesOrUsersSet.h>
|
||||
#include <Access/SettingsProfileElement.h>
|
||||
@ -17,7 +17,7 @@ struct User : public IAccessEntity
|
||||
{
|
||||
Authentication authentication;
|
||||
AllowedClientHosts allowed_client_hosts = AllowedClientHosts::AnyHostTag{};
|
||||
GrantedAccess access;
|
||||
AccessRights access;
|
||||
GrantedRoles granted_roles;
|
||||
RolesOrUsersSet default_roles = RolesOrUsersSet::AllTag{};
|
||||
SettingsProfileElements settings;
|
||||
|
@ -17,7 +17,6 @@ SRCS(
|
||||
EnabledRolesInfo.cpp
|
||||
EnabledRowPolicies.cpp
|
||||
EnabledSettings.cpp
|
||||
GrantedAccess.cpp
|
||||
GrantedRoles.cpp
|
||||
IAccessEntity.cpp
|
||||
IAccessStorage.cpp
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <Interpreters/AddDefaultDatabaseVisitor.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Access/AccessRightsElement.h>
|
||||
#include <Access/ContextAccess.h>
|
||||
#include <Common/DNSResolver.h>
|
||||
#include <Common/Macros.h>
|
||||
#include <common/getFQDNOrHostName.h>
|
||||
@ -1278,7 +1279,7 @@ private:
|
||||
};
|
||||
|
||||
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & context, AccessRightsElements && query_required_access)
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & context, AccessRightsElements && query_requires_access, bool query_requires_grant_option)
|
||||
{
|
||||
/// Remove FORMAT <fmt> and INTO OUTFILE <file> if exists
|
||||
ASTPtr query_ptr = query_ptr_->clone();
|
||||
@ -1323,10 +1324,10 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & cont
|
||||
/// the local current database or a shard's default database.
|
||||
bool need_replace_current_database
|
||||
= (std::find_if(
|
||||
query_required_access.begin(),
|
||||
query_required_access.end(),
|
||||
query_requires_access.begin(),
|
||||
query_requires_access.end(),
|
||||
[](const AccessRightsElement & elem) { return elem.isEmptyDatabase(); })
|
||||
!= query_required_access.end());
|
||||
!= query_requires_access.end());
|
||||
|
||||
if (need_replace_current_database)
|
||||
{
|
||||
@ -1355,29 +1356,31 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & cont
|
||||
AddDefaultDatabaseVisitor visitor(current_database);
|
||||
visitor.visitDDL(query_ptr);
|
||||
|
||||
query_required_access.replaceEmptyDatabase(current_database);
|
||||
query_requires_access.replaceEmptyDatabase(current_database);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t old_num_elements = query_required_access.size();
|
||||
for (size_t i = 0; i != old_num_elements; ++i)
|
||||
for (size_t i = 0; i != query_requires_access.size();)
|
||||
{
|
||||
auto & element = query_required_access[i];
|
||||
auto & element = query_requires_access[i];
|
||||
if (element.isEmptyDatabase())
|
||||
{
|
||||
element.setDatabase(shard_default_databases[0]);
|
||||
for (size_t j = 1; j != shard_default_databases.size(); ++j)
|
||||
{
|
||||
query_required_access.push_back(element);
|
||||
query_required_access.back().setDatabase(shard_default_databases[j]);
|
||||
}
|
||||
query_requires_access.insert(query_requires_access.begin() + i + 1, shard_default_databases.size() - 1, element);
|
||||
for (size_t j = 0; j != shard_default_databases.size(); ++j)
|
||||
query_requires_access[i + j].replaceEmptyDatabase(shard_default_databases[j]);
|
||||
i += shard_default_databases.size();
|
||||
}
|
||||
else
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check access rights, assume that all servers have the same users config
|
||||
context.checkAccess(query_required_access);
|
||||
if (query_requires_grant_option)
|
||||
context.getAccess()->checkGrantOption(query_requires_access);
|
||||
else
|
||||
context.checkAccess(query_requires_access);
|
||||
|
||||
DDLLogEntry entry;
|
||||
entry.hosts = std::move(hosts);
|
||||
@ -1394,6 +1397,10 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & cont
|
||||
return io;
|
||||
}
|
||||
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, const AccessRightsElements & query_requires_access, bool query_requires_grant_option)
|
||||
{
|
||||
return executeDDLQueryOnCluster(query_ptr, context, AccessRightsElements{query_requires_access}, query_requires_grant_option);
|
||||
}
|
||||
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & context)
|
||||
{
|
||||
|
@ -29,8 +29,9 @@ struct DDLTask;
|
||||
|
||||
|
||||
/// Pushes distributed DDL query to the queue
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, AccessRightsElements && query_required_access);
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context);
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, const AccessRightsElements & query_requires_access, bool query_requires_grant_option = false);
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, AccessRightsElements && query_requires_access, bool query_requires_grant_option = false);
|
||||
|
||||
|
||||
class DDLWorker
|
||||
|
@ -16,7 +16,7 @@ namespace DB
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
void updateFromQueryImpl(T & grantee, const ASTGrantQuery & query, const std::vector<UUID> & roles_from_query, const String & current_database)
|
||||
void updateFromQueryImpl(T & grantee, const ASTGrantQuery & query, const std::vector<UUID> & roles_from_query)
|
||||
{
|
||||
using Kind = ASTGrantQuery::Kind;
|
||||
if (!query.access_rights_elements.empty())
|
||||
@ -24,16 +24,16 @@ namespace
|
||||
if (query.kind == Kind::GRANT)
|
||||
{
|
||||
if (query.grant_option)
|
||||
grantee.access.grantWithGrantOption(query.access_rights_elements, current_database);
|
||||
grantee.access.grantWithGrantOption(query.access_rights_elements);
|
||||
else
|
||||
grantee.access.grant(query.access_rights_elements, current_database);
|
||||
grantee.access.grant(query.access_rights_elements);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (query.grant_option)
|
||||
grantee.access.revokeGrantOption(query.access_rights_elements, current_database);
|
||||
grantee.access.revokeGrantOption(query.access_rights_elements);
|
||||
else
|
||||
grantee.access.revoke(query.access_rights_elements, current_database);
|
||||
grantee.access.revoke(query.access_rights_elements);
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,9 +67,9 @@ namespace
|
||||
BlockIO InterpreterGrantQuery::execute()
|
||||
{
|
||||
auto & query = query_ptr->as<ASTGrantQuery &>();
|
||||
auto & access_control = context.getAccessControlManager();
|
||||
query.replaceCurrentUserTagWithName(context.getUserName());
|
||||
auto access = context.getAccess();
|
||||
access->checkGrantOption(query.access_rights_elements);
|
||||
auto & access_control = context.getAccessControlManager();
|
||||
|
||||
std::vector<UUID> roles_from_query;
|
||||
if (query.roles)
|
||||
@ -80,25 +80,24 @@ BlockIO InterpreterGrantQuery::execute()
|
||||
}
|
||||
|
||||
if (!query.cluster.empty())
|
||||
{
|
||||
query.replaceCurrentUserTagWithName(context.getUserName());
|
||||
return executeDDLQueryOnCluster(query_ptr, context);
|
||||
}
|
||||
return executeDDLQueryOnCluster(query_ptr, context, query.access_rights_elements, true);
|
||||
|
||||
query.replaceEmptyDatabaseWithCurrent(context.getCurrentDatabase());
|
||||
access->checkGrantOption(query.access_rights_elements);
|
||||
|
||||
std::vector<UUID> to_roles = RolesOrUsersSet{*query.to_roles, access_control, context.getUserID()}.getMatchingIDs(access_control);
|
||||
String current_database = context.getCurrentDatabase();
|
||||
|
||||
auto update_func = [&](const AccessEntityPtr & entity) -> AccessEntityPtr
|
||||
{
|
||||
auto clone = entity->clone();
|
||||
if (auto user = typeid_cast<std::shared_ptr<User>>(clone))
|
||||
{
|
||||
updateFromQueryImpl(*user, query, roles_from_query, current_database);
|
||||
updateFromQueryImpl(*user, query, roles_from_query);
|
||||
return user;
|
||||
}
|
||||
else if (auto role = typeid_cast<std::shared_ptr<Role>>(clone))
|
||||
{
|
||||
updateFromQueryImpl(*role, query, roles_from_query, current_database);
|
||||
updateFromQueryImpl(*role, query, roles_from_query);
|
||||
return role;
|
||||
}
|
||||
else
|
||||
@ -116,7 +115,7 @@ void InterpreterGrantQuery::updateUserFromQuery(User & user, const ASTGrantQuery
|
||||
std::vector<UUID> roles_from_query;
|
||||
if (query.roles)
|
||||
roles_from_query = RolesOrUsersSet{*query.roles}.getMatchingIDs();
|
||||
updateFromQueryImpl(user, query, roles_from_query, {});
|
||||
updateFromQueryImpl(user, query, roles_from_query);
|
||||
}
|
||||
|
||||
|
||||
@ -125,7 +124,7 @@ void InterpreterGrantQuery::updateRoleFromQuery(Role & role, const ASTGrantQuery
|
||||
std::vector<UUID> roles_from_query;
|
||||
if (query.roles)
|
||||
roles_from_query = RolesOrUsersSet{*query.roles}.getMatchingIDs();
|
||||
updateFromQueryImpl(role, query, roles_from_query, {});
|
||||
updateFromQueryImpl(role, query, roles_from_query);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -84,19 +84,20 @@ static QueryDescriptors extractQueriesExceptMeAndCheckAccess(const Block & proce
|
||||
const ColumnString & user_col = typeid_cast<const ColumnString &>(*processes_block.getByName("user").column);
|
||||
const ClientInfo & my_client = context.getProcessListElement()->getClientInfo();
|
||||
|
||||
std::optional<bool> can_kill_query_started_by_another_user_cached;
|
||||
auto can_kill_query_started_by_another_user = [&]() -> bool
|
||||
bool access_denied = false;
|
||||
std::optional<bool> is_kill_query_granted_value;
|
||||
auto is_kill_query_granted = [&]() -> bool
|
||||
{
|
||||
if (!can_kill_query_started_by_another_user_cached)
|
||||
if (!is_kill_query_granted_value)
|
||||
{
|
||||
can_kill_query_started_by_another_user_cached
|
||||
= context.getAccess()->isGranted(&Poco::Logger::get("InterpreterKillQueryQuery"), AccessType::KILL_QUERY);
|
||||
is_kill_query_granted_value = context.getAccess()->isGranted(AccessType::KILL_QUERY);
|
||||
if (!*is_kill_query_granted_value)
|
||||
access_denied = true;
|
||||
}
|
||||
return *can_kill_query_started_by_another_user_cached;
|
||||
return *is_kill_query_granted_value;
|
||||
};
|
||||
|
||||
String query_user;
|
||||
bool access_denied = false;
|
||||
|
||||
for (size_t i = 0; i < num_processes; ++i)
|
||||
{
|
||||
@ -107,11 +108,8 @@ static QueryDescriptors extractQueriesExceptMeAndCheckAccess(const Block & proce
|
||||
auto query_id = query_id_col.getDataAt(i).toString();
|
||||
query_user = user_col.getDataAt(i).toString();
|
||||
|
||||
if ((my_client.current_user != query_user) && !can_kill_query_started_by_another_user())
|
||||
{
|
||||
access_denied = true;
|
||||
if ((my_client.current_user != query_user) && !is_kill_query_granted())
|
||||
continue;
|
||||
}
|
||||
|
||||
res.emplace_back(std::move(query_id), query_user, i, false);
|
||||
}
|
||||
@ -269,7 +267,7 @@ BlockIO InterpreterKillQueryQuery::execute()
|
||||
ParserAlterCommand parser;
|
||||
auto command_ast = parseQuery(parser, command_col.getDataAt(i).toString(), 0, context.getSettingsRef().max_parser_depth);
|
||||
required_access_rights = InterpreterAlterQuery::getRequiredAccessForCommand(command_ast->as<const ASTAlterCommand &>(), table_id.database_name, table_id.table_name);
|
||||
if (!access->isGranted(&Poco::Logger::get("InterpreterKillQueryQuery"), required_access_rights))
|
||||
if (!access->isGranted(required_access_rights))
|
||||
{
|
||||
access_denied = true;
|
||||
continue;
|
||||
|
@ -35,44 +35,33 @@ namespace
|
||||
std::shared_ptr<ASTRolesOrUsersSet> to_roles = std::make_shared<ASTRolesOrUsersSet>();
|
||||
to_roles->names.push_back(grantee.getName());
|
||||
|
||||
auto grants_and_partial_revokes = grantee.access.getGrantsAndPartialRevokes();
|
||||
std::shared_ptr<ASTGrantQuery> current_query = nullptr;
|
||||
|
||||
for (bool grant_option : {false, true})
|
||||
auto elements = grantee.access.getElements();
|
||||
for (const auto & element : elements)
|
||||
{
|
||||
using Kind = ASTGrantQuery::Kind;
|
||||
for (Kind kind : {Kind::GRANT, Kind::REVOKE})
|
||||
if (current_query)
|
||||
{
|
||||
AccessRightsElements * elements = nullptr;
|
||||
if (grant_option)
|
||||
elements = (kind == Kind::GRANT) ? &grants_and_partial_revokes.grants_with_grant_option : &grants_and_partial_revokes.revokes_grant_option;
|
||||
else
|
||||
elements = (kind == Kind::GRANT) ? &grants_and_partial_revokes.grants : &grants_and_partial_revokes.revokes;
|
||||
elements->normalize();
|
||||
|
||||
std::shared_ptr<ASTGrantQuery> grant_query = nullptr;
|
||||
for (size_t i = 0; i != elements->size(); ++i)
|
||||
{
|
||||
const auto & element = (*elements)[i];
|
||||
bool prev_element_on_same_db_and_table = false;
|
||||
if (grant_query)
|
||||
{
|
||||
const auto & prev_element = grant_query->access_rights_elements.back();
|
||||
if ((element.database == prev_element.database) && (element.any_database == prev_element.any_database)
|
||||
&& (element.table == prev_element.table) && (element.any_table == prev_element.any_table))
|
||||
prev_element_on_same_db_and_table = true;
|
||||
}
|
||||
if (!prev_element_on_same_db_and_table)
|
||||
{
|
||||
grant_query = std::make_shared<ASTGrantQuery>();
|
||||
grant_query->kind = kind;
|
||||
grant_query->attach = attach_mode;
|
||||
grant_query->grant_option = grant_option;
|
||||
grant_query->to_roles = to_roles;
|
||||
res.push_back(grant_query);
|
||||
}
|
||||
grant_query->access_rights_elements.emplace_back(std::move(element));
|
||||
}
|
||||
const auto & prev_element = current_query->access_rights_elements.back();
|
||||
bool continue_using_current_query = (element.database == prev_element.database)
|
||||
&& (element.any_database == prev_element.any_database) && (element.table == prev_element.table)
|
||||
&& (element.any_table == prev_element.any_table) && (element.grant_option == current_query->grant_option)
|
||||
&& (element.kind == current_query->kind);
|
||||
if (!continue_using_current_query)
|
||||
current_query = nullptr;
|
||||
}
|
||||
|
||||
if (!current_query)
|
||||
{
|
||||
current_query = std::make_shared<ASTGrantQuery>();
|
||||
current_query->kind = element.kind;
|
||||
current_query->attach = attach_mode;
|
||||
current_query->grant_option = element.grant_option;
|
||||
current_query->to_roles = to_roles;
|
||||
res.push_back(current_query);
|
||||
}
|
||||
|
||||
current_query->access_rights_elements.emplace_back(std::move(element));
|
||||
}
|
||||
|
||||
auto grants_roles = grantee.granted_roles.getGrants();
|
||||
|
@ -152,8 +152,16 @@ void InterpreterSystemQuery::startStopAction(StorageActionBlockType action_type,
|
||||
if (!table)
|
||||
continue;
|
||||
|
||||
if (!access->isGranted(log, getRequiredAccessType(action_type), elem.first, iterator->name()))
|
||||
if (!access->isGranted(getRequiredAccessType(action_type), elem.first, iterator->name()))
|
||||
{
|
||||
LOG_INFO(
|
||||
log,
|
||||
"Access {} denied, skipping {}.{}",
|
||||
toString(getRequiredAccessType(action_type)),
|
||||
elem.first,
|
||||
iterator->name());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (start)
|
||||
manager->remove(table, action_type);
|
||||
|
@ -218,7 +218,7 @@ void runOneTest(const TestDescriptor & test_descriptor)
|
||||
|
||||
try
|
||||
{
|
||||
res = acl_manager.read<DB::User>(entry.user_name)->access.access.isGranted(DB::AccessType::ALL, entry.database_name);
|
||||
res = acl_manager.read<DB::User>(entry.user_name)->access.isGranted(DB::AccessType::ALL, entry.database_name);
|
||||
}
|
||||
catch (const Poco::Exception &)
|
||||
{
|
||||
|
@ -133,6 +133,12 @@ void ASTGrantQuery::formatImpl(const FormatSettings & settings, FormatState &, F
|
||||
}
|
||||
|
||||
|
||||
void ASTGrantQuery::replaceEmptyDatabaseWithCurrent(const String & current_database)
|
||||
{
|
||||
access_rights_elements.replaceEmptyDatabase(current_database);
|
||||
}
|
||||
|
||||
|
||||
void ASTGrantQuery::replaceCurrentUserTagWithName(const String & current_user_name) const
|
||||
{
|
||||
if (to_roles)
|
||||
|
@ -19,11 +19,7 @@ class ASTRolesOrUsersSet;
|
||||
class ASTGrantQuery : public IAST, public ASTQueryWithOnCluster
|
||||
{
|
||||
public:
|
||||
enum class Kind
|
||||
{
|
||||
GRANT,
|
||||
REVOKE,
|
||||
};
|
||||
using Kind = AccessRightsElementWithOptions::Kind;
|
||||
Kind kind = Kind::GRANT;
|
||||
bool attach = false;
|
||||
AccessRightsElements access_rights_elements;
|
||||
@ -35,6 +31,7 @@ public:
|
||||
String getID(char) const override;
|
||||
ASTPtr clone() const override;
|
||||
void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
|
||||
void replaceEmptyDatabaseWithCurrent(const String & current_database);
|
||||
void replaceCurrentUserTagWithName(const String & current_user_name) const;
|
||||
ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster<ASTGrantQuery>(clone()); }
|
||||
};
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <Columns/ColumnNullable.h>
|
||||
#include <Columns/ColumnsNumber.h>
|
||||
#include <Access/AccessControlManager.h>
|
||||
#include <Access/AccessRightsElement.h>
|
||||
#include <Access/Role.h>
|
||||
#include <Access/User.h>
|
||||
#include <Interpreters/Context.h>
|
||||
@ -17,7 +18,7 @@
|
||||
namespace DB
|
||||
{
|
||||
using EntityType = IAccessEntity::Type;
|
||||
|
||||
using Kind = AccessRightsElementWithOptions::Kind;
|
||||
|
||||
NamesAndTypesList StorageSystemGrants::getNamesAndTypes()
|
||||
{
|
||||
@ -63,7 +64,7 @@ void StorageSystemGrants::fillData(MutableColumns & res_columns, const Context &
|
||||
const String * database,
|
||||
const String * table,
|
||||
const String * column,
|
||||
bool is_partial_revoke,
|
||||
Kind kind,
|
||||
bool grant_option)
|
||||
{
|
||||
if (grantee_type == EntityType::USER)
|
||||
@ -118,15 +119,13 @@ void StorageSystemGrants::fillData(MutableColumns & res_columns, const Context &
|
||||
column_column_null_map.push_back(true);
|
||||
}
|
||||
|
||||
column_is_partial_revoke.push_back(is_partial_revoke);
|
||||
column_is_partial_revoke.push_back(kind == Kind::REVOKE);
|
||||
column_grant_option.push_back(grant_option);
|
||||
};
|
||||
|
||||
auto add_rows = [&](const String & grantee_name,
|
||||
IAccessEntity::Type grantee_type,
|
||||
const AccessRightsElements & elements,
|
||||
bool is_partial_revoke,
|
||||
bool grant_option)
|
||||
const AccessRightsElementsWithOptions & elements)
|
||||
{
|
||||
for (const auto & element : elements)
|
||||
{
|
||||
@ -140,13 +139,13 @@ void StorageSystemGrants::fillData(MutableColumns & res_columns, const Context &
|
||||
if (element.any_column)
|
||||
{
|
||||
for (const auto & access_type : access_types)
|
||||
add_row(grantee_name, grantee_type, access_type, database, table, nullptr, is_partial_revoke, grant_option);
|
||||
add_row(grantee_name, grantee_type, access_type, database, table, nullptr, element.kind, element.grant_option);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto & access_type : access_types)
|
||||
for (const auto & column : element.columns)
|
||||
add_row(grantee_name, grantee_type, access_type, database, table, &column, is_partial_revoke, grant_option);
|
||||
add_row(grantee_name, grantee_type, access_type, database, table, &column, element.kind, element.grant_option);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -157,7 +156,7 @@ void StorageSystemGrants::fillData(MutableColumns & res_columns, const Context &
|
||||
if (!entity)
|
||||
continue;
|
||||
|
||||
const GrantedAccess * access = nullptr;
|
||||
const AccessRights * access = nullptr;
|
||||
if (auto role = typeid_cast<RolePtr>(entity))
|
||||
access = &role->access;
|
||||
else if (auto user = typeid_cast<UserPtr>(entity))
|
||||
@ -167,13 +166,8 @@ void StorageSystemGrants::fillData(MutableColumns & res_columns, const Context &
|
||||
|
||||
const String & grantee_name = entity->getName();
|
||||
const auto grantee_type = entity->getType();
|
||||
auto grants_and_revokes = access->access.getGrantsAndPartialRevokes();
|
||||
auto grants_and_revokes_with_grant_option = access->access_with_grant_option.getGrantsAndPartialRevokes();
|
||||
|
||||
add_rows(grantee_name, grantee_type, grants_and_revokes.grants, /* is_partial_revoke = */ false, /* grant_option = */ false);
|
||||
add_rows(grantee_name, grantee_type, grants_and_revokes.revokes, /* is_partial_revoke = */ true, /* grant_option = */ false);
|
||||
add_rows(grantee_name, grantee_type, grants_and_revokes_with_grant_option.grants, /* is_partial_revoke = */ false, /* grant_option = */ true);
|
||||
add_rows(grantee_name, grantee_type, grants_and_revokes_with_grant_option.revokes, /* is_partial_revoke = */ true, /* grant_option = */ true);
|
||||
auto elements = access->getElements();
|
||||
add_rows(grantee_name, grantee_type, elements);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,6 @@ def test_introspection():
|
||||
|
||||
assert instance.query("SELECT * from system.grants WHERE user_name IN ('A', 'B') ORDER BY user_name, access_type, grant_option") ==\
|
||||
TSV([[ "A", "\N", "SELECT", "test", "table", "\N", 0, 0 ],
|
||||
[ "B", "\N", "CREATE", "\N", "\N", "\N", 0, 0 ],
|
||||
[ "B", "\N", "CREATE", "\N", "\N", "\N", 0, 1 ]])
|
||||
|
||||
|
||||
|
@ -140,7 +140,6 @@ def test_introspection():
|
||||
|
||||
assert instance.query("SELECT * from system.grants WHERE user_name IN ('A', 'B') OR role_name IN ('R1', 'R2') ORDER BY user_name, role_name, access_type, grant_option") ==\
|
||||
TSV([[ "A", "\N", "SELECT", "test", "table", "\N", 0, 0 ],
|
||||
[ "B", "\N", "CREATE", "\N", "\N", "\N", 0, 0 ],
|
||||
[ "B", "\N", "CREATE", "\N", "\N", "\N", 0, 1 ],
|
||||
[ "\N", "R2", "SELECT", "test", "table", "\N", 0, 0 ],
|
||||
[ "\N", "R2", "SELECT", "test", "table", "x", 1, 0 ]])
|
||||
|
@ -1,11 +1,11 @@
|
||||
CREATE USER test_user_01073
|
||||
A
|
||||
B
|
||||
GRANT INSERT, ALTER DELETE ON *.* TO test_user_01073
|
||||
GRANT SELECT ON db1.* TO test_user_01073
|
||||
GRANT SELECT ON db2.table TO test_user_01073
|
||||
GRANT SELECT(col1) ON db3.table TO test_user_01073
|
||||
GRANT SELECT(col1, col2) ON db4.table TO test_user_01073
|
||||
GRANT INSERT, ALTER DELETE ON *.* TO test_user_01073
|
||||
C
|
||||
GRANT SELECT(col1) ON db4.table TO test_user_01073
|
||||
GRANT ALTER DELETE ON *.* TO test_user_01073
|
||||
GRANT SELECT(col1) ON db4.table TO test_user_01073
|
||||
|
@ -1,2 +1,61 @@
|
||||
--simple 1
|
||||
GRANT SELECT ON *.* TO test_user_01074
|
||||
REVOKE SELECT ON db.* FROM test_user_01074
|
||||
--cleanup
|
||||
--simple 2
|
||||
GRANT SELECT ON db.* TO test_user_01074
|
||||
REVOKE SELECT ON db.table FROM test_user_01074
|
||||
--cleanup
|
||||
--simple 3
|
||||
GRANT SELECT ON db.table TO test_user_01074
|
||||
REVOKE SELECT(col1) ON db.table FROM test_user_01074
|
||||
--cleanup
|
||||
--complex 1
|
||||
GRANT SELECT ON *.* TO test_user_01074
|
||||
REVOKE SELECT(col1, col2) ON db.table FROM test_user_01074
|
||||
--cleanup
|
||||
--complex 2
|
||||
GRANT SELECT ON *.* TO test_user_01074
|
||||
REVOKE SELECT ON db.* FROM test_user_01074
|
||||
GRANT SELECT ON db.table TO test_user_01074
|
||||
REVOKE SELECT(col1) ON db.table FROM test_user_01074
|
||||
┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
|
||||
┃ [1muser_name [0m ┃ [1mrole_name[0m ┃ [1maccess_type[0m ┃ [1mdatabase[0m ┃ [1mtable[0m ┃ [1mcolumn[0m ┃ [1mis_partial_revoke[0m ┃ [1mgrant_option[0m ┃
|
||||
┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
|
||||
│ test_user_01074 │ ᴺᵁᴸᴸ │ SELECT │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 0 │ 0 │
|
||||
├─────────────────┼───────────┼─────────────┼──────────┼───────┼────────┼───────────────────┼──────────────┤
|
||||
│ test_user_01074 │ ᴺᵁᴸᴸ │ SELECT │ db │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 1 │ 0 │
|
||||
├─────────────────┼───────────┼─────────────┼──────────┼───────┼────────┼───────────────────┼──────────────┤
|
||||
│ test_user_01074 │ ᴺᵁᴸᴸ │ SELECT │ db │ table │ ᴺᵁᴸᴸ │ 0 │ 0 │
|
||||
├─────────────────┼───────────┼─────────────┼──────────┼───────┼────────┼───────────────────┼──────────────┤
|
||||
│ test_user_01074 │ ᴺᵁᴸᴸ │ SELECT │ db │ table │ col1 │ 1 │ 0 │
|
||||
└─────────────────┴───────────┴─────────────┴──────────┴───────┴────────┴───────────────────┴──────────────┘
|
||||
--cleanup
|
||||
--revoke 1
|
||||
GRANT SELECT ON *.* TO test_user_01074
|
||||
REVOKE SELECT ON db.* FROM test_user_01074
|
||||
--cleanup
|
||||
--revoke 2
|
||||
GRANT SELECT ON *.* TO test_user_01074
|
||||
--cleanup
|
||||
--grant option 1
|
||||
GRANT SELECT ON *.* TO test_user_01074 WITH GRANT OPTION
|
||||
REVOKE GRANT OPTION FOR SELECT(col1) ON db.table FROM test_user_01074
|
||||
┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
|
||||
┃ [1muser_name [0m ┃ [1mrole_name[0m ┃ [1maccess_type[0m ┃ [1mdatabase[0m ┃ [1mtable[0m ┃ [1mcolumn[0m ┃ [1mis_partial_revoke[0m ┃ [1mgrant_option[0m ┃
|
||||
┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
|
||||
│ test_user_01074 │ ᴺᵁᴸᴸ │ SELECT │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 0 │ 1 │
|
||||
├─────────────────┼───────────┼─────────────┼──────────┼───────┼────────┼───────────────────┼──────────────┤
|
||||
│ test_user_01074 │ ᴺᵁᴸᴸ │ SELECT │ db │ table │ col1 │ 1 │ 1 │
|
||||
└─────────────────┴───────────┴─────────────┴──────────┴───────┴────────┴───────────────────┴──────────────┘
|
||||
--cleanup
|
||||
--grant option 2
|
||||
GRANT SELECT ON *.* TO test_user_01074 WITH GRANT OPTION
|
||||
REVOKE SELECT(col1) ON db.table FROM test_user_01074
|
||||
--cleanup
|
||||
--grant option 3
|
||||
GRANT SELECT ON *.* TO test_user_01074
|
||||
--cleanup
|
||||
--grant option 4
|
||||
GRANT SELECT ON *.* TO test_user_01074
|
||||
GRANT SELECT ON db.* TO test_user_01074 WITH GRANT OPTION
|
||||
|
@ -1,8 +1,106 @@
|
||||
DROP USER IF EXISTS test_user_01074;
|
||||
CREATE USER test_user_01074;
|
||||
|
||||
SELECT '--simple 1';
|
||||
GRANT SELECT ON *.* TO test_user_01074;
|
||||
REVOKE SELECT ON db.* FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--cleanup';
|
||||
REVOKE SELECT ON *.* FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--simple 2';
|
||||
GRANT SELECT ON db.* TO test_user_01074;
|
||||
REVOKE SELECT ON db.table FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--cleanup';
|
||||
REVOKE SELECT ON *.* FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--simple 3';
|
||||
GRANT SELECT ON db.table TO test_user_01074;
|
||||
REVOKE SELECT(col1) ON db.table FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--cleanup';
|
||||
REVOKE SELECT ON *.* FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--complex 1';
|
||||
GRANT SELECT ON *.* TO test_user_01074;
|
||||
REVOKE SELECT(col1, col2) ON db.table FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--cleanup';
|
||||
REVOKE SELECT ON *.* FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--complex 2';
|
||||
GRANT SELECT ON *.* TO test_user_01074;
|
||||
REVOKE SELECT ON db.* FROM test_user_01074;
|
||||
GRANT SELECT ON db.table TO test_user_01074;
|
||||
REVOKE SELECT(col1) ON db.table FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
SELECT * FROM system.grants WHERE user_name = 'test_user_01074' format Pretty;
|
||||
|
||||
SELECT '--cleanup';
|
||||
REVOKE SELECT ON *.* FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--revoke 1';
|
||||
GRANT SELECT ON *.* TO test_user_01074;
|
||||
REVOKE SELECT ON db.table FROM test_user_01074;
|
||||
REVOKE SELECT ON db.* FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--cleanup';
|
||||
REVOKE SELECT ON *.* FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--revoke 2';
|
||||
GRANT SELECT ON *.* TO test_user_01074;
|
||||
REVOKE SELECT ON db.table FROM test_user_01074;
|
||||
GRANT SELECT ON db.* TO test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--cleanup';
|
||||
REVOKE SELECT ON *.* FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--grant option 1';
|
||||
GRANT SELECT ON *.* TO test_user_01074 WITH GRANT OPTION;
|
||||
REVOKE GRANT OPTION FOR SELECT(col1) ON db.table FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
SELECT * FROM system.grants WHERE user_name = 'test_user_01074' format Pretty;
|
||||
|
||||
SELECT '--cleanup';
|
||||
REVOKE SELECT ON *.* FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--grant option 2';
|
||||
GRANT SELECT ON *.* TO test_user_01074 WITH GRANT OPTION;
|
||||
REVOKE SELECT(col1) ON db.table FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--cleanup';
|
||||
REVOKE SELECT ON *.* FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--grant option 3';
|
||||
GRANT SELECT ON *.* TO test_user_01074;
|
||||
REVOKE GRANT OPTION FOR SELECT(col1) ON db.table FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--cleanup';
|
||||
REVOKE SELECT ON *.* FROM test_user_01074;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
SELECT '--grant option 4';
|
||||
GRANT SELECT ON *.* TO test_user_01074;
|
||||
REVOKE SELECT ON db.table FROM test_user_01074;
|
||||
GRANT SELECT ON db.* TO test_user_01074 WITH GRANT OPTION;
|
||||
SHOW GRANTS FOR test_user_01074;
|
||||
|
||||
DROP USER test_user_01074;
|
||||
|
Loading…
Reference in New Issue
Block a user