mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
Improvements in implementations of the classes AccessRights and GrantedRoles.
This commit is contained in:
parent
084bd03672
commit
d6e0342c30
@ -403,7 +403,7 @@ void AccessControlManager::checkSettingNameIsAllowed(const std::string_view & se
|
||||
|
||||
std::shared_ptr<const ContextAccess> AccessControlManager::getContextAccess(
|
||||
const UUID & user_id,
|
||||
const boost::container::flat_set<UUID> & current_roles,
|
||||
const std::vector<UUID> & current_roles,
|
||||
bool use_default_roles,
|
||||
const Settings & settings,
|
||||
const String & current_database,
|
||||
@ -411,7 +411,7 @@ std::shared_ptr<const ContextAccess> AccessControlManager::getContextAccess(
|
||||
{
|
||||
ContextAccessParams params;
|
||||
params.user_id = user_id;
|
||||
params.current_roles = current_roles;
|
||||
params.current_roles.insert(current_roles.begin(), current_roles.end());
|
||||
params.use_default_roles = use_default_roles;
|
||||
params.current_database = current_database;
|
||||
params.readonly = settings.readonly;
|
||||
@ -444,8 +444,8 @@ std::shared_ptr<const ContextAccess> AccessControlManager::getContextAccess(cons
|
||||
|
||||
|
||||
std::shared_ptr<const EnabledRoles> AccessControlManager::getEnabledRoles(
|
||||
const boost::container::flat_set<UUID> & current_roles,
|
||||
const boost::container::flat_set<UUID> & current_roles_with_admin_option) const
|
||||
const std::vector<UUID> & current_roles,
|
||||
const std::vector<UUID> & current_roles_with_admin_option) const
|
||||
{
|
||||
return role_cache->getEnabledRoles(current_roles, current_roles_with_admin_option);
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ public:
|
||||
|
||||
std::shared_ptr<const ContextAccess> getContextAccess(
|
||||
const UUID & user_id,
|
||||
const boost::container::flat_set<UUID> & current_roles,
|
||||
const std::vector<UUID> & current_roles,
|
||||
bool use_default_roles,
|
||||
const Settings & settings,
|
||||
const String & current_database,
|
||||
@ -123,8 +123,8 @@ public:
|
||||
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;
|
||||
const std::vector<UUID> & current_roles,
|
||||
const std::vector<UUID> & current_roles_with_admin_option) const;
|
||||
|
||||
std::shared_ptr<const EnabledRowPolicies> getEnabledRowPolicies(
|
||||
const UUID & user_id,
|
||||
|
@ -7,16 +7,19 @@
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
using Kind = AccessRightsElementWithOptions::Kind;
|
||||
|
||||
struct ProtoElement
|
||||
{
|
||||
AccessFlags access_flags;
|
||||
boost::container::small_vector<std::string_view, 3> full_name;
|
||||
bool grant_option = false;
|
||||
Kind kind = Kind::GRANT;
|
||||
bool is_partial_revoke = false;
|
||||
|
||||
friend bool operator<(const ProtoElement & left, const ProtoElement & right)
|
||||
{
|
||||
@ -43,8 +46,8 @@ namespace
|
||||
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.is_partial_revoke != right.is_partial_revoke)
|
||||
return right.is_partial_revoke;
|
||||
|
||||
if (left.grant_option != right.grant_option)
|
||||
return right.grant_option;
|
||||
@ -55,12 +58,12 @@ namespace
|
||||
return (left.access_flags < right.access_flags);
|
||||
}
|
||||
|
||||
AccessRightsElementWithOptions getResult() const
|
||||
AccessRightsElement getResult() const
|
||||
{
|
||||
AccessRightsElementWithOptions res;
|
||||
AccessRightsElement res;
|
||||
res.access_flags = access_flags;
|
||||
res.grant_option = grant_option;
|
||||
res.kind = kind;
|
||||
res.is_partial_revoke = is_partial_revoke;
|
||||
switch (full_name.size())
|
||||
{
|
||||
case 0:
|
||||
@ -105,11 +108,11 @@ namespace
|
||||
class ProtoElements : public std::vector<ProtoElement>
|
||||
{
|
||||
public:
|
||||
AccessRightsElementsWithOptions getResult() const
|
||||
AccessRightsElements getResult() const
|
||||
{
|
||||
ProtoElements sorted = *this;
|
||||
boost::range::sort(sorted);
|
||||
AccessRightsElementsWithOptions res;
|
||||
AccessRightsElements res;
|
||||
res.reserve(sorted.size());
|
||||
|
||||
for (size_t i = 0; i != sorted.size();)
|
||||
@ -144,7 +147,7 @@ namespace
|
||||
{
|
||||
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);
|
||||
|| (element.is_partial_revoke != start_element.is_partial_revoke);
|
||||
});
|
||||
|
||||
return it - (begin() + start);
|
||||
@ -153,7 +156,7 @@ namespace
|
||||
/// 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
|
||||
void appendResultWithElementsWithDifferenceInColumnOnly(size_t start, size_t count, AccessRightsElements & res) const
|
||||
{
|
||||
const auto * pbegin = data() + start;
|
||||
const auto * pend = pbegin + count;
|
||||
@ -180,7 +183,7 @@ namespace
|
||||
res.emplace_back();
|
||||
auto & back = res.back();
|
||||
back.grant_option = pbegin->grant_option;
|
||||
back.kind = pbegin->kind;
|
||||
back.is_partial_revoke = pbegin->is_partial_revoke;
|
||||
back.any_database = false;
|
||||
back.database = pbegin->full_name[0];
|
||||
back.any_table = false;
|
||||
@ -515,10 +518,10 @@ private:
|
||||
auto grants = flags - parent_fl;
|
||||
|
||||
if (revokes)
|
||||
res.push_back(ProtoElement{revokes, full_name, false, Kind::REVOKE});
|
||||
res.push_back(ProtoElement{revokes, full_name, false, true});
|
||||
|
||||
if (grants)
|
||||
res.push_back(ProtoElement{grants, full_name, false, Kind::GRANT});
|
||||
res.push_back(ProtoElement{grants, full_name, false, false});
|
||||
|
||||
if (node.children)
|
||||
{
|
||||
@ -550,16 +553,16 @@ private:
|
||||
auto grants = flags - parent_fl - grants_go;
|
||||
|
||||
if (revokes)
|
||||
res.push_back(ProtoElement{revokes, full_name, false, Kind::REVOKE});
|
||||
res.push_back(ProtoElement{revokes, full_name, false, true});
|
||||
|
||||
if (revokes_go)
|
||||
res.push_back(ProtoElement{revokes_go, full_name, true, Kind::REVOKE});
|
||||
res.push_back(ProtoElement{revokes_go, full_name, true, true});
|
||||
|
||||
if (grants)
|
||||
res.push_back(ProtoElement{grants, full_name, false, Kind::GRANT});
|
||||
res.push_back(ProtoElement{grants, full_name, false, false});
|
||||
|
||||
if (grants_go)
|
||||
res.push_back(ProtoElement{grants_go, full_name, true, Kind::GRANT});
|
||||
res.push_back(ProtoElement{grants_go, full_name, true, false});
|
||||
|
||||
if (node && node->children)
|
||||
{
|
||||
@ -774,8 +777,10 @@ void AccessRights::grantImpl(const AccessFlags & flags, const Args &... args)
|
||||
}
|
||||
|
||||
template <bool with_grant_option>
|
||||
void AccessRights::grantImpl(const AccessRightsElement & element)
|
||||
void AccessRights::grantImplHelper(const AccessRightsElement & element)
|
||||
{
|
||||
assert(!element.is_partial_revoke);
|
||||
assert(!element.grant_option || with_grant_option);
|
||||
if (element.any_database)
|
||||
grantImpl<with_grant_option>(element.access_flags);
|
||||
else if (element.any_table)
|
||||
@ -786,6 +791,24 @@ void AccessRights::grantImpl(const AccessRightsElement & element)
|
||||
grantImpl<with_grant_option>(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
|
||||
template <bool with_grant_option>
|
||||
void AccessRights::grantImpl(const AccessRightsElement & element)
|
||||
{
|
||||
if (element.is_partial_revoke)
|
||||
throw Exception("A partial revoke should be revoked, not granted", ErrorCodes::BAD_ARGUMENTS);
|
||||
if constexpr (with_grant_option)
|
||||
{
|
||||
grantImplHelper<true>(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (element.grant_option)
|
||||
grantImplHelper<true>(element);
|
||||
else
|
||||
grantImplHelper<false>(element);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool with_grant_option>
|
||||
void AccessRights::grantImpl(const AccessRightsElements & elements)
|
||||
{
|
||||
@ -830,8 +853,9 @@ void AccessRights::revokeImpl(const AccessFlags & flags, const Args &... args)
|
||||
}
|
||||
|
||||
template <bool grant_option>
|
||||
void AccessRights::revokeImpl(const AccessRightsElement & element)
|
||||
void AccessRights::revokeImplHelper(const AccessRightsElement & element)
|
||||
{
|
||||
assert(!element.grant_option || grant_option);
|
||||
if (element.any_database)
|
||||
revokeImpl<grant_option>(element.access_flags);
|
||||
else if (element.any_table)
|
||||
@ -842,6 +866,22 @@ void AccessRights::revokeImpl(const AccessRightsElement & element)
|
||||
revokeImpl<grant_option>(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
|
||||
template <bool grant_option>
|
||||
void AccessRights::revokeImpl(const AccessRightsElement & element)
|
||||
{
|
||||
if constexpr (grant_option)
|
||||
{
|
||||
revokeImplHelper<true>(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (element.grant_option)
|
||||
revokeImplHelper<true>(element);
|
||||
else
|
||||
revokeImplHelper<false>(element);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool grant_option>
|
||||
void AccessRights::revokeImpl(const AccessRightsElements & elements)
|
||||
{
|
||||
@ -868,7 +908,7 @@ void AccessRights::revokeGrantOption(const AccessRightsElement & element) { revo
|
||||
void AccessRights::revokeGrantOption(const AccessRightsElements & elements) { revokeImpl<true>(elements); }
|
||||
|
||||
|
||||
AccessRightsElementsWithOptions AccessRights::getElements() const
|
||||
AccessRightsElements AccessRights::getElements() const
|
||||
{
|
||||
#if 0
|
||||
logTree();
|
||||
@ -903,8 +943,9 @@ bool AccessRights::isGrantedImpl(const AccessFlags & flags, const Args &... args
|
||||
}
|
||||
|
||||
template <bool grant_option>
|
||||
bool AccessRights::isGrantedImpl(const AccessRightsElement & element) const
|
||||
bool AccessRights::isGrantedImplHelper(const AccessRightsElement & element) const
|
||||
{
|
||||
assert(!element.grant_option || grant_option);
|
||||
if (element.any_database)
|
||||
return isGrantedImpl<grant_option>(element.access_flags);
|
||||
else if (element.any_table)
|
||||
@ -915,6 +956,22 @@ bool AccessRights::isGrantedImpl(const AccessRightsElement & element) const
|
||||
return isGrantedImpl<grant_option>(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
|
||||
template <bool grant_option>
|
||||
bool AccessRights::isGrantedImpl(const AccessRightsElement & element) const
|
||||
{
|
||||
if constexpr (grant_option)
|
||||
{
|
||||
return isGrantedImplHelper<true>(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (element.grant_option)
|
||||
return isGrantedImplHelper<true>(element);
|
||||
else
|
||||
return isGrantedImplHelper<false>(element);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool grant_option>
|
||||
bool AccessRights::isGrantedImpl(const AccessRightsElements & elements) const
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ public:
|
||||
String toString() const;
|
||||
|
||||
/// Returns the information about all the access granted.
|
||||
AccessRightsElementsWithOptions getElements() const;
|
||||
AccessRightsElements getElements() const;
|
||||
|
||||
/// Grants access on a specified database/table/column.
|
||||
/// Does nothing if the specified access has been already granted.
|
||||
@ -119,12 +119,15 @@ private:
|
||||
template <bool with_grant_option, typename... Args>
|
||||
void grantImpl(const AccessFlags & flags, const Args &... args);
|
||||
|
||||
template <bool with_grant_options>
|
||||
template <bool with_grant_option>
|
||||
void grantImpl(const AccessRightsElement & element);
|
||||
|
||||
template <bool with_grant_options>
|
||||
template <bool with_grant_option>
|
||||
void grantImpl(const AccessRightsElements & elements);
|
||||
|
||||
template <bool with_grant_option>
|
||||
void grantImplHelper(const AccessRightsElement & element);
|
||||
|
||||
template <bool grant_option, typename... Args>
|
||||
void revokeImpl(const AccessFlags & flags, const Args &... args);
|
||||
|
||||
@ -134,6 +137,9 @@ private:
|
||||
template <bool grant_option>
|
||||
void revokeImpl(const AccessRightsElements & elements);
|
||||
|
||||
template <bool grant_option>
|
||||
void revokeImplHelper(const AccessRightsElement & element);
|
||||
|
||||
template <bool grant_option, typename... Args>
|
||||
bool isGrantedImpl(const AccessFlags & flags, const Args &... args) const;
|
||||
|
||||
@ -143,6 +149,9 @@ private:
|
||||
template <bool grant_option>
|
||||
bool isGrantedImpl(const AccessRightsElements & elements) const;
|
||||
|
||||
template <bool grant_option>
|
||||
bool isGrantedImplHelper(const AccessRightsElement & element) const;
|
||||
|
||||
void logTree() const;
|
||||
|
||||
struct Node;
|
||||
|
@ -1,169 +1,162 @@
|
||||
#include <Access/AccessRightsElement.h>
|
||||
#include <Dictionaries/IDictionary.h>
|
||||
#include <Common/quoteString.h>
|
||||
#include <boost/range/algorithm/sort.hpp>
|
||||
#include <boost/range/algorithm/unique.hpp>
|
||||
#include <boost/range/algorithm_ext/is_sorted.hpp>
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
#include <boost/range/algorithm_ext/push_back.hpp>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Kind = AccessRightsElementWithOptions::Kind;
|
||||
|
||||
String formatOptions(bool grant_option, Kind kind, const String & inner_part)
|
||||
void formatColumnNames(const Strings & columns, String & result)
|
||||
{
|
||||
if (kind == Kind::REVOKE)
|
||||
result += "(";
|
||||
bool need_comma = false;
|
||||
for (const auto & column : columns)
|
||||
{
|
||||
if (grant_option)
|
||||
return "REVOKE GRANT OPTION " + inner_part;
|
||||
else
|
||||
return "REVOKE " + inner_part;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (grant_option)
|
||||
return "GRANT " + inner_part + " WITH GRANT OPTION";
|
||||
else
|
||||
return "GRANT " + inner_part;
|
||||
if (need_comma)
|
||||
result += ", ";
|
||||
need_comma = true;
|
||||
result += backQuoteIfNeed(column);
|
||||
}
|
||||
result += ")";
|
||||
}
|
||||
|
||||
|
||||
String formatONClause(const String & database, bool any_database, const String & table, bool any_table)
|
||||
void formatONClause(const String & database, bool any_database, const String & table, bool any_table, String & result)
|
||||
{
|
||||
String msg = "ON ";
|
||||
|
||||
result += "ON ";
|
||||
if (any_database)
|
||||
msg += "*.";
|
||||
else if (!database.empty())
|
||||
msg += backQuoteIfNeed(database) + ".";
|
||||
|
||||
if (any_table)
|
||||
msg += "*";
|
||||
{
|
||||
result += "*.*";
|
||||
}
|
||||
else
|
||||
msg += backQuoteIfNeed(table);
|
||||
return msg;
|
||||
{
|
||||
if (!database.empty())
|
||||
{
|
||||
result += backQuoteIfNeed(database);
|
||||
result += ".";
|
||||
}
|
||||
if (any_table)
|
||||
result += "*";
|
||||
else
|
||||
result += backQuoteIfNeed(table);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String formatAccessFlagsWithColumns(const AccessFlags & access_flags, const Strings & columns, bool any_column)
|
||||
void formatOptions(bool grant_option, bool is_partial_revoke, String & result)
|
||||
{
|
||||
String columns_in_parentheses;
|
||||
if (is_partial_revoke)
|
||||
{
|
||||
if (grant_option)
|
||||
result.insert(0, "REVOKE GRANT OPTION ");
|
||||
else
|
||||
result.insert(0, "REVOKE ");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (grant_option)
|
||||
result.insert(0, "GRANT ").append(" WITH GRANT OPTION");
|
||||
else
|
||||
result.insert(0, "GRANT ");
|
||||
}
|
||||
}
|
||||
|
||||
void formatAccessFlagsWithColumns(const AccessFlags & access_flags, const Strings & columns, bool any_column, String & result)
|
||||
{
|
||||
String columns_as_str;
|
||||
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);
|
||||
result += "USAGE";
|
||||
return;
|
||||
}
|
||||
columns_in_parentheses += ")";
|
||||
formatColumnNames(columns, columns_as_str);
|
||||
}
|
||||
|
||||
auto keywords = access_flags.toKeywords();
|
||||
if (keywords.empty())
|
||||
return "USAGE";
|
||||
{
|
||||
result += "USAGE";
|
||||
return;
|
||||
}
|
||||
|
||||
String msg;
|
||||
bool need_comma = false;
|
||||
for (const std::string_view & keyword : keywords)
|
||||
{
|
||||
if (!msg.empty())
|
||||
msg += ", ";
|
||||
msg += String{keyword} + columns_in_parentheses;
|
||||
if (need_comma)
|
||||
result.append(", ");
|
||||
need_comma = true;
|
||||
result += keyword;
|
||||
result += columns_as_str;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String AccessRightsElement::toString() const
|
||||
{
|
||||
return formatAccessFlagsWithColumns(access_flags, columns, any_column) + " " + formatONClause(database, any_database, table, any_table);
|
||||
}
|
||||
|
||||
String AccessRightsElementWithOptions::toString() const
|
||||
{
|
||||
return formatOptions(grant_option, kind, AccessRightsElement::toString());
|
||||
}
|
||||
|
||||
String AccessRightsElements::toString() const
|
||||
{
|
||||
if (empty())
|
||||
return "USAGE ON *.*";
|
||||
|
||||
String res;
|
||||
String inner_part;
|
||||
|
||||
for (size_t i = 0; i != size(); ++i)
|
||||
String toStringImpl(const AccessRightsElement & element, bool with_options)
|
||||
{
|
||||
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_table = false;
|
||||
if (i != size() - 1)
|
||||
{
|
||||
const auto & next_element = (*this)[i + 1];
|
||||
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();
|
||||
}
|
||||
String result;
|
||||
formatAccessFlagsWithColumns(element.access_flags, element.columns, element.any_column, result);
|
||||
result += " ";
|
||||
formatONClause(element.database, element.any_database, element.table, element.any_table, result);
|
||||
if (with_options)
|
||||
formatOptions(element.grant_option, element.is_partial_revoke, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
String AccessRightsElementsWithOptions::toString() const
|
||||
{
|
||||
if (empty())
|
||||
return "GRANT USAGE ON *.*";
|
||||
|
||||
String res;
|
||||
String inner_part;
|
||||
|
||||
for (size_t i = 0; i != size(); ++i)
|
||||
String toStringImpl(const AccessRightsElements & elements, bool with_options)
|
||||
{
|
||||
const auto & element = (*this)[i];
|
||||
if (elements.empty())
|
||||
return with_options ? "GRANT USAGE ON *.*" : "USAGE ON *.*";
|
||||
|
||||
if (!inner_part.empty())
|
||||
inner_part += ", ";
|
||||
inner_part += formatAccessFlagsWithColumns(element.access_flags, element.columns, element.any_column);
|
||||
String result;
|
||||
String part;
|
||||
|
||||
bool next_element_uses_same_mode_and_table = false;
|
||||
if (i != size() - 1)
|
||||
for (size_t i = 0; i != elements.size(); ++i)
|
||||
{
|
||||
const auto & next_element = (*this)[i + 1];
|
||||
if (element.sameDatabaseAndTable(next_element) && element.sameOptions(next_element))
|
||||
next_element_uses_same_mode_and_table = true;
|
||||
const auto & element = elements[i];
|
||||
|
||||
if (!part.empty())
|
||||
part += ", ";
|
||||
formatAccessFlagsWithColumns(element.access_flags, element.columns, element.any_column, part);
|
||||
|
||||
bool next_element_uses_same_table_and_options = false;
|
||||
if (i != elements.size() - 1)
|
||||
{
|
||||
const auto & next_element = elements[i + 1];
|
||||
if (element.sameDatabaseAndTable(next_element) && element.sameOptions(next_element))
|
||||
next_element_uses_same_table_and_options = true;
|
||||
}
|
||||
|
||||
if (!next_element_uses_same_table_and_options)
|
||||
{
|
||||
part += " ";
|
||||
formatONClause(element.database, element.any_database, element.table, element.any_table, part);
|
||||
if (with_options)
|
||||
formatOptions(element.grant_option, element.is_partial_revoke, part);
|
||||
if (result.empty())
|
||||
result = std::move(part);
|
||||
else
|
||||
result.append(", ").append(part);
|
||||
part.clear();
|
||||
}
|
||||
}
|
||||
|
||||
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 result;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
String AccessRightsElement::toString() const { return toStringImpl(*this, true); }
|
||||
String AccessRightsElement::toStringWithoutOptions() const { return toStringImpl(*this, false); }
|
||||
String AccessRightsElements::toString() const { return toStringImpl(*this, true); }
|
||||
String AccessRightsElements::toStringWithoutOptions() const { return toStringImpl(*this, false); }
|
||||
|
||||
void AccessRightsElements::eraseNonGrantable()
|
||||
{
|
||||
boost::range::remove_erase_if(*this, [](AccessRightsElement & element)
|
||||
{
|
||||
element.eraseNonGrantable();
|
||||
return element.empty();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,6 +16,8 @@ struct AccessRightsElement
|
||||
bool any_database = true;
|
||||
bool any_table = true;
|
||||
bool any_column = true;
|
||||
bool grant_option = false;
|
||||
bool is_partial_revoke = false;
|
||||
|
||||
AccessRightsElement() = default;
|
||||
AccessRightsElement(const AccessRightsElement &) = default;
|
||||
@ -73,7 +75,7 @@ struct AccessRightsElement
|
||||
|
||||
bool empty() const { return !access_flags || (!any_column && columns.empty()); }
|
||||
|
||||
auto toTuple() const { return std::tie(access_flags, any_database, database, any_table, table, any_column, columns); }
|
||||
auto toTuple() const { return std::tie(access_flags, any_database, database, any_table, table, any_column, columns, grant_option, is_partial_revoke); }
|
||||
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); }
|
||||
|
||||
@ -83,44 +85,36 @@ struct AccessRightsElement
|
||||
&& (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);
|
||||
|
||||
/// Resets flags which cannot be granted.
|
||||
void removeNonGrantableFlags();
|
||||
|
||||
/// Returns a human-readable representation like "SELECT, UPDATE(x, y) ON db.table".
|
||||
String toString() const;
|
||||
};
|
||||
|
||||
|
||||
struct AccessRightsElementWithOptions : public AccessRightsElement
|
||||
{
|
||||
bool grant_option = false;
|
||||
|
||||
enum class Kind
|
||||
bool sameOptions(const AccessRightsElement & other) const
|
||||
{
|
||||
GRANT,
|
||||
REVOKE,
|
||||
};
|
||||
Kind kind = Kind::GRANT;
|
||||
|
||||
bool sameOptions(const AccessRightsElementWithOptions & other) const
|
||||
{
|
||||
return (grant_option == other.grant_option) && (kind == other.kind);
|
||||
return (grant_option == other.grant_option) && (is_partial_revoke == other.is_partial_revoke);
|
||||
}
|
||||
|
||||
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); }
|
||||
|
||||
/// Resets flags which cannot be granted.
|
||||
void removeNonGrantableFlags();
|
||||
void eraseNonGrantable()
|
||||
{
|
||||
if (!any_column)
|
||||
access_flags &= AccessFlags::allFlagsGrantableOnColumnLevel();
|
||||
else if (!any_table)
|
||||
access_flags &= AccessFlags::allFlagsGrantableOnTableLevel();
|
||||
else if (!any_database)
|
||||
access_flags &= AccessFlags::allFlagsGrantableOnDatabaseLevel();
|
||||
else
|
||||
access_flags &= AccessFlags::allFlagsGrantableOnGlobalLevel();
|
||||
}
|
||||
|
||||
bool isEmptyDatabase() const { return !any_database && database.empty(); }
|
||||
|
||||
/// If the database is empty, replaces it with `current_database`. Otherwise does nothing.
|
||||
void replaceEmptyDatabase(const String & current_database)
|
||||
{
|
||||
if (isEmptyDatabase())
|
||||
database = current_database;
|
||||
}
|
||||
|
||||
/// Returns a human-readable representation like "GRANT SELECT, UPDATE(x, y) ON db.table".
|
||||
String toString() const;
|
||||
String toStringWithoutOptions() const;
|
||||
};
|
||||
|
||||
|
||||
@ -130,77 +124,29 @@ class AccessRightsElements : public std::vector<AccessRightsElement>
|
||||
public:
|
||||
bool empty() const { return std::all_of(begin(), end(), [](const AccessRightsElement & e) { return e.empty(); }); }
|
||||
|
||||
/// Replaces the empty database with `new_database`.
|
||||
void replaceEmptyDatabase(const String & new_database);
|
||||
bool sameDatabaseAndTable() const
|
||||
{
|
||||
return (size() < 2) || std::all_of(std::next(begin()), end(), [this](const AccessRightsElement & e) { return e.sameDatabaseAndTable(front()); });
|
||||
}
|
||||
|
||||
bool sameOptions() const
|
||||
{
|
||||
return (size() < 2) || std::all_of(std::next(begin()), end(), [this](const AccessRightsElement & e) { return e.sameOptions(front()); });
|
||||
}
|
||||
|
||||
/// Resets flags which cannot be granted.
|
||||
void removeNonGrantableFlags();
|
||||
void eraseNonGrantable();
|
||||
|
||||
/// If the database is empty, replaces it with `current_database`. Otherwise does nothing.
|
||||
void replaceEmptyDatabase(const String & current_database)
|
||||
{
|
||||
for (auto & element : *this)
|
||||
element.replaceEmptyDatabase(current_database);
|
||||
}
|
||||
|
||||
/// Returns a human-readable representation like "GRANT SELECT, UPDATE(x, y) ON db.table".
|
||||
String toString() const;
|
||||
String toStringWithoutOptions() const;
|
||||
};
|
||||
|
||||
|
||||
class AccessRightsElementsWithOptions : public std::vector<AccessRightsElementWithOptions>
|
||||
{
|
||||
public:
|
||||
/// Replaces the empty database with `new_database`.
|
||||
void replaceEmptyDatabase(const String & new_database);
|
||||
|
||||
/// Resets flags which cannot be granted.
|
||||
void removeNonGrantableFlags();
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
inline void AccessRightsElement::removeNonGrantableFlags()
|
||||
{
|
||||
if (!any_column)
|
||||
access_flags &= AccessFlags::allFlagsGrantableOnColumnLevel();
|
||||
else if (!any_table)
|
||||
access_flags &= AccessFlags::allFlagsGrantableOnTableLevel();
|
||||
else if (!any_database)
|
||||
access_flags &= AccessFlags::allFlagsGrantableOnDatabaseLevel();
|
||||
else
|
||||
access_flags &= AccessFlags::allFlagsGrantableOnGlobalLevel();
|
||||
}
|
||||
|
||||
inline void AccessRightsElementWithOptions::removeNonGrantableFlags()
|
||||
{
|
||||
if (kind == Kind::GRANT)
|
||||
AccessRightsElement::removeNonGrantableFlags();
|
||||
}
|
||||
|
||||
inline void AccessRightsElements::removeNonGrantableFlags()
|
||||
{
|
||||
for (auto & element : *this)
|
||||
element.removeNonGrantableFlags();
|
||||
}
|
||||
|
||||
inline void AccessRightsElementsWithOptions::removeNonGrantableFlags()
|
||||
{
|
||||
for (auto & element : *this)
|
||||
element.removeNonGrantableFlags();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -177,28 +177,18 @@ void ContextAccess::setUser(const UserPtr & user_) const
|
||||
user_name = user->getName();
|
||||
trace_log = &Poco::Logger::get("ContextAccess (" + user_name + ")");
|
||||
|
||||
boost::container::flat_set<UUID> current_roles, current_roles_with_admin_option;
|
||||
std::vector<UUID> current_roles, current_roles_with_admin_option;
|
||||
if (params.use_default_roles)
|
||||
{
|
||||
for (const UUID & id : user->granted_roles.roles)
|
||||
{
|
||||
if (user->default_roles.match(id))
|
||||
current_roles.emplace(id);
|
||||
}
|
||||
current_roles = user->granted_roles.findGranted(user->default_roles);
|
||||
current_roles_with_admin_option = user->granted_roles.findGrantedWithAdminOption(user->default_roles);
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::range::set_intersection(
|
||||
params.current_roles,
|
||||
user->granted_roles.roles,
|
||||
std::inserter(current_roles, current_roles.end()));
|
||||
current_roles = user->granted_roles.findGranted(params.current_roles);
|
||||
current_roles_with_admin_option = user->granted_roles.findGrantedWithAdminOption(params.current_roles);
|
||||
}
|
||||
|
||||
boost::range::set_intersection(
|
||||
current_roles,
|
||||
user->granted_roles.roles_with_admin_option,
|
||||
std::inserter(current_roles_with_admin_option, current_roles_with_admin_option.end()));
|
||||
|
||||
subscription_for_roles_changes = {};
|
||||
enabled_roles = manager->getEnabledRoles(current_roles, current_roles_with_admin_option);
|
||||
subscription_for_roles_changes = enabled_roles->subscribeForChanges([this](const std::shared_ptr<const EnabledRolesInfo> & roles_info_)
|
||||
@ -331,47 +321,13 @@ std::shared_ptr<const AccessRights> ContextAccess::getAccessRightsWithImplicit()
|
||||
}
|
||||
|
||||
|
||||
template <bool throw_if_denied, bool grant_option>
|
||||
bool ContextAccess::checkAccessImpl(const AccessFlags & flags) const
|
||||
{
|
||||
return checkAccessImpl2<throw_if_denied, grant_option>(flags);
|
||||
}
|
||||
|
||||
template <bool throw_if_denied, bool grant_option, typename... Args>
|
||||
bool ContextAccess::checkAccessImpl(const AccessFlags & flags, const std::string_view & database, const Args &... args) const
|
||||
{
|
||||
return checkAccessImpl2<throw_if_denied, grant_option>(flags, database.empty() ? params.current_database : database, args...);
|
||||
}
|
||||
|
||||
template <bool throw_if_denied, bool grant_option>
|
||||
bool ContextAccess::checkAccessImpl(const AccessRightsElement & element) const
|
||||
{
|
||||
if (element.any_database)
|
||||
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags);
|
||||
else if (element.any_table)
|
||||
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags, element.database);
|
||||
else if (element.any_column)
|
||||
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags, element.database, element.table);
|
||||
else
|
||||
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
|
||||
template <bool throw_if_denied, bool grant_option>
|
||||
bool ContextAccess::checkAccessImpl(const AccessRightsElements & elements) const
|
||||
{
|
||||
for (const auto & element : elements)
|
||||
if (!checkAccessImpl<throw_if_denied, grant_option>(element))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <bool throw_if_denied, bool grant_option, typename... Args>
|
||||
bool ContextAccess::checkAccessImpl2(const AccessFlags & flags, const Args &... args) const
|
||||
bool ContextAccess::checkAccessImplHelper(const AccessFlags & flags, const Args &... args) const
|
||||
{
|
||||
auto access_granted = [&]
|
||||
{
|
||||
if (trace_log)
|
||||
LOG_TRACE(trace_log, "Access granted: {}{}", (AccessRightsElement{flags, args...}.toString()),
|
||||
LOG_TRACE(trace_log, "Access granted: {}{}", (AccessRightsElement{flags, args...}.toStringWithoutOptions()),
|
||||
(grant_option ? " WITH GRANT OPTION" : ""));
|
||||
return true;
|
||||
};
|
||||
@ -379,7 +335,7 @@ bool ContextAccess::checkAccessImpl2(const AccessFlags & flags, const Args &...
|
||||
auto access_denied = [&](const String & error_msg, int error_code [[maybe_unused]])
|
||||
{
|
||||
if (trace_log)
|
||||
LOG_TRACE(trace_log, "Access denied: {}{}", (AccessRightsElement{flags, args...}.toString()),
|
||||
LOG_TRACE(trace_log, "Access denied: {}{}", (AccessRightsElement{flags, args...}.toStringWithoutOptions()),
|
||||
(grant_option ? " WITH GRANT OPTION" : ""));
|
||||
if constexpr (throw_if_denied)
|
||||
throw Exception(getUserName() + ": " + error_msg, error_code);
|
||||
@ -415,13 +371,13 @@ bool ContextAccess::checkAccessImpl2(const AccessFlags & flags, const Args &...
|
||||
"Not enough privileges. "
|
||||
"The required privileges have been granted, but without grant option. "
|
||||
"To execute this query it's necessary to have grant "
|
||||
+ AccessRightsElement{flags, args...}.toString() + " WITH GRANT OPTION",
|
||||
+ AccessRightsElement{flags, args...}.toStringWithoutOptions() + " WITH GRANT OPTION",
|
||||
ErrorCodes::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
return access_denied(
|
||||
"Not enough privileges. To execute this query it's necessary to have grant "
|
||||
+ AccessRightsElement{flags, args...}.toString() + (grant_option ? " WITH GRANT OPTION" : ""),
|
||||
+ AccessRightsElement{flags, args...}.toStringWithoutOptions() + (grant_option ? " WITH GRANT OPTION" : ""),
|
||||
ErrorCodes::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
@ -478,6 +434,56 @@ bool ContextAccess::checkAccessImpl2(const AccessFlags & flags, const Args &...
|
||||
return access_granted();
|
||||
}
|
||||
|
||||
template <bool throw_if_denied, bool grant_option>
|
||||
bool ContextAccess::checkAccessImpl(const AccessFlags & flags) const
|
||||
{
|
||||
return checkAccessImplHelper<throw_if_denied, grant_option>(flags);
|
||||
}
|
||||
|
||||
template <bool throw_if_denied, bool grant_option, typename... Args>
|
||||
bool ContextAccess::checkAccessImpl(const AccessFlags & flags, const std::string_view & database, const Args &... args) const
|
||||
{
|
||||
return checkAccessImplHelper<throw_if_denied, grant_option>(flags, database.empty() ? params.current_database : database, args...);
|
||||
}
|
||||
|
||||
template <bool throw_if_denied, bool grant_option>
|
||||
bool ContextAccess::checkAccessImplHelper(const AccessRightsElement & element) const
|
||||
{
|
||||
assert(!element.grant_option || grant_option);
|
||||
if (element.any_database)
|
||||
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags);
|
||||
else if (element.any_table)
|
||||
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags, element.database);
|
||||
else if (element.any_column)
|
||||
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags, element.database, element.table);
|
||||
else
|
||||
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
|
||||
template <bool throw_if_denied, bool grant_option>
|
||||
bool ContextAccess::checkAccessImpl(const AccessRightsElement & element) const
|
||||
{
|
||||
if constexpr (grant_option)
|
||||
{
|
||||
return checkAccessImplHelper<throw_if_denied, true>(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (element.grant_option)
|
||||
return checkAccessImplHelper<throw_if_denied, true>(element);
|
||||
else
|
||||
return checkAccessImplHelper<throw_if_denied, false>(element);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool throw_if_denied, bool grant_option>
|
||||
bool ContextAccess::checkAccessImpl(const AccessRightsElements & elements) const
|
||||
{
|
||||
for (const auto & element : elements)
|
||||
if (!checkAccessImpl<throw_if_denied, grant_option>(element))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags) const { return checkAccessImpl<false, false>(flags); }
|
||||
bool ContextAccess::isGranted(const AccessFlags & flags, const std::string_view & database) const { return checkAccessImpl<false, false>(flags, database); }
|
||||
@ -516,44 +522,8 @@ void ContextAccess::checkGrantOption(const AccessRightsElement & element) const
|
||||
void ContextAccess::checkGrantOption(const AccessRightsElements & elements) const { checkAccessImpl<true, true>(elements); }
|
||||
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool ContextAccess::checkAdminOptionImpl(const UUID & role_id) const
|
||||
{
|
||||
return checkAdminOptionImpl2<throw_if_denied>(to_array(role_id), [this](const UUID & id, size_t) { return manager->tryReadName(id); });
|
||||
}
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool ContextAccess::checkAdminOptionImpl(const UUID & role_id, const String & role_name) const
|
||||
{
|
||||
return checkAdminOptionImpl2<throw_if_denied>(to_array(role_id), [&role_name](const UUID &, size_t) { return std::optional<String>{role_name}; });
|
||||
}
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool ContextAccess::checkAdminOptionImpl(const UUID & role_id, const std::unordered_map<UUID, String> & names_of_roles) const
|
||||
{
|
||||
return checkAdminOptionImpl2<throw_if_denied>(to_array(role_id), [&names_of_roles](const UUID & id, size_t) { auto it = names_of_roles.find(id); return (it != names_of_roles.end()) ? it->second : std::optional<String>{}; });
|
||||
}
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool ContextAccess::checkAdminOptionImpl(const std::vector<UUID> & role_ids) const
|
||||
{
|
||||
return checkAdminOptionImpl2<throw_if_denied>(role_ids, [this](const UUID & id, size_t) { return manager->tryReadName(id); });
|
||||
}
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool ContextAccess::checkAdminOptionImpl(const std::vector<UUID> & role_ids, const Strings & names_of_roles) const
|
||||
{
|
||||
return checkAdminOptionImpl2<throw_if_denied>(role_ids, [&names_of_roles](const UUID &, size_t i) { return std::optional<String>{names_of_roles[i]}; });
|
||||
}
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool ContextAccess::checkAdminOptionImpl(const std::vector<UUID> & role_ids, const std::unordered_map<UUID, String> & names_of_roles) const
|
||||
{
|
||||
return checkAdminOptionImpl2<throw_if_denied>(role_ids, [&names_of_roles](const UUID & id, size_t) { auto it = names_of_roles.find(id); return (it != names_of_roles.end()) ? it->second : std::optional<String>{}; });
|
||||
}
|
||||
|
||||
template <bool throw_if_denied, typename Container, typename GetNameFunction>
|
||||
bool ContextAccess::checkAdminOptionImpl2(const Container & role_ids, const GetNameFunction & get_name_function) const
|
||||
bool ContextAccess::checkAdminOptionImplHelper(const Container & role_ids, const GetNameFunction & get_name_function) const
|
||||
{
|
||||
if (!std::size(role_ids) || is_full_access)
|
||||
return true;
|
||||
@ -605,6 +575,42 @@ bool ContextAccess::checkAdminOptionImpl2(const Container & role_ids, const GetN
|
||||
return true;
|
||||
}
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool ContextAccess::checkAdminOptionImpl(const UUID & role_id) const
|
||||
{
|
||||
return checkAdminOptionImplHelper<throw_if_denied>(to_array(role_id), [this](const UUID & id, size_t) { return manager->tryReadName(id); });
|
||||
}
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool ContextAccess::checkAdminOptionImpl(const UUID & role_id, const String & role_name) const
|
||||
{
|
||||
return checkAdminOptionImplHelper<throw_if_denied>(to_array(role_id), [&role_name](const UUID &, size_t) { return std::optional<String>{role_name}; });
|
||||
}
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool ContextAccess::checkAdminOptionImpl(const UUID & role_id, const std::unordered_map<UUID, String> & names_of_roles) const
|
||||
{
|
||||
return checkAdminOptionImplHelper<throw_if_denied>(to_array(role_id), [&names_of_roles](const UUID & id, size_t) { auto it = names_of_roles.find(id); return (it != names_of_roles.end()) ? it->second : std::optional<String>{}; });
|
||||
}
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool ContextAccess::checkAdminOptionImpl(const std::vector<UUID> & role_ids) const
|
||||
{
|
||||
return checkAdminOptionImplHelper<throw_if_denied>(role_ids, [this](const UUID & id, size_t) { return manager->tryReadName(id); });
|
||||
}
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool ContextAccess::checkAdminOptionImpl(const std::vector<UUID> & role_ids, const Strings & names_of_roles) const
|
||||
{
|
||||
return checkAdminOptionImplHelper<throw_if_denied>(role_ids, [&names_of_roles](const UUID &, size_t i) { return std::optional<String>{names_of_roles[i]}; });
|
||||
}
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool ContextAccess::checkAdminOptionImpl(const std::vector<UUID> & role_ids, const std::unordered_map<UUID, String> & names_of_roles) const
|
||||
{
|
||||
return checkAdminOptionImplHelper<throw_if_denied>(role_ids, [&names_of_roles](const UUID & id, size_t) { auto it = names_of_roles.find(id); return (it != names_of_roles.end()) ? it->second : std::optional<String>{}; });
|
||||
}
|
||||
|
||||
bool ContextAccess::hasAdminOption(const UUID & role_id) const { return checkAdminOptionImpl<false>(role_id); }
|
||||
bool ContextAccess::hasAdminOption(const UUID & role_id, const String & role_name) const { return checkAdminOptionImpl<false>(role_id, role_name); }
|
||||
bool ContextAccess::hasAdminOption(const UUID & role_id, const std::unordered_map<UUID, String> & names_of_roles) const { return checkAdminOptionImpl<false>(role_id, names_of_roles); }
|
||||
|
@ -99,25 +99,6 @@ public:
|
||||
std::shared_ptr<const AccessRights> getAccessRights() const;
|
||||
std::shared_ptr<const AccessRights> getAccessRightsWithImplicit() const;
|
||||
|
||||
/// Checks if a specified access is granted.
|
||||
bool isGranted(const AccessFlags & flags) const;
|
||||
bool isGranted(const AccessFlags & flags, const std::string_view & database) const;
|
||||
bool isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const;
|
||||
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) const;
|
||||
bool isGranted(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;
|
||||
@ -138,6 +119,26 @@ public:
|
||||
void checkGrantOption(const AccessRightsElement & element) const;
|
||||
void checkGrantOption(const AccessRightsElements & elements) const;
|
||||
|
||||
/// Checks if a specified access is granted, and returns false if not.
|
||||
/// Empty database means the current database.
|
||||
bool isGranted(const AccessFlags & flags) const;
|
||||
bool isGranted(const AccessFlags & flags, const std::string_view & database) const;
|
||||
bool isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const;
|
||||
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) const;
|
||||
bool isGranted(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 role is granted with admin option, and throws an exception if not.
|
||||
void checkAdminOption(const UUID & role_id) const;
|
||||
void checkAdminOption(const UUID & role_id, const String & role_name) const;
|
||||
@ -146,6 +147,7 @@ public:
|
||||
void checkAdminOption(const std::vector<UUID> & role_ids, const Strings & names_of_roles) const;
|
||||
void checkAdminOption(const std::vector<UUID> & role_ids, const std::unordered_map<UUID, String> & names_of_roles) const;
|
||||
|
||||
/// Checks if a specified role is granted with admin option, and returns false if not.
|
||||
bool hasAdminOption(const UUID & role_id) const;
|
||||
bool hasAdminOption(const UUID & role_id, const String & role_name) const;
|
||||
bool hasAdminOption(const UUID & role_id, const std::unordered_map<UUID, String> & names_of_roles) const;
|
||||
@ -180,7 +182,10 @@ private:
|
||||
bool checkAccessImpl(const AccessRightsElements & elements) const;
|
||||
|
||||
template <bool throw_if_denied, bool grant_option, typename... Args>
|
||||
bool checkAccessImpl2(const AccessFlags & flags, const Args &... args) const;
|
||||
bool checkAccessImplHelper(const AccessFlags & flags, const Args &... args) const;
|
||||
|
||||
template <bool throw_if_denied, bool grant_option>
|
||||
bool checkAccessImplHelper(const AccessRightsElement & element) const;
|
||||
|
||||
template <bool throw_if_denied>
|
||||
bool checkAdminOptionImpl(const UUID & role_id) const;
|
||||
@ -201,7 +206,7 @@ private:
|
||||
bool checkAdminOptionImpl(const std::vector<UUID> & role_ids, const std::unordered_map<UUID, String> & names_of_roles) const;
|
||||
|
||||
template <bool throw_if_denied, typename Container, typename GetNameFunction>
|
||||
bool checkAdminOptionImpl2(const Container & role_ids, const GetNameFunction & get_name_function) const;
|
||||
bool checkAdminOptionImplHelper(const Container & role_ids, const GetNameFunction & get_name_function) const;
|
||||
|
||||
const AccessControlManager * manager = nullptr;
|
||||
const Params params;
|
||||
|
@ -1,37 +1,38 @@
|
||||
#include <Access/GrantedRoles.h>
|
||||
#include <Access/RolesOrUsersSet.h>
|
||||
#include <boost/range/algorithm/set_algorithm.hpp>
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
void GrantedRoles::grant(const UUID & role)
|
||||
void GrantedRoles::grant(const UUID & role_)
|
||||
{
|
||||
roles.insert(role);
|
||||
roles.insert(role_);
|
||||
}
|
||||
|
||||
void GrantedRoles::grant(const std::vector<UUID> & roles_)
|
||||
{
|
||||
for (const UUID & role : roles_)
|
||||
grant(role);
|
||||
roles.insert(roles_.begin(), roles_.end());
|
||||
}
|
||||
|
||||
void GrantedRoles::grantWithAdminOption(const UUID & role)
|
||||
void GrantedRoles::grantWithAdminOption(const UUID & role_)
|
||||
{
|
||||
roles.insert(role);
|
||||
roles_with_admin_option.insert(role);
|
||||
roles.insert(role_);
|
||||
roles_with_admin_option.insert(role_);
|
||||
}
|
||||
|
||||
void GrantedRoles::grantWithAdminOption(const std::vector<UUID> & roles_)
|
||||
{
|
||||
for (const UUID & role : roles_)
|
||||
grantWithAdminOption(role);
|
||||
roles.insert(roles_.begin(), roles_.end());
|
||||
roles_with_admin_option.insert(roles_.begin(), roles_.end());
|
||||
}
|
||||
|
||||
|
||||
void GrantedRoles::revoke(const UUID & role)
|
||||
void GrantedRoles::revoke(const UUID & role_)
|
||||
{
|
||||
roles.erase(role);
|
||||
roles_with_admin_option.erase(role);
|
||||
roles.erase(role_);
|
||||
roles_with_admin_option.erase(role_);
|
||||
}
|
||||
|
||||
void GrantedRoles::revoke(const std::vector<UUID> & roles_)
|
||||
@ -40,9 +41,9 @@ void GrantedRoles::revoke(const std::vector<UUID> & roles_)
|
||||
revoke(role);
|
||||
}
|
||||
|
||||
void GrantedRoles::revokeAdminOption(const UUID & role)
|
||||
void GrantedRoles::revokeAdminOption(const UUID & role_)
|
||||
{
|
||||
roles_with_admin_option.erase(role);
|
||||
roles_with_admin_option.erase(role_);
|
||||
}
|
||||
|
||||
void GrantedRoles::revokeAdminOption(const std::vector<UUID> & roles_)
|
||||
@ -52,13 +53,118 @@ void GrantedRoles::revokeAdminOption(const std::vector<UUID> & roles_)
|
||||
}
|
||||
|
||||
|
||||
GrantedRoles::Grants GrantedRoles::getGrants() const
|
||||
bool GrantedRoles::isGranted(const UUID & role_) const
|
||||
{
|
||||
Grants res;
|
||||
res.grants_with_admin_option.insert(res.grants_with_admin_option.end(), roles_with_admin_option.begin(), roles_with_admin_option.end());
|
||||
res.grants.reserve(roles.size() - roles_with_admin_option.size());
|
||||
boost::range::set_difference(roles, roles_with_admin_option, std::back_inserter(res.grants));
|
||||
return roles.count(role_);
|
||||
}
|
||||
|
||||
bool GrantedRoles::isGrantedWithAdminOption(const UUID & role_) const
|
||||
{
|
||||
return roles_with_admin_option.count(role_);
|
||||
}
|
||||
|
||||
|
||||
std::vector<UUID> GrantedRoles::findGranted(const std::vector<UUID> & ids) const
|
||||
{
|
||||
std::vector<UUID> res;
|
||||
res.reserve(ids.size());
|
||||
for (const UUID & id : ids)
|
||||
{
|
||||
if (isGranted(id))
|
||||
res.push_back(id);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<UUID> GrantedRoles::findGranted(const boost::container::flat_set<UUID> & ids) const
|
||||
{
|
||||
std::vector<UUID> res;
|
||||
res.reserve(ids.size());
|
||||
boost::range::set_difference(ids, roles, std::back_inserter(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<UUID> GrantedRoles::findGranted(const RolesOrUsersSet & ids) const
|
||||
{
|
||||
std::vector<UUID> res;
|
||||
for (const UUID & id : roles)
|
||||
{
|
||||
if (ids.match(id))
|
||||
res.emplace_back(id);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<UUID> GrantedRoles::findGrantedWithAdminOption(const std::vector<UUID> & ids) const
|
||||
{
|
||||
std::vector<UUID> res;
|
||||
res.reserve(ids.size());
|
||||
for (const UUID & id : ids)
|
||||
{
|
||||
if (isGrantedWithAdminOption(id))
|
||||
res.push_back(id);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<UUID> GrantedRoles::findGrantedWithAdminOption(const boost::container::flat_set<UUID> & ids) const
|
||||
{
|
||||
std::vector<UUID> res;
|
||||
res.reserve(ids.size());
|
||||
boost::range::set_difference(ids, roles_with_admin_option, std::back_inserter(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<UUID> GrantedRoles::findGrantedWithAdminOption(const RolesOrUsersSet & ids) const
|
||||
{
|
||||
std::vector<UUID> res;
|
||||
for (const UUID & id : roles_with_admin_option)
|
||||
{
|
||||
if (ids.match(id))
|
||||
res.emplace_back(id);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
GrantedRoles::Elements GrantedRoles::getElements() const
|
||||
{
|
||||
Elements elements;
|
||||
|
||||
Element element;
|
||||
element.ids.reserve(roles.size());
|
||||
boost::range::set_difference(roles, roles_with_admin_option, std::back_inserter(element.ids));
|
||||
if (!element.empty())
|
||||
{
|
||||
element.admin_option = false;
|
||||
elements.emplace_back(std::move(element));
|
||||
}
|
||||
|
||||
if (!roles_with_admin_option.empty())
|
||||
{
|
||||
element = {};
|
||||
element.ids.insert(element.ids.end(), roles_with_admin_option.begin(), roles_with_admin_option.end());
|
||||
element.admin_option = true;
|
||||
elements.emplace_back(std::move(element));
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
|
||||
void GrantedRoles::makeUnion(const GrantedRoles & other)
|
||||
{
|
||||
roles.insert(other.roles.begin(), other.roles.end());
|
||||
roles_with_admin_option.insert(other.roles_with_admin_option.begin(), other.roles_with_admin_option.end());
|
||||
}
|
||||
|
||||
void GrantedRoles::makeIntersection(const GrantedRoles & other)
|
||||
{
|
||||
boost::range::remove_erase_if(roles, [&other](const UUID & id) { return other.roles.find(id) == other.roles.end(); });
|
||||
|
||||
boost::range::remove_erase_if(roles_with_admin_option, [&other](const UUID & id)
|
||||
{
|
||||
return other.roles_with_admin_option.find(id) == other.roles_with_admin_option.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,33 +7,55 @@
|
||||
|
||||
namespace DB
|
||||
{
|
||||
struct RolesOrUsersSet;
|
||||
|
||||
/// Roles when they are granted to a role or user.
|
||||
/// Stores both the roles themselves and the roles with admin option.
|
||||
struct GrantedRoles
|
||||
class GrantedRoles
|
||||
{
|
||||
boost::container::flat_set<UUID> roles;
|
||||
boost::container::flat_set<UUID> roles_with_admin_option;
|
||||
|
||||
void grant(const UUID & role);
|
||||
public:
|
||||
void grant(const UUID & role_);
|
||||
void grant(const std::vector<UUID> & roles_);
|
||||
void grantWithAdminOption(const UUID & role);
|
||||
void grantWithAdminOption(const UUID & role_);
|
||||
void grantWithAdminOption(const std::vector<UUID> & roles_);
|
||||
|
||||
void revoke(const UUID & role);
|
||||
void revoke(const UUID & role_);
|
||||
void revoke(const std::vector<UUID> & roles_);
|
||||
void revokeAdminOption(const UUID & role);
|
||||
void revokeAdminOption(const UUID & role_);
|
||||
void revokeAdminOption(const std::vector<UUID> & roles_);
|
||||
|
||||
struct Grants
|
||||
bool isGranted(const UUID & role_) const;
|
||||
bool isGrantedWithAdminOption(const UUID & role_) const;
|
||||
|
||||
const boost::container::flat_set<UUID> & getGranted() const { return roles; }
|
||||
const boost::container::flat_set<UUID> & getGrantedWithAdminOption() const { return roles_with_admin_option; }
|
||||
|
||||
std::vector<UUID> findGranted(const std::vector<UUID> & ids) const;
|
||||
std::vector<UUID> findGranted(const boost::container::flat_set<UUID> & ids) const;
|
||||
std::vector<UUID> findGranted(const RolesOrUsersSet & ids) const;
|
||||
std::vector<UUID> findGrantedWithAdminOption(const std::vector<UUID> & ids) const;
|
||||
std::vector<UUID> findGrantedWithAdminOption(const boost::container::flat_set<UUID> & ids) const;
|
||||
std::vector<UUID> findGrantedWithAdminOption(const RolesOrUsersSet & ids) const;
|
||||
|
||||
struct Element
|
||||
{
|
||||
std::vector<UUID> grants;
|
||||
std::vector<UUID> grants_with_admin_option;
|
||||
std::vector<UUID> ids;
|
||||
bool admin_option = false;
|
||||
bool empty() const { return ids.empty(); }
|
||||
};
|
||||
using Elements = std::vector<Element>;
|
||||
|
||||
/// Retrieves the information about grants.
|
||||
Grants getGrants() const;
|
||||
Elements getElements() const;
|
||||
|
||||
void makeUnion(const GrantedRoles & other);
|
||||
void makeIntersection(const GrantedRoles & other);
|
||||
|
||||
friend bool operator ==(const GrantedRoles & left, const GrantedRoles & right) { return (left.roles == right.roles) && (left.roles_with_admin_option == right.roles_with_admin_option); }
|
||||
friend bool operator !=(const GrantedRoles & left, const GrantedRoles & right) { return !(left == right); }
|
||||
|
||||
private:
|
||||
boost::container::flat_set<UUID> roles;
|
||||
boost::container::flat_set<UUID> roles_with_admin_option;
|
||||
};
|
||||
}
|
||||
|
@ -187,13 +187,10 @@ void LDAPAccessStorage::applyRoleChangeNoLock(bool grant, const UUID & role_id,
|
||||
if (auto user = typeid_cast<std::shared_ptr<const User>>(entity_))
|
||||
{
|
||||
auto changed_user = typeid_cast<std::shared_ptr<User>>(user->clone());
|
||||
auto & granted_roles = changed_user->granted_roles.roles;
|
||||
|
||||
if (grant)
|
||||
granted_roles.insert(role_id);
|
||||
changed_user->granted_roles.grant(role_id);
|
||||
else
|
||||
granted_roles.erase(role_id);
|
||||
|
||||
changed_user->granted_roles.revoke(role_id);
|
||||
return changed_user;
|
||||
}
|
||||
return entity_;
|
||||
@ -229,7 +226,7 @@ void LDAPAccessStorage::assignRolesNoLock(User & user, const LDAPClient::SearchR
|
||||
void LDAPAccessStorage::assignRolesNoLock(User & user, const LDAPClient::SearchResultsList & external_roles, const std::size_t external_roles_hash) const
|
||||
{
|
||||
const auto & user_name = user.getName();
|
||||
auto & granted_roles = user.granted_roles.roles;
|
||||
auto & granted_roles = user.granted_roles;
|
||||
const auto local_role_names = mapExternalRolesNoLock(external_roles);
|
||||
|
||||
auto grant_role = [this, &user_name, &granted_roles] (const String & role_name, const bool common)
|
||||
@ -247,7 +244,7 @@ void LDAPAccessStorage::assignRolesNoLock(User & user, const LDAPClient::SearchR
|
||||
if (it != granted_role_ids.end())
|
||||
{
|
||||
const auto & role_id = it->second;
|
||||
granted_roles.insert(role_id);
|
||||
granted_roles.grant(role_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -256,7 +253,7 @@ void LDAPAccessStorage::assignRolesNoLock(User & user, const LDAPClient::SearchR
|
||||
};
|
||||
|
||||
external_role_hashes.erase(user_name);
|
||||
granted_roles.clear();
|
||||
granted_roles = {};
|
||||
const auto old_role_names = std::move(roles_per_users[user_name]);
|
||||
|
||||
// Grant the common roles first.
|
||||
|
@ -46,10 +46,10 @@ namespace
|
||||
roles_info.access.makeUnion(role->access);
|
||||
roles_info.settings_from_enabled_roles.merge(role->settings);
|
||||
|
||||
for (const auto & granted_role : role->granted_roles.roles)
|
||||
for (const auto & granted_role : role->granted_roles.getGranted())
|
||||
collectRoles(roles_info, skip_ids, get_role_function, granted_role, false, false);
|
||||
|
||||
for (const auto & granted_role : role->granted_roles.roles_with_admin_option)
|
||||
for (const auto & granted_role : role->granted_roles.getGrantedWithAdminOption())
|
||||
collectRoles(roles_info, skip_ids, get_role_function, granted_role, false, true);
|
||||
}
|
||||
}
|
||||
@ -63,15 +63,15 @@ RoleCache::~RoleCache() = default;
|
||||
|
||||
|
||||
std::shared_ptr<const EnabledRoles>
|
||||
RoleCache::getEnabledRoles(const boost::container::flat_set<UUID> & roles, const boost::container::flat_set<UUID> & roles_with_admin_option)
|
||||
RoleCache::getEnabledRoles(const std::vector<UUID> & roles, const std::vector<UUID> & roles_with_admin_option)
|
||||
{
|
||||
/// Declared before `lock` to send notifications after the mutex will be unlocked.
|
||||
ext::scope_guard notifications;
|
||||
|
||||
std::lock_guard lock{mutex};
|
||||
EnabledRoles::Params params;
|
||||
params.current_roles = roles;
|
||||
params.current_roles_with_admin_option = roles_with_admin_option;
|
||||
params.current_roles.insert(roles.begin(), roles.end());
|
||||
params.current_roles_with_admin_option.insert(roles_with_admin_option.begin(), roles_with_admin_option.end());
|
||||
auto it = enabled_roles.find(params);
|
||||
if (it != enabled_roles.end())
|
||||
{
|
||||
|
@ -20,7 +20,8 @@ public:
|
||||
~RoleCache();
|
||||
|
||||
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 std::vector<UUID> & current_roles,
|
||||
const std::vector<UUID> & current_roles_with_admin_option);
|
||||
|
||||
private:
|
||||
void collectEnabledRoles(ext::scope_guard & notifications);
|
||||
|
@ -72,20 +72,20 @@ void RolesOrUsersSet::init(const ASTRolesOrUsersSet & ast, const AccessControlMa
|
||||
if (ast.id_mode)
|
||||
return parse<UUID>(name);
|
||||
assert(manager);
|
||||
if (ast.allow_user_names && ast.allow_role_names)
|
||||
if (ast.allow_users && ast.allow_roles)
|
||||
{
|
||||
auto id = manager->find<User>(name);
|
||||
if (id)
|
||||
return *id;
|
||||
return manager->getID<Role>(name);
|
||||
}
|
||||
else if (ast.allow_user_names)
|
||||
else if (ast.allow_users)
|
||||
{
|
||||
return manager->getID<User>(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(ast.allow_role_names);
|
||||
assert(ast.allow_roles);
|
||||
return manager->getID<Role>(name);
|
||||
}
|
||||
};
|
||||
@ -106,8 +106,8 @@ void RolesOrUsersSet::init(const ASTRolesOrUsersSet & ast, const AccessControlMa
|
||||
if (!ast.except_names.empty())
|
||||
{
|
||||
except_ids.reserve(ast.except_names.size());
|
||||
for (const String & except_name : ast.except_names)
|
||||
except_ids.insert(name_to_id(except_name));
|
||||
for (const String & name : ast.except_names)
|
||||
except_ids.insert(name_to_id(name));
|
||||
}
|
||||
|
||||
if (ast.except_current_user)
|
||||
@ -116,8 +116,8 @@ void RolesOrUsersSet::init(const ASTRolesOrUsersSet & ast, const AccessControlMa
|
||||
except_ids.insert(*current_user_id);
|
||||
}
|
||||
|
||||
for (const UUID & except_id : except_ids)
|
||||
ids.erase(except_id);
|
||||
for (const UUID & id : except_ids)
|
||||
ids.erase(id);
|
||||
}
|
||||
|
||||
|
||||
@ -127,7 +127,7 @@ std::shared_ptr<ASTRolesOrUsersSet> RolesOrUsersSet::toAST() const
|
||||
ast->id_mode = true;
|
||||
ast->all = all;
|
||||
|
||||
if (!ids.empty())
|
||||
if (!ids.empty() && !all)
|
||||
{
|
||||
ast->names.reserve(ids.size());
|
||||
for (const UUID & id : ids)
|
||||
@ -152,7 +152,7 @@ std::shared_ptr<ASTRolesOrUsersSet> RolesOrUsersSet::toASTWithNames(const Access
|
||||
auto ast = std::make_shared<ASTRolesOrUsersSet>();
|
||||
ast->all = all;
|
||||
|
||||
if (!ids.empty())
|
||||
if (!ids.empty() && !all)
|
||||
{
|
||||
ast->names.reserve(ids.size());
|
||||
for (const UUID & id : ids)
|
||||
@ -194,44 +194,6 @@ String RolesOrUsersSet::toStringWithNames(const AccessControlManager & manager)
|
||||
}
|
||||
|
||||
|
||||
Strings RolesOrUsersSet::toStringsWithNames(const AccessControlManager & manager) const
|
||||
{
|
||||
if (!all && ids.empty())
|
||||
return {};
|
||||
|
||||
Strings res;
|
||||
res.reserve(ids.size() + except_ids.size());
|
||||
|
||||
if (all)
|
||||
res.emplace_back("ALL");
|
||||
else
|
||||
{
|
||||
for (const UUID & id : ids)
|
||||
{
|
||||
auto name = manager.tryReadName(id);
|
||||
if (name)
|
||||
res.emplace_back(std::move(*name));
|
||||
}
|
||||
std::sort(res.begin(), res.end());
|
||||
}
|
||||
|
||||
if (!except_ids.empty())
|
||||
{
|
||||
res.emplace_back("EXCEPT");
|
||||
size_t old_size = res.size();
|
||||
for (const UUID & id : except_ids)
|
||||
{
|
||||
auto name = manager.tryReadName(id);
|
||||
if (name)
|
||||
res.emplace_back(std::move(*name));
|
||||
}
|
||||
std::sort(res.begin() + old_size, res.end());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
bool RolesOrUsersSet::empty() const
|
||||
{
|
||||
return ids.empty() && !all;
|
||||
@ -248,14 +210,18 @@ void RolesOrUsersSet::clear()
|
||||
|
||||
void RolesOrUsersSet::add(const UUID & id)
|
||||
{
|
||||
ids.insert(id);
|
||||
if (!all)
|
||||
ids.insert(id);
|
||||
except_ids.erase(id);
|
||||
}
|
||||
|
||||
|
||||
void RolesOrUsersSet::add(const std::vector<UUID> & ids_)
|
||||
{
|
||||
if (!all)
|
||||
ids.insert(ids_.begin(), ids_.end());
|
||||
for (const auto & id : ids_)
|
||||
add(id);
|
||||
except_ids.erase(id);
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,7 +13,8 @@ class AccessControlManager;
|
||||
|
||||
|
||||
/// Represents a set of users/roles like
|
||||
/// {user_name | role_name | CURRENT_USER} [,...] | NONE | ALL | ALL EXCEPT {user_name | role_name | CURRENT_USER} [,...]
|
||||
/// {user_name | role_name | CURRENT_USER | ALL | NONE} [,...]
|
||||
/// [EXCEPT {user_name | role_name | CURRENT_USER | ALL | NONE} [,...]]
|
||||
/// Similar to ASTRolesOrUsersSet, but with IDs instead of names.
|
||||
struct RolesOrUsersSet
|
||||
{
|
||||
@ -60,8 +61,8 @@ struct RolesOrUsersSet
|
||||
friend bool operator ==(const RolesOrUsersSet & lhs, const RolesOrUsersSet & rhs);
|
||||
friend bool operator !=(const RolesOrUsersSet & lhs, const RolesOrUsersSet & rhs) { return !(lhs == rhs); }
|
||||
|
||||
boost::container::flat_set<UUID> ids;
|
||||
bool all = false;
|
||||
boost::container::flat_set<UUID> ids;
|
||||
boost::container::flat_set<UUID> except_ids;
|
||||
|
||||
private:
|
||||
|
@ -753,7 +753,7 @@ std::optional<UUID> Context::getUserID() const
|
||||
}
|
||||
|
||||
|
||||
void Context::setCurrentRoles(const boost::container::flat_set<UUID> & current_roles_)
|
||||
void Context::setCurrentRoles(const std::vector<UUID> & current_roles_)
|
||||
{
|
||||
auto lock = getLock();
|
||||
if (current_roles == current_roles_ && !use_default_roles)
|
||||
|
@ -181,7 +181,7 @@ private:
|
||||
InputBlocksReader input_blocks_reader;
|
||||
|
||||
std::optional<UUID> user_id;
|
||||
boost::container::flat_set<UUID> current_roles;
|
||||
std::vector<UUID> current_roles;
|
||||
bool use_default_roles = false;
|
||||
std::shared_ptr<const ContextAccess> access;
|
||||
std::shared_ptr<const EnabledRowPolicies> initial_row_policy;
|
||||
@ -354,7 +354,7 @@ public:
|
||||
String getUserName() const;
|
||||
std::optional<UUID> getUserID() const;
|
||||
|
||||
void setCurrentRoles(const boost::container::flat_set<UUID> & current_roles_);
|
||||
void setCurrentRoles(const std::vector<UUID> & current_roles_);
|
||||
void setCurrentRolesDefault();
|
||||
boost::container::flat_set<UUID> getCurrentRoles() const;
|
||||
boost::container::flat_set<UUID> getEnabledRoles() const;
|
||||
|
@ -78,7 +78,7 @@ BlockIO InterpreterCreateQuotaQuery::execute()
|
||||
|
||||
if (!query.cluster.empty())
|
||||
{
|
||||
query.replaceCurrentUserTagWithName(context.getUserName());
|
||||
query.replaceCurrentUserTag(context.getUserName());
|
||||
return executeDDLQueryOnCluster(query_ptr, context);
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ BlockIO InterpreterCreateRowPolicyQuery::execute()
|
||||
|
||||
if (!query.cluster.empty())
|
||||
{
|
||||
query.replaceCurrentUserTagWithName(context.getUserName());
|
||||
query.replaceCurrentUserTag(context.getUserName());
|
||||
return executeDDLQueryOnCluster(query_ptr, context);
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ BlockIO InterpreterCreateRowPolicyQuery::execute()
|
||||
if (query.roles)
|
||||
roles_from_query = RolesOrUsersSet{*query.roles, access_control, context.getUserID()};
|
||||
|
||||
query.replaceEmptyDatabaseWithCurrent(context.getCurrentDatabase());
|
||||
query.replaceEmptyDatabase(context.getCurrentDatabase());
|
||||
|
||||
if (query.alter)
|
||||
{
|
||||
|
@ -50,7 +50,7 @@ BlockIO InterpreterCreateSettingsProfileQuery::execute()
|
||||
|
||||
if (!query.cluster.empty())
|
||||
{
|
||||
query.replaceCurrentUserTagWithName(context.getUserName());
|
||||
query.replaceCurrentUserTag(context.getUserName());
|
||||
return executeDDLQueryOnCluster(query_ptr, context);
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ BlockIO InterpreterDropAccessEntityQuery::execute()
|
||||
if (!query.cluster.empty())
|
||||
return executeDDLQueryOnCluster(query_ptr, context);
|
||||
|
||||
query.replaceEmptyDatabaseWithCurrent(context.getCurrentDatabase());
|
||||
query.replaceEmptyDatabase(context.getCurrentDatabase());
|
||||
|
||||
auto do_drop = [&](const Strings & names)
|
||||
{
|
||||
|
@ -12,13 +12,15 @@
|
||||
#include <boost/range/algorithm/copy.hpp>
|
||||
#include <boost/range/algorithm/set_algorithm.hpp>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
using Kind = ASTGrantQuery::Kind;
|
||||
|
||||
template <typename T>
|
||||
void updateFromQueryTemplate(
|
||||
T & grantee,
|
||||
@ -27,38 +29,28 @@ namespace
|
||||
{
|
||||
if (!query.access_rights_elements.empty())
|
||||
{
|
||||
if (query.kind == Kind::GRANT)
|
||||
{
|
||||
if (query.grant_option)
|
||||
grantee.access.grantWithGrantOption(query.access_rights_elements);
|
||||
else
|
||||
grantee.access.grant(query.access_rights_elements);
|
||||
}
|
||||
if (query.is_revoke)
|
||||
grantee.access.revoke(query.access_rights_elements);
|
||||
else
|
||||
{
|
||||
if (query.grant_option)
|
||||
grantee.access.revokeGrantOption(query.access_rights_elements);
|
||||
else
|
||||
grantee.access.revoke(query.access_rights_elements);
|
||||
}
|
||||
grantee.access.grant(query.access_rights_elements);
|
||||
}
|
||||
|
||||
if (!roles_to_grant_or_revoke.empty())
|
||||
{
|
||||
if (query.kind == Kind::GRANT)
|
||||
{
|
||||
if (query.admin_option)
|
||||
grantee.granted_roles.grantWithAdminOption(roles_to_grant_or_revoke);
|
||||
else
|
||||
grantee.granted_roles.grant(roles_to_grant_or_revoke);
|
||||
}
|
||||
else
|
||||
if (query.is_revoke)
|
||||
{
|
||||
if (query.admin_option)
|
||||
grantee.granted_roles.revokeAdminOption(roles_to_grant_or_revoke);
|
||||
else
|
||||
grantee.granted_roles.revoke(roles_to_grant_or_revoke);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (query.admin_option)
|
||||
grantee.granted_roles.grantWithAdminOption(roles_to_grant_or_revoke);
|
||||
else
|
||||
grantee.granted_roles.grant(roles_to_grant_or_revoke);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,122 +64,166 @@ namespace
|
||||
else if (auto * role = typeid_cast<Role *>(&grantee))
|
||||
updateFromQueryTemplate(*role, query, roles_to_grant_or_revoke);
|
||||
}
|
||||
|
||||
void checkGrantOption(
|
||||
const AccessControlManager & access_control,
|
||||
const ContextAccess & access,
|
||||
const ASTGrantQuery & query,
|
||||
const std::vector<UUID> & grantees_from_query)
|
||||
{
|
||||
const auto & elements = query.access_rights_elements;
|
||||
if (elements.empty())
|
||||
return;
|
||||
|
||||
/// To execute the command GRANT the current user needs to have the access granted
|
||||
/// with GRANT OPTION.
|
||||
if (!query.is_revoke)
|
||||
{
|
||||
access.checkGrantOption(elements);
|
||||
return;
|
||||
}
|
||||
|
||||
if (access.hasGrantOption(elements))
|
||||
return;
|
||||
|
||||
/// Special case for the command REVOKE: it's possible that the current user doesn't have
|
||||
/// the access granted with GRANT OPTION but it's still ok because the roles or users
|
||||
/// from whom the access rights will be revoked don't have the specified access granted either.
|
||||
///
|
||||
/// For example, to execute
|
||||
/// GRANT ALL ON mydb.* TO role1
|
||||
/// REVOKE ALL ON *.* FROM role1
|
||||
/// the current user needs to have grants only on the 'mydb' database.
|
||||
AccessRights all_granted_access;
|
||||
for (const auto & id : grantees_from_query)
|
||||
{
|
||||
auto entity = access_control.tryRead(id);
|
||||
if (auto role = typeid_cast<RolePtr>(entity))
|
||||
all_granted_access.makeUnion(role->access);
|
||||
else if (auto user = typeid_cast<UserPtr>(entity))
|
||||
all_granted_access.makeUnion(user->access);
|
||||
}
|
||||
|
||||
AccessRights required_access;
|
||||
if (elements[0].is_partial_revoke)
|
||||
{
|
||||
AccessRightsElements non_revoke_elements = elements;
|
||||
std::for_each(non_revoke_elements.begin(), non_revoke_elements.end(), [&](AccessRightsElement & element) { element.is_partial_revoke = false; });
|
||||
required_access.grant(non_revoke_elements);
|
||||
}
|
||||
else
|
||||
{
|
||||
required_access.grant(elements);
|
||||
}
|
||||
required_access.makeIntersection(all_granted_access);
|
||||
|
||||
for (auto & required_access_element : required_access.getElements())
|
||||
{
|
||||
if (!required_access_element.is_partial_revoke && (required_access_element.grant_option || !elements[0].grant_option))
|
||||
access.checkGrantOption(required_access_element);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<UUID> getRoleIDsAndCheckAdminOption(
|
||||
const AccessControlManager & access_control,
|
||||
const ContextAccess & access,
|
||||
const ASTGrantQuery & query,
|
||||
const RolesOrUsersSet & roles_from_query,
|
||||
const std::vector<UUID> & grantees_from_query)
|
||||
{
|
||||
std::vector<UUID> matching_ids;
|
||||
|
||||
if (!query.is_revoke)
|
||||
{
|
||||
matching_ids = roles_from_query.getMatchingIDs(access_control);
|
||||
access.checkAdminOption(matching_ids);
|
||||
return matching_ids;
|
||||
}
|
||||
|
||||
if (!roles_from_query.all)
|
||||
{
|
||||
matching_ids = roles_from_query.getMatchingIDs();
|
||||
if (access.hasAdminOption(matching_ids))
|
||||
return matching_ids;
|
||||
}
|
||||
|
||||
/// Special case for the command REVOKE: it's possible that the current user doesn't have the admin option
|
||||
/// for some of the specified roles but it's still ok because the roles or users from whom the roles will be
|
||||
/// revoked from don't have the specified roles granted either.
|
||||
///
|
||||
/// For example, to execute
|
||||
/// GRANT role2 TO role1
|
||||
/// REVOKE ALL FROM role1
|
||||
/// the current user needs to have only 'role2' to be granted with admin option (not all the roles).
|
||||
GrantedRoles all_granted_roles;
|
||||
for (const auto & id : grantees_from_query)
|
||||
{
|
||||
auto entity = access_control.tryRead(id);
|
||||
if (auto role = typeid_cast<RolePtr>(entity))
|
||||
all_granted_roles.makeUnion(role->granted_roles);
|
||||
else if (auto user = typeid_cast<UserPtr>(entity))
|
||||
all_granted_roles.makeUnion(user->granted_roles);
|
||||
}
|
||||
|
||||
const auto & all_granted_roles_set = query.admin_option ? all_granted_roles.getGrantedWithAdminOption() : all_granted_roles.getGranted();
|
||||
if (roles_from_query.all)
|
||||
boost::range::set_difference(all_granted_roles_set, roles_from_query.except_ids, std::back_inserter(matching_ids));
|
||||
else
|
||||
boost::range::remove_erase_if(matching_ids, [&](const UUID & id) { return !all_granted_roles_set.count(id); });
|
||||
access.checkAdminOption(matching_ids);
|
||||
return matching_ids;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BlockIO InterpreterGrantQuery::execute()
|
||||
{
|
||||
auto & query = query_ptr->as<ASTGrantQuery &>();
|
||||
query.replaceCurrentUserTagWithName(context.getUserName());
|
||||
|
||||
if (!query.cluster.empty())
|
||||
return executeDDLQueryOnCluster(query_ptr, context, query.access_rights_elements, true);
|
||||
query.replaceCurrentUserTag(context.getUserName());
|
||||
query.access_rights_elements.eraseNonGrantable();
|
||||
|
||||
if (!query.access_rights_elements.sameOptions())
|
||||
throw Exception("Elements of an ASTGrantQuery are expected to have the same options", ErrorCodes::LOGICAL_ERROR);
|
||||
if (!query.access_rights_elements.empty() && query.access_rights_elements[0].is_partial_revoke && !query.is_revoke)
|
||||
throw Exception("A partial revoke should be revoked, not granted", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
auto access = context.getAccess();
|
||||
auto & access_control = context.getAccessControlManager();
|
||||
query.replaceEmptyDatabaseWithCurrent(context.getCurrentDatabase());
|
||||
|
||||
RolesOrUsersSet roles_set;
|
||||
std::optional<RolesOrUsersSet> roles_set;
|
||||
if (query.roles)
|
||||
roles_set = RolesOrUsersSet{*query.roles, access_control};
|
||||
|
||||
std::vector<UUID> to_roles = RolesOrUsersSet{*query.to_roles, access_control, context.getUserID()}.getMatchingIDs(access_control);
|
||||
std::vector<UUID> grantees = RolesOrUsersSet{*query.grantees, access_control, context.getUserID()}.getMatchingIDs(access_control);
|
||||
|
||||
/// Check if the current user has corresponding roles granted with admin option.
|
||||
std::vector<UUID> roles;
|
||||
if (roles_set)
|
||||
roles = getRoleIDsAndCheckAdminOption(access_control, *context.getAccess(), query, *roles_set, grantees);
|
||||
|
||||
if (!query.cluster.empty())
|
||||
{
|
||||
/// To execute the command GRANT the current user needs to have the access granted with GRANT OPTION.
|
||||
auto required_access = query.access_rights_elements;
|
||||
std::for_each(required_access.begin(), required_access.end(), [&](AccessRightsElement & element) { element.grant_option = true; });
|
||||
return executeDDLQueryOnCluster(query_ptr, context, std::move(required_access));
|
||||
}
|
||||
|
||||
query.replaceEmptyDatabase(context.getCurrentDatabase());
|
||||
|
||||
/// Check if the current user has corresponding access rights with grant option.
|
||||
if (!query.access_rights_elements.empty())
|
||||
{
|
||||
query.access_rights_elements.removeNonGrantableFlags();
|
||||
checkGrantOption(access_control, *context.getAccess(), query, grantees);
|
||||
|
||||
/// Special case for REVOKE: it's possible that the current user doesn't have the grant option for all
|
||||
/// the specified access rights and that's ok because the roles or users which the access rights
|
||||
/// will be revoked from don't have the specified access rights either.
|
||||
///
|
||||
/// For example, to execute
|
||||
/// GRANT ALL ON mydb.* TO role1
|
||||
/// REVOKE ALL ON *.* FROM role1
|
||||
/// the current user needs to have access rights only for the 'mydb' database.
|
||||
if ((query.kind == Kind::REVOKE) && !access->hasGrantOption(query.access_rights_elements))
|
||||
{
|
||||
AccessRights max_access;
|
||||
for (const auto & id : to_roles)
|
||||
{
|
||||
auto entity = access_control.tryRead(id);
|
||||
if (auto role = typeid_cast<RolePtr>(entity))
|
||||
max_access.makeUnion(role->access);
|
||||
else if (auto user = typeid_cast<UserPtr>(entity))
|
||||
max_access.makeUnion(user->access);
|
||||
}
|
||||
AccessRights access_to_revoke;
|
||||
if (query.grant_option)
|
||||
access_to_revoke.grantWithGrantOption(query.access_rights_elements);
|
||||
else
|
||||
access_to_revoke.grant(query.access_rights_elements);
|
||||
access_to_revoke.makeIntersection(max_access);
|
||||
AccessRightsElements filtered_access_to_revoke;
|
||||
for (auto & element : access_to_revoke.getElements())
|
||||
{
|
||||
if ((element.kind == Kind::GRANT) && (element.grant_option || !query.grant_option))
|
||||
filtered_access_to_revoke.emplace_back(std::move(element));
|
||||
}
|
||||
query.access_rights_elements = std::move(filtered_access_to_revoke);
|
||||
}
|
||||
|
||||
access->checkGrantOption(query.access_rights_elements);
|
||||
}
|
||||
|
||||
/// Check if the current user has corresponding roles granted with admin option.
|
||||
std::vector<UUID> roles_to_grant_or_revoke;
|
||||
if (!roles_set.empty())
|
||||
{
|
||||
bool all = roles_set.all;
|
||||
if (!all)
|
||||
roles_to_grant_or_revoke = roles_set.getMatchingIDs();
|
||||
|
||||
/// Special case for REVOKE: it's possible that the current user doesn't have the admin option for all
|
||||
/// the specified roles and that's ok because the roles or users which the roles will be revoked from
|
||||
/// don't have the specified roles granted either.
|
||||
///
|
||||
/// For example, to execute
|
||||
/// GRANT role2 TO role1
|
||||
/// REVOKE ALL FROM role1
|
||||
/// the current user needs to have only 'role2' to be granted with admin option (not all the roles).
|
||||
if ((query.kind == Kind::REVOKE) && (roles_set.all || !access->hasAdminOption(roles_to_grant_or_revoke)))
|
||||
{
|
||||
auto & roles_to_revoke = roles_to_grant_or_revoke;
|
||||
boost::container::flat_set<UUID> max_roles;
|
||||
for (const auto & id : to_roles)
|
||||
{
|
||||
auto entity = access_control.tryRead(id);
|
||||
auto add_to_max_roles = [&](const GrantedRoles & granted_roles)
|
||||
{
|
||||
if (query.admin_option)
|
||||
max_roles.insert(granted_roles.roles_with_admin_option.begin(), granted_roles.roles_with_admin_option.end());
|
||||
else
|
||||
max_roles.insert(granted_roles.roles.begin(), granted_roles.roles.end());
|
||||
};
|
||||
if (auto role = typeid_cast<RolePtr>(entity))
|
||||
add_to_max_roles(role->granted_roles);
|
||||
else if (auto user = typeid_cast<UserPtr>(entity))
|
||||
add_to_max_roles(user->granted_roles);
|
||||
}
|
||||
if (roles_set.all)
|
||||
boost::range::set_difference(max_roles, roles_set.except_ids, std::back_inserter(roles_to_revoke));
|
||||
else
|
||||
boost::range::remove_erase_if(roles_to_revoke, [&](const UUID & id) { return !max_roles.count(id); });
|
||||
}
|
||||
|
||||
access->checkAdminOption(roles_to_grant_or_revoke);
|
||||
}
|
||||
|
||||
/// Update roles and users listed in `to_roles`.
|
||||
/// Update roles and users listed in `grantees`.
|
||||
auto update_func = [&](const AccessEntityPtr & entity) -> AccessEntityPtr
|
||||
{
|
||||
auto clone = entity->clone();
|
||||
updateFromQueryImpl(*clone, query, roles_to_grant_or_revoke);
|
||||
updateFromQueryImpl(*clone, query, roles);
|
||||
return clone;
|
||||
};
|
||||
|
||||
access_control.update(to_roles, update_func);
|
||||
access_control.update(grantees, update_func);
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -213,10 +249,10 @@ void InterpreterGrantQuery::updateRoleFromQuery(Role & role, const ASTGrantQuery
|
||||
void InterpreterGrantQuery::extendQueryLogElemImpl(QueryLogElement & elem, const ASTPtr & /*ast*/, const Context &) const
|
||||
{
|
||||
auto & query = query_ptr->as<ASTGrantQuery &>();
|
||||
if (query.kind == Kind::GRANT)
|
||||
elem.query_kind = "Grant";
|
||||
else if (query.kind == Kind::REVOKE)
|
||||
if (query.is_revoke)
|
||||
elem.query_kind = "Revoke";
|
||||
else
|
||||
elem.query_kind = "Grant";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,20 +39,18 @@ void InterpreterSetRoleQuery::setRole(const ASTSetRoleQuery & query)
|
||||
else
|
||||
{
|
||||
RolesOrUsersSet roles_from_query{*query.roles, access_control};
|
||||
boost::container::flat_set<UUID> new_current_roles;
|
||||
std::vector<UUID> new_current_roles;
|
||||
if (roles_from_query.all)
|
||||
{
|
||||
for (const auto & id : user->granted_roles.roles)
|
||||
if (roles_from_query.match(id))
|
||||
new_current_roles.emplace(id);
|
||||
new_current_roles = user->granted_roles.findGranted(roles_from_query);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto & id : roles_from_query.getMatchingIDs())
|
||||
{
|
||||
if (!user->granted_roles.roles.count(id))
|
||||
if (!user->granted_roles.isGranted(id))
|
||||
throw Exception("Role should be granted to set current", ErrorCodes::SET_NON_GRANTED_ROLE);
|
||||
new_current_roles.emplace(id);
|
||||
new_current_roles.emplace_back(id);
|
||||
}
|
||||
}
|
||||
session_context.setCurrentRoles(new_current_roles);
|
||||
@ -85,7 +83,7 @@ void InterpreterSetRoleQuery::updateUserSetDefaultRoles(User & user, const Roles
|
||||
{
|
||||
for (const auto & id : roles_from_query.getMatchingIDs())
|
||||
{
|
||||
if (!user.granted_roles.roles.count(id))
|
||||
if (!user.granted_roles.isGranted(id))
|
||||
throw Exception("Role should be granted to set default", ErrorCodes::SET_NON_GRANTED_ROLE);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ BlockIO InterpreterShowAccessEntitiesQuery::execute()
|
||||
String InterpreterShowAccessEntitiesQuery::getRewrittenQuery() const
|
||||
{
|
||||
auto & query = query_ptr->as<ASTShowAccessEntitiesQuery &>();
|
||||
query.replaceEmptyDatabaseWithCurrent(context.getCurrentDatabase());
|
||||
query.replaceEmptyDatabase(context.getCurrentDatabase());
|
||||
String origin;
|
||||
String expr = "*";
|
||||
String filter, order;
|
||||
|
@ -263,7 +263,7 @@ std::vector<AccessEntityPtr> InterpreterShowCreateAccessEntityQuery::getEntities
|
||||
auto & show_query = query_ptr->as<ASTShowCreateAccessEntityQuery &>();
|
||||
const auto & access_control = context.getAccessControlManager();
|
||||
context.checkAccess(getRequiredAccess());
|
||||
show_query.replaceEmptyDatabaseWithCurrent(context.getCurrentDatabase());
|
||||
show_query.replaceEmptyDatabase(context.getCurrentDatabase());
|
||||
std::vector<AccessEntityPtr> entities;
|
||||
|
||||
if (show_query.all)
|
||||
|
@ -32,56 +32,50 @@ namespace
|
||||
{
|
||||
ASTs res;
|
||||
|
||||
std::shared_ptr<ASTRolesOrUsersSet> to_roles = std::make_shared<ASTRolesOrUsersSet>();
|
||||
to_roles->names.push_back(grantee.getName());
|
||||
std::shared_ptr<ASTRolesOrUsersSet> grantees = std::make_shared<ASTRolesOrUsersSet>();
|
||||
grantees->names.push_back(grantee.getName());
|
||||
|
||||
std::shared_ptr<ASTGrantQuery> current_query = nullptr;
|
||||
|
||||
auto elements = grantee.access.getElements();
|
||||
for (const auto & element : elements)
|
||||
for (const auto & element : grantee.access.getElements())
|
||||
{
|
||||
if (element.empty())
|
||||
continue;
|
||||
|
||||
if (current_query)
|
||||
{
|
||||
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)
|
||||
bool continue_with_current_query = element.sameDatabaseAndTable(prev_element) && element.sameOptions(prev_element);
|
||||
if (!continue_with_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;
|
||||
current_query->grantees = grantees;
|
||||
current_query->attach_mode = attach_mode;
|
||||
if (element.is_partial_revoke)
|
||||
current_query->is_revoke = true;
|
||||
res.push_back(current_query);
|
||||
}
|
||||
|
||||
current_query->access_rights_elements.emplace_back(std::move(element));
|
||||
}
|
||||
|
||||
auto grants_roles = grantee.granted_roles.getGrants();
|
||||
|
||||
for (bool admin_option : {false, true})
|
||||
for (const auto & element : grantee.granted_roles.getElements())
|
||||
{
|
||||
const auto & roles = admin_option ? grants_roles.grants_with_admin_option : grants_roles.grants;
|
||||
if (roles.empty())
|
||||
if (element.empty())
|
||||
continue;
|
||||
|
||||
auto grant_query = std::make_shared<ASTGrantQuery>();
|
||||
using Kind = ASTGrantQuery::Kind;
|
||||
grant_query->kind = Kind::GRANT;
|
||||
grant_query->attach = attach_mode;
|
||||
grant_query->admin_option = admin_option;
|
||||
grant_query->to_roles = to_roles;
|
||||
grant_query->grantees = grantees;
|
||||
grant_query->admin_option = element.admin_option;
|
||||
grant_query->attach_mode = attach_mode;
|
||||
if (attach_mode)
|
||||
grant_query->roles = RolesOrUsersSet{roles}.toAST();
|
||||
grant_query->roles = RolesOrUsersSet{element.ids}.toAST();
|
||||
else
|
||||
grant_query->roles = RolesOrUsersSet{roles}.toASTWithNames(*manager);
|
||||
grant_query->roles = RolesOrUsersSet{element.ids}.toASTWithNames(*manager);
|
||||
res.push_back(std::move(grant_query));
|
||||
}
|
||||
|
||||
|
@ -50,12 +50,12 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & cont
|
||||
return executeDDLQueryOnCluster(query_ptr_, context, {});
|
||||
}
|
||||
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, const AccessRightsElements & query_requires_access, bool query_requires_grant_option)
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, const AccessRightsElements & query_requires_access)
|
||||
{
|
||||
return executeDDLQueryOnCluster(query_ptr, context, AccessRightsElements{query_requires_access}, query_requires_grant_option);
|
||||
return executeDDLQueryOnCluster(query_ptr, context, AccessRightsElements{query_requires_access});
|
||||
}
|
||||
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & context, AccessRightsElements && query_requires_access, bool query_requires_grant_option)
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & context, AccessRightsElements && query_requires_access)
|
||||
{
|
||||
/// Remove FORMAT <fmt> and INTO OUTFILE <file> if exists
|
||||
ASTPtr query_ptr = query_ptr_->clone();
|
||||
@ -154,10 +154,7 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & cont
|
||||
visitor.visitDDL(query_ptr);
|
||||
|
||||
/// Check access rights, assume that all servers have the same users config
|
||||
if (query_requires_grant_option)
|
||||
context.getAccess()->checkGrantOption(query_requires_access);
|
||||
else
|
||||
context.checkAccess(query_requires_access);
|
||||
context.checkAccess(query_requires_access);
|
||||
|
||||
DDLLogEntry entry;
|
||||
entry.hosts = std::move(hosts);
|
||||
|
@ -21,8 +21,8 @@ bool isSupportedAlterType(int type);
|
||||
/// Pushes distributed DDL query to the queue.
|
||||
/// Returns DDLQueryStatusInputStream, which reads results of query execution on each host in the cluster.
|
||||
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);
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, const AccessRightsElements & query_requires_access);
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, AccessRightsElements && query_requires_access);
|
||||
|
||||
|
||||
class DDLQueryStatusInputStream final : public IBlockInputStream
|
||||
|
@ -185,10 +185,10 @@ void ASTCreateQuotaQuery::formatImpl(const FormatSettings & settings, FormatStat
|
||||
}
|
||||
|
||||
|
||||
void ASTCreateQuotaQuery::replaceCurrentUserTagWithName(const String & current_user_name) const
|
||||
void ASTCreateQuotaQuery::replaceCurrentUserTag(const String & current_user_name) const
|
||||
{
|
||||
if (roles)
|
||||
roles->replaceCurrentUserTagWithName(current_user_name);
|
||||
roles->replaceCurrentUserTag(current_user_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ public:
|
||||
String getID(char) const override;
|
||||
ASTPtr clone() const override;
|
||||
void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
|
||||
void replaceCurrentUserTagWithName(const String & current_user_name) const;
|
||||
void replaceCurrentUserTag(const String & current_user_name) const;
|
||||
ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster<ASTCreateQuotaQuery>(clone()); }
|
||||
};
|
||||
}
|
||||
|
@ -169,15 +169,15 @@ void ASTCreateRowPolicyQuery::formatImpl(const FormatSettings & settings, Format
|
||||
}
|
||||
|
||||
|
||||
void ASTCreateRowPolicyQuery::replaceCurrentUserTagWithName(const String & current_user_name) const
|
||||
void ASTCreateRowPolicyQuery::replaceCurrentUserTag(const String & current_user_name) const
|
||||
{
|
||||
if (roles)
|
||||
roles->replaceCurrentUserTagWithName(current_user_name);
|
||||
roles->replaceCurrentUserTag(current_user_name);
|
||||
}
|
||||
|
||||
void ASTCreateRowPolicyQuery::replaceEmptyDatabaseWithCurrent(const String & current_database) const
|
||||
void ASTCreateRowPolicyQuery::replaceEmptyDatabase(const String & current_database) const
|
||||
{
|
||||
if (names)
|
||||
names->replaceEmptyDatabaseWithCurrent(current_database);
|
||||
names->replaceEmptyDatabase(current_database);
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ public:
|
||||
void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
|
||||
ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster<ASTCreateRowPolicyQuery>(clone()); }
|
||||
|
||||
void replaceCurrentUserTagWithName(const String & current_user_name) const;
|
||||
void replaceEmptyDatabaseWithCurrent(const String & current_database) const;
|
||||
void replaceCurrentUserTag(const String & current_user_name) const;
|
||||
void replaceEmptyDatabase(const String & current_database) const;
|
||||
};
|
||||
}
|
||||
|
@ -86,9 +86,9 @@ void ASTCreateSettingsProfileQuery::formatImpl(const FormatSettings & format, Fo
|
||||
}
|
||||
|
||||
|
||||
void ASTCreateSettingsProfileQuery::replaceCurrentUserTagWithName(const String & current_user_name) const
|
||||
void ASTCreateSettingsProfileQuery::replaceCurrentUserTag(const String & current_user_name) const
|
||||
{
|
||||
if (to_roles)
|
||||
to_roles->replaceCurrentUserTagWithName(current_user_name);
|
||||
to_roles->replaceCurrentUserTag(current_user_name);
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ public:
|
||||
String getID(char) const override;
|
||||
ASTPtr clone() const override;
|
||||
void formatImpl(const FormatSettings & format, FormatState &, FormatStateStacked) const override;
|
||||
void replaceCurrentUserTagWithName(const String & current_user_name) const;
|
||||
void replaceCurrentUserTag(const String & current_user_name) const;
|
||||
ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster<ASTCreateSettingsProfileQuery>(clone()); }
|
||||
};
|
||||
}
|
||||
|
@ -19,11 +19,11 @@ class ASTSettingsProfileElements;
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
*
|
||||
* ALTER USER [IF EXISTS] name
|
||||
* [RENAME TO new_name]
|
||||
* [NOT IDENTIFIED | IDENTIFIED {[WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash}] BY {'password'|'hash'}}|{WITH ldap SERVER 'server_name'}|{WITH kerberos [REALM 'realm']}]
|
||||
* [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
* [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
* [RENAME TO new_name]
|
||||
* [NOT IDENTIFIED | IDENTIFIED {[WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash}] BY {'password'|'hash'}}|{WITH ldap SERVER 'server_name'}|{WITH kerberos [REALM 'realm']}]
|
||||
* [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
* [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
*/
|
||||
class ASTCreateUserQuery : public IAST, public ASTQueryWithOnCluster
|
||||
{
|
||||
@ -46,7 +46,6 @@ public:
|
||||
std::optional<AllowedClientHosts> remove_hosts;
|
||||
|
||||
std::shared_ptr<ASTRolesOrUsersSet> default_roles;
|
||||
|
||||
std::shared_ptr<ASTSettingsProfileElements> settings;
|
||||
|
||||
String getID(char) const override;
|
||||
|
@ -54,9 +54,9 @@ void ASTDropAccessEntityQuery::formatImpl(const FormatSettings & settings, Forma
|
||||
}
|
||||
|
||||
|
||||
void ASTDropAccessEntityQuery::replaceEmptyDatabaseWithCurrent(const String & current_database) const
|
||||
void ASTDropAccessEntityQuery::replaceEmptyDatabase(const String & current_database) const
|
||||
{
|
||||
if (row_policy_names)
|
||||
row_policy_names->replaceEmptyDatabaseWithCurrent(current_database);
|
||||
row_policy_names->replaceEmptyDatabase(current_database);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,6 @@ public:
|
||||
void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
|
||||
ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster<ASTDropAccessEntityQuery>(clone()); }
|
||||
|
||||
void replaceEmptyDatabaseWithCurrent(const String & current_database) const;
|
||||
void replaceEmptyDatabase(const String & current_database) const;
|
||||
};
|
||||
}
|
||||
|
@ -27,7 +27,26 @@ namespace
|
||||
}
|
||||
|
||||
|
||||
void formatAccessRightsElements(const AccessRightsElements & elements, const IAST::FormatSettings & settings)
|
||||
void formatONClause(const String & database, bool any_database, const String & table, bool any_table, const IAST::FormatSettings & settings)
|
||||
{
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << "ON " << (settings.hilite ? IAST::hilite_none : "");
|
||||
if (any_database)
|
||||
{
|
||||
settings.ostr << "*.*";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!database.empty())
|
||||
settings.ostr << backQuoteIfNeed(database) << ".";
|
||||
if (any_table)
|
||||
settings.ostr << "*";
|
||||
else
|
||||
settings.ostr << backQuoteIfNeed(table);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void formatElementsWithoutOptions(const AccessRightsElements & elements, const IAST::FormatSettings & settings)
|
||||
{
|
||||
bool no_output = true;
|
||||
for (size_t i = 0; i != elements.size(); ++i)
|
||||
@ -58,31 +77,14 @@ namespace
|
||||
|
||||
if (!next_element_on_same_db_and_table)
|
||||
{
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " ON " << (settings.hilite ? IAST::hilite_none : "");
|
||||
if (element.any_database)
|
||||
settings.ostr << "*.";
|
||||
else if (!element.database.empty())
|
||||
settings.ostr << backQuoteIfNeed(element.database) + ".";
|
||||
|
||||
if (element.any_table)
|
||||
settings.ostr << "*";
|
||||
else
|
||||
settings.ostr << backQuoteIfNeed(element.table);
|
||||
settings.ostr << " ";
|
||||
formatONClause(element.database, element.any_database, element.table, element.any_table, settings);
|
||||
}
|
||||
}
|
||||
|
||||
if (no_output)
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << "USAGE ON " << (settings.hilite ? IAST::hilite_none : "") << "*.*";
|
||||
}
|
||||
|
||||
|
||||
void formatToRoles(const ASTRolesOrUsersSet & to_roles, ASTGrantQuery::Kind kind, const IAST::FormatSettings & settings)
|
||||
{
|
||||
using Kind = ASTGrantQuery::Kind;
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << ((kind == Kind::GRANT) ? " TO " : " FROM ")
|
||||
<< (settings.hilite ? IAST::hilite_none : "");
|
||||
to_roles.format(settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -100,12 +102,18 @@ ASTPtr ASTGrantQuery::clone() const
|
||||
|
||||
void ASTGrantQuery::formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const
|
||||
{
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << (attach ? "ATTACH " : "") << ((kind == Kind::GRANT) ? "GRANT" : "REVOKE")
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << (attach_mode ? "ATTACH " : "") << (is_revoke ? "REVOKE" : "GRANT")
|
||||
<< (settings.hilite ? IAST::hilite_none : "");
|
||||
|
||||
if (!access_rights_elements.sameOptions())
|
||||
throw Exception("Elements of an ASTGrantQuery are expected to have the same options", ErrorCodes::LOGICAL_ERROR);
|
||||
if (!access_rights_elements.empty() && access_rights_elements[0].is_partial_revoke && !is_revoke)
|
||||
throw Exception("A partial revoke should be revoked, not granted", ErrorCodes::LOGICAL_ERROR);
|
||||
bool grant_option = !access_rights_elements.empty() && access_rights_elements[0].grant_option;
|
||||
|
||||
formatOnCluster(settings);
|
||||
|
||||
if (kind == Kind::REVOKE)
|
||||
if (is_revoke)
|
||||
{
|
||||
if (grant_option)
|
||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << " GRANT OPTION FOR" << (settings.hilite ? hilite_none : "");
|
||||
@ -113,18 +121,21 @@ void ASTGrantQuery::formatImpl(const FormatSettings & settings, FormatState &, F
|
||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << " ADMIN OPTION FOR" << (settings.hilite ? hilite_none : "");
|
||||
}
|
||||
|
||||
if (roles && !access_rights_elements.empty())
|
||||
throw Exception("Either roles or access rights elements should be set", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
settings.ostr << " ";
|
||||
if (roles)
|
||||
{
|
||||
roles->format(settings);
|
||||
if (!access_rights_elements.empty())
|
||||
throw Exception("ASTGrantQuery can contain either roles or access rights elements to grant or revoke, not both of them", ErrorCodes::LOGICAL_ERROR);
|
||||
}
|
||||
else
|
||||
formatAccessRightsElements(access_rights_elements, settings);
|
||||
formatElementsWithoutOptions(access_rights_elements, settings);
|
||||
|
||||
formatToRoles(*to_roles, kind, settings);
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << (is_revoke ? " FROM " : " TO ")
|
||||
<< (settings.hilite ? IAST::hilite_none : "");
|
||||
grantees->format(settings);
|
||||
|
||||
if (kind == Kind::GRANT)
|
||||
if (!is_revoke)
|
||||
{
|
||||
if (grant_option)
|
||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << " WITH GRANT OPTION" << (settings.hilite ? hilite_none : "");
|
||||
@ -134,16 +145,16 @@ void ASTGrantQuery::formatImpl(const FormatSettings & settings, FormatState &, F
|
||||
}
|
||||
|
||||
|
||||
void ASTGrantQuery::replaceEmptyDatabaseWithCurrent(const String & current_database)
|
||||
void ASTGrantQuery::replaceEmptyDatabase(const String & current_database)
|
||||
{
|
||||
access_rights_elements.replaceEmptyDatabase(current_database);
|
||||
}
|
||||
|
||||
|
||||
void ASTGrantQuery::replaceCurrentUserTagWithName(const String & current_user_name) const
|
||||
void ASTGrantQuery::replaceCurrentUserTag(const String & current_user_name) const
|
||||
{
|
||||
if (to_roles)
|
||||
to_roles->replaceCurrentUserTagWithName(current_user_name);
|
||||
if (grantees)
|
||||
grantees->replaceCurrentUserTag(current_user_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,20 +19,18 @@ class ASTRolesOrUsersSet;
|
||||
class ASTGrantQuery : public IAST, public ASTQueryWithOnCluster
|
||||
{
|
||||
public:
|
||||
using Kind = AccessRightsElementWithOptions::Kind;
|
||||
Kind kind = Kind::GRANT;
|
||||
bool attach = false;
|
||||
bool attach_mode = false;
|
||||
bool is_revoke = false;
|
||||
AccessRightsElements access_rights_elements;
|
||||
std::shared_ptr<ASTRolesOrUsersSet> roles;
|
||||
std::shared_ptr<ASTRolesOrUsersSet> to_roles;
|
||||
bool grant_option = false;
|
||||
bool admin_option = false;
|
||||
std::shared_ptr<ASTRolesOrUsersSet> grantees;
|
||||
|
||||
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;
|
||||
void replaceEmptyDatabase(const String & current_database);
|
||||
void replaceCurrentUserTag(const String & current_user_name) const;
|
||||
ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster<ASTGrantQuery>(clone()); }
|
||||
};
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace DB
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void formatRoleNameOrID(const String & str, bool is_id, const IAST::FormatSettings & settings)
|
||||
void formatNameOrID(const String & str, bool is_id, const IAST::FormatSettings & settings)
|
||||
{
|
||||
if (is_id)
|
||||
{
|
||||
@ -30,6 +30,7 @@ void ASTRolesOrUsersSet::formatImpl(const FormatSettings & settings, FormatState
|
||||
}
|
||||
|
||||
bool need_comma = false;
|
||||
|
||||
if (all)
|
||||
{
|
||||
if (std::exchange(need_comma, true))
|
||||
@ -38,11 +39,11 @@ void ASTRolesOrUsersSet::formatImpl(const FormatSettings & settings, FormatState
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto & role : names)
|
||||
for (const auto & name : names)
|
||||
{
|
||||
if (std::exchange(need_comma, true))
|
||||
settings.ostr << ", ";
|
||||
formatRoleNameOrID(role, id_mode, settings);
|
||||
formatNameOrID(name, id_mode, settings);
|
||||
}
|
||||
|
||||
if (current_user)
|
||||
@ -58,11 +59,11 @@ void ASTRolesOrUsersSet::formatImpl(const FormatSettings & settings, FormatState
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " EXCEPT " << (settings.hilite ? IAST::hilite_none : "");
|
||||
need_comma = false;
|
||||
|
||||
for (const auto & except_role : except_names)
|
||||
for (const auto & name : except_names)
|
||||
{
|
||||
if (std::exchange(need_comma, true))
|
||||
settings.ostr << ", ";
|
||||
formatRoleNameOrID(except_role, id_mode, settings);
|
||||
formatNameOrID(name, id_mode, settings);
|
||||
}
|
||||
|
||||
if (except_current_user)
|
||||
@ -75,7 +76,7 @@ void ASTRolesOrUsersSet::formatImpl(const FormatSettings & settings, FormatState
|
||||
}
|
||||
|
||||
|
||||
void ASTRolesOrUsersSet::replaceCurrentUserTagWithName(const String & current_user_name)
|
||||
void ASTRolesOrUsersSet::replaceCurrentUserTag(const String & current_user_name)
|
||||
{
|
||||
if (current_user)
|
||||
{
|
||||
|
@ -9,22 +9,23 @@ namespace DB
|
||||
using Strings = std::vector<String>;
|
||||
|
||||
/// Represents a set of users/roles like
|
||||
/// {user_name | role_name | CURRENT_USER} [,...] | NONE | ALL | ALL EXCEPT {user_name | role_name | CURRENT_USER} [,...]
|
||||
/// {user_name | role_name | CURRENT_USER | ALL | NONE} [,...]
|
||||
/// [EXCEPT {user_name | role_name | CURRENT_USER | ALL | NONE} [,...]]
|
||||
class ASTRolesOrUsersSet : public IAST
|
||||
{
|
||||
public:
|
||||
bool all = false;
|
||||
Strings names;
|
||||
bool current_user = false;
|
||||
bool all = false;
|
||||
Strings except_names;
|
||||
bool except_current_user = false;
|
||||
|
||||
bool id_mode = false; /// true if `names` and `except_names` keep UUIDs, not names.
|
||||
bool allow_role_names = true; /// true if this set can contain names of roles.
|
||||
bool allow_user_names = true; /// true if this set can contain names of users.
|
||||
bool allow_users = true; /// whether this set can contain names of users
|
||||
bool allow_roles = true; /// whether this set can contain names of roles
|
||||
bool id_mode = false; /// whether this set keep UUIDs instead of names
|
||||
|
||||
bool empty() const { return names.empty() && !current_user && !all; }
|
||||
void replaceCurrentUserTagWithName(const String & current_user_name);
|
||||
void replaceCurrentUserTag(const String & current_user_name);
|
||||
|
||||
String getID(char) const override { return "RolesOrUsersSet"; }
|
||||
ASTPtr clone() const override { return std::make_shared<ASTRolesOrUsersSet>(*this); }
|
||||
|
@ -23,7 +23,7 @@ void ASTRowPolicyName::formatImpl(const FormatSettings & settings, FormatState &
|
||||
}
|
||||
|
||||
|
||||
void ASTRowPolicyName::replaceEmptyDatabaseWithCurrent(const String & current_database)
|
||||
void ASTRowPolicyName::replaceEmptyDatabase(const String & current_database)
|
||||
{
|
||||
if (name_parts.database.empty())
|
||||
name_parts.database = current_database;
|
||||
@ -125,7 +125,7 @@ Strings ASTRowPolicyNames::toStrings() const
|
||||
}
|
||||
|
||||
|
||||
void ASTRowPolicyNames::replaceEmptyDatabaseWithCurrent(const String & current_database)
|
||||
void ASTRowPolicyNames::replaceEmptyDatabase(const String & current_database)
|
||||
{
|
||||
for (auto & np : name_parts)
|
||||
if (np.database.empty())
|
||||
|
@ -22,7 +22,7 @@ public:
|
||||
void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
|
||||
ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster<ASTRowPolicyName>(clone()); }
|
||||
|
||||
void replaceEmptyDatabaseWithCurrent(const String & current_database);
|
||||
void replaceEmptyDatabase(const String & current_database);
|
||||
};
|
||||
|
||||
|
||||
@ -44,6 +44,6 @@ public:
|
||||
void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
|
||||
ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster<ASTRowPolicyNames>(clone()); }
|
||||
|
||||
void replaceEmptyDatabaseWithCurrent(const String & current_database);
|
||||
void replaceEmptyDatabase(const String & current_database);
|
||||
};
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ void ASTShowAccessEntitiesQuery::formatQueryImpl(const FormatSettings & settings
|
||||
}
|
||||
|
||||
|
||||
void ASTShowAccessEntitiesQuery::replaceEmptyDatabaseWithCurrent(const String & current_database)
|
||||
void ASTShowAccessEntitiesQuery::replaceEmptyDatabase(const String & current_database)
|
||||
{
|
||||
if (database_and_table_name)
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ public:
|
||||
String getID(char) const override;
|
||||
ASTPtr clone() const override { return std::make_shared<ASTShowAccessEntitiesQuery>(*this); }
|
||||
|
||||
void replaceEmptyDatabaseWithCurrent(const String & current_database);
|
||||
void replaceEmptyDatabase(const String & current_database);
|
||||
|
||||
protected:
|
||||
void formatQueryImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
|
||||
|
@ -72,10 +72,10 @@ void ASTShowCreateAccessEntityQuery::formatQueryImpl(const FormatSettings & sett
|
||||
}
|
||||
|
||||
|
||||
void ASTShowCreateAccessEntityQuery::replaceEmptyDatabaseWithCurrent(const String & current_database)
|
||||
void ASTShowCreateAccessEntityQuery::replaceEmptyDatabase(const String & current_database)
|
||||
{
|
||||
if (row_policy_names)
|
||||
row_policy_names->replaceEmptyDatabaseWithCurrent(current_database);
|
||||
row_policy_names->replaceEmptyDatabase(current_database);
|
||||
|
||||
if (database_and_table_name)
|
||||
{
|
||||
|
@ -40,7 +40,7 @@ public:
|
||||
String getID(char) const override;
|
||||
ASTPtr clone() const override;
|
||||
|
||||
void replaceEmptyDatabaseWithCurrent(const String & current_database);
|
||||
void replaceEmptyDatabase(const String & current_database);
|
||||
|
||||
protected:
|
||||
String getKeyword() const;
|
||||
|
@ -226,7 +226,7 @@ namespace
|
||||
{
|
||||
ASTPtr node;
|
||||
ParserRolesOrUsersSet roles_p;
|
||||
roles_p.allowAll().allowRoleNames().allowUserNames().allowCurrentUser().useIDMode(id_mode);
|
||||
roles_p.allowAll().allowRoles().allowUsers().allowCurrentUser().useIDMode(id_mode);
|
||||
if (!ParserKeyword{"TO"}.ignore(pos, expected) || !roles_p.parse(pos, node, expected))
|
||||
return false;
|
||||
|
||||
|
@ -187,7 +187,7 @@ namespace
|
||||
return false;
|
||||
|
||||
ParserRolesOrUsersSet roles_p;
|
||||
roles_p.allowAll().allowRoleNames().allowUserNames().allowCurrentUser().useIDMode(id_mode);
|
||||
roles_p.allowAll().allowRoles().allowUsers().allowCurrentUser().useIDMode(id_mode);
|
||||
if (!roles_p.parse(pos, ast, expected))
|
||||
return false;
|
||||
|
||||
|
@ -53,7 +53,7 @@ namespace
|
||||
return false;
|
||||
|
||||
ParserRolesOrUsersSet roles_p;
|
||||
roles_p.allowAll().allowRoleNames().allowUserNames().allowCurrentUser().useIDMode(id_mode);
|
||||
roles_p.allowAll().allowRoles().allowUsers().allowCurrentUser().useIDMode(id_mode);
|
||||
if (!roles_p.parse(pos, ast, expected))
|
||||
return false;
|
||||
|
||||
|
@ -246,12 +246,12 @@ namespace
|
||||
|
||||
ASTPtr ast;
|
||||
ParserRolesOrUsersSet default_roles_p;
|
||||
default_roles_p.allowAll().allowRoleNames().useIDMode(id_mode);
|
||||
default_roles_p.allowAll().allowRoles().useIDMode(id_mode);
|
||||
if (!default_roles_p.parse(pos, ast, expected))
|
||||
return false;
|
||||
|
||||
default_roles = typeid_cast<std::shared_ptr<ASTRolesOrUsersSet>>(ast);
|
||||
default_roles->allow_user_names = false;
|
||||
default_roles->allow_users = false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
@ -9,12 +9,14 @@ namespace DB
|
||||
* CREATE USER [IF NOT EXISTS | OR REPLACE] name
|
||||
* [NOT IDENTIFIED | IDENTIFIED {[WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash}] BY {'password'|'hash'}}|{WITH ldap SERVER 'server_name'}|{WITH kerberos [REALM 'realm']}]
|
||||
* [HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
* [DEFAULT ROLE role [,...]]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
*
|
||||
* ALTER USER [IF EXISTS] name
|
||||
* [RENAME TO new_name]
|
||||
* [NOT IDENTIFIED | IDENTIFIED {[WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash}] BY {'password'|'hash'}}|{WITH ldap SERVER 'server_name'}|{WITH kerberos [REALM 'realm']}]
|
||||
* [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
* [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
*/
|
||||
class ParserCreateUserQuery : public IParserBase
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <Parsers/ParserRolesOrUsersSet.h>
|
||||
#include <Parsers/parseDatabaseAndTableName.h>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -20,8 +21,6 @@ namespace ErrorCodes
|
||||
|
||||
namespace
|
||||
{
|
||||
using Kind = ASTGrantQuery::Kind;
|
||||
|
||||
bool parseAccessFlags(IParser::Pos & pos, Expected & expected, AccessFlags & access_flags)
|
||||
{
|
||||
static constexpr auto is_one_of_access_type_words = [](IParser::Pos & pos_)
|
||||
@ -87,7 +86,7 @@ namespace
|
||||
});
|
||||
}
|
||||
|
||||
bool parseAccessTypesWithColumns(IParser::Pos & pos, Expected & expected,
|
||||
bool parseAccessFlagsWithColumns(IParser::Pos & pos, Expected & expected,
|
||||
std::vector<std::pair<AccessFlags, Strings>> & access_and_columns)
|
||||
{
|
||||
std::vector<std::pair<AccessFlags, Strings>> res;
|
||||
@ -112,7 +111,7 @@ namespace
|
||||
}
|
||||
|
||||
|
||||
bool parseAccessRightsElements(IParser::Pos & pos, Expected & expected, AccessRightsElements & elements)
|
||||
bool parseElementsWithoutOptions(IParser::Pos & pos, Expected & expected, AccessRightsElements & elements)
|
||||
{
|
||||
return IParserBase::wrapParseImpl(pos, [&]
|
||||
{
|
||||
@ -121,7 +120,7 @@ namespace
|
||||
auto parse_around_on = [&]
|
||||
{
|
||||
std::vector<std::pair<AccessFlags, Strings>> access_and_columns;
|
||||
if (!parseAccessTypesWithColumns(pos, expected, access_and_columns))
|
||||
if (!parseAccessFlagsWithColumns(pos, expected, access_and_columns))
|
||||
return false;
|
||||
|
||||
if (!ParserKeyword{"ON"}.ignore(pos, expected))
|
||||
@ -157,16 +156,16 @@ namespace
|
||||
}
|
||||
|
||||
|
||||
void removeNonGrantableFlags(AccessRightsElements & elements)
|
||||
void eraseNonGrantable(AccessRightsElements & elements)
|
||||
{
|
||||
for (auto & element : elements)
|
||||
boost::range::remove_erase_if(elements, [](AccessRightsElement & element)
|
||||
{
|
||||
if (element.empty())
|
||||
continue;
|
||||
return true;
|
||||
auto old_flags = element.access_flags;
|
||||
element.removeNonGrantableFlags();
|
||||
element.eraseNonGrantable();
|
||||
if (!element.empty())
|
||||
continue;
|
||||
return false;
|
||||
|
||||
if (!element.any_column)
|
||||
throw Exception(old_flags.toString() + " cannot be granted on the column level", ErrorCodes::INVALID_GRANT);
|
||||
@ -176,17 +175,17 @@ namespace
|
||||
throw Exception(old_flags.toString() + " cannot be granted on the database level", ErrorCodes::INVALID_GRANT);
|
||||
else
|
||||
throw Exception(old_flags.toString() + " cannot be granted", ErrorCodes::INVALID_GRANT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool parseRoles(IParser::Pos & pos, Expected & expected, Kind kind, bool id_mode, std::shared_ptr<ASTRolesOrUsersSet> & roles)
|
||||
bool parseRoles(IParser::Pos & pos, Expected & expected, bool is_revoke, bool id_mode, std::shared_ptr<ASTRolesOrUsersSet> & roles)
|
||||
{
|
||||
return IParserBase::wrapParseImpl(pos, [&]
|
||||
{
|
||||
ParserRolesOrUsersSet roles_p;
|
||||
roles_p.allowRoleNames().useIDMode(id_mode);
|
||||
if (kind == Kind::REVOKE)
|
||||
roles_p.allowRoles().useIDMode(id_mode);
|
||||
if (is_revoke)
|
||||
roles_p.allowAll();
|
||||
|
||||
ASTPtr ast;
|
||||
@ -199,28 +198,20 @@ namespace
|
||||
}
|
||||
|
||||
|
||||
bool parseToRoles(IParser::Pos & pos, Expected & expected, ASTGrantQuery::Kind kind, std::shared_ptr<ASTRolesOrUsersSet> & to_roles)
|
||||
bool parseToGrantees(IParser::Pos & pos, Expected & expected, bool is_revoke, std::shared_ptr<ASTRolesOrUsersSet> & grantees)
|
||||
{
|
||||
return IParserBase::wrapParseImpl(pos, [&]
|
||||
{
|
||||
if (kind == Kind::GRANT)
|
||||
{
|
||||
if (!ParserKeyword{"TO"}.ignore(pos, expected))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ParserKeyword{"FROM"}.ignore(pos, expected))
|
||||
return false;
|
||||
}
|
||||
if (!ParserKeyword{is_revoke ? "FROM" : "TO"}.ignore(pos, expected))
|
||||
return false;
|
||||
|
||||
ASTPtr ast;
|
||||
ParserRolesOrUsersSet roles_p;
|
||||
roles_p.allowRoleNames().allowUserNames().allowCurrentUser().allowAll(kind == Kind::REVOKE);
|
||||
roles_p.allowRoles().allowUsers().allowCurrentUser().allowAll(is_revoke);
|
||||
if (!roles_p.parse(pos, ast, expected))
|
||||
return false;
|
||||
|
||||
to_roles = typeid_cast<std::shared_ptr<ASTRolesOrUsersSet>>(ast);
|
||||
grantees = typeid_cast<std::shared_ptr<ASTRolesOrUsersSet>>(ast);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@ -237,20 +228,13 @@ namespace
|
||||
|
||||
bool ParserGrantQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
{
|
||||
bool attach = false;
|
||||
if (attach_mode)
|
||||
{
|
||||
if (!ParserKeyword{"ATTACH"}.ignore(pos, expected))
|
||||
return false;
|
||||
attach = true;
|
||||
}
|
||||
if (attach_mode && !ParserKeyword{"ATTACH"}.ignore(pos, expected))
|
||||
return false;
|
||||
|
||||
Kind kind;
|
||||
if (ParserKeyword{"GRANT"}.ignore(pos, expected))
|
||||
kind = Kind::GRANT;
|
||||
else if (ParserKeyword{"REVOKE"}.ignore(pos, expected))
|
||||
kind = Kind::REVOKE;
|
||||
else
|
||||
bool is_revoke = false;
|
||||
if (ParserKeyword{"REVOKE"}.ignore(pos, expected))
|
||||
is_revoke = true;
|
||||
else if (!ParserKeyword{"GRANT"}.ignore(pos, expected))
|
||||
return false;
|
||||
|
||||
String cluster;
|
||||
@ -259,7 +243,7 @@ bool ParserGrantQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
|
||||
bool grant_option = false;
|
||||
bool admin_option = false;
|
||||
if (kind == Kind::REVOKE)
|
||||
if (is_revoke)
|
||||
{
|
||||
if (ParserKeyword{"GRANT OPTION FOR"}.ignore(pos, expected))
|
||||
grant_option = true;
|
||||
@ -269,20 +253,20 @@ bool ParserGrantQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
|
||||
AccessRightsElements elements;
|
||||
std::shared_ptr<ASTRolesOrUsersSet> roles;
|
||||
if (!parseAccessRightsElements(pos, expected, elements) && !parseRoles(pos, expected, kind, attach, roles))
|
||||
if (!parseElementsWithoutOptions(pos, expected, elements) && !parseRoles(pos, expected, is_revoke, attach_mode, roles))
|
||||
return false;
|
||||
|
||||
if (cluster.empty())
|
||||
parseOnCluster(pos, expected, cluster);
|
||||
|
||||
std::shared_ptr<ASTRolesOrUsersSet> to_roles;
|
||||
if (!parseToRoles(pos, expected, kind, to_roles))
|
||||
std::shared_ptr<ASTRolesOrUsersSet> grantees;
|
||||
if (!parseToGrantees(pos, expected, is_revoke, grantees))
|
||||
return false;
|
||||
|
||||
if (cluster.empty())
|
||||
parseOnCluster(pos, expected, cluster);
|
||||
|
||||
if (kind == Kind::GRANT)
|
||||
if (!is_revoke)
|
||||
{
|
||||
if (ParserKeyword{"WITH GRANT OPTION"}.ignore(pos, expected))
|
||||
grant_option = true;
|
||||
@ -298,19 +282,24 @@ bool ParserGrantQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
if (admin_option && !elements.empty())
|
||||
throw Exception("ADMIN OPTION should be specified for roles", ErrorCodes::SYNTAX_ERROR);
|
||||
|
||||
if (kind == Kind::GRANT)
|
||||
removeNonGrantableFlags(elements);
|
||||
if (grant_option)
|
||||
{
|
||||
for (auto & element : elements)
|
||||
element.grant_option = true;
|
||||
}
|
||||
|
||||
if (!is_revoke)
|
||||
eraseNonGrantable(elements);
|
||||
|
||||
auto query = std::make_shared<ASTGrantQuery>();
|
||||
node = query;
|
||||
|
||||
query->kind = kind;
|
||||
query->attach = attach;
|
||||
query->is_revoke = is_revoke;
|
||||
query->attach_mode = attach_mode;
|
||||
query->cluster = std::move(cluster);
|
||||
query->access_rights_elements = std::move(elements);
|
||||
query->roles = std::move(roles);
|
||||
query->to_roles = std::move(to_roles);
|
||||
query->grant_option = grant_option;
|
||||
query->grantees = std::move(grantees);
|
||||
query->admin_option = admin_option;
|
||||
|
||||
return true;
|
||||
|
@ -12,11 +12,7 @@ namespace DB
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool parseRoleNameOrID(
|
||||
IParserBase::Pos & pos,
|
||||
Expected & expected,
|
||||
bool id_mode,
|
||||
String & res)
|
||||
bool parseNameOrID(IParserBase::Pos & pos, Expected & expected, bool id_mode, String & res)
|
||||
{
|
||||
return IParserBase::wrapParseImpl(pos, [&]
|
||||
{
|
||||
@ -39,20 +35,20 @@ namespace
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool parseBeforeExcept(
|
||||
IParserBase::Pos & pos,
|
||||
Expected & expected,
|
||||
bool id_mode,
|
||||
bool allow_all,
|
||||
bool allow_current_user,
|
||||
Strings & names,
|
||||
bool & all,
|
||||
Strings & names,
|
||||
bool & current_user)
|
||||
{
|
||||
bool res_all = false;
|
||||
bool res_current_user = false;
|
||||
Strings res_names;
|
||||
bool res_current_user = false;
|
||||
Strings res_with_roles_names;
|
||||
|
||||
auto parse_element = [&]
|
||||
{
|
||||
@ -72,7 +68,7 @@ namespace
|
||||
}
|
||||
|
||||
String name;
|
||||
if (parseRoleNameOrID(pos, expected, id_mode, name))
|
||||
if (parseNameOrID(pos, expected, id_mode, name))
|
||||
{
|
||||
res_names.emplace_back(std::move(name));
|
||||
return true;
|
||||
@ -85,8 +81,8 @@ namespace
|
||||
return false;
|
||||
|
||||
names = std::move(res_names);
|
||||
all = res_all;
|
||||
current_user = res_current_user;
|
||||
all = res_all;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -98,13 +94,12 @@ namespace
|
||||
Strings & except_names,
|
||||
bool & except_current_user)
|
||||
{
|
||||
return IParserBase::wrapParseImpl(pos, [&]
|
||||
{
|
||||
return IParserBase::wrapParseImpl(pos, [&] {
|
||||
if (!ParserKeyword{"EXCEPT"}.ignore(pos, expected))
|
||||
return false;
|
||||
|
||||
bool unused;
|
||||
return parseBeforeExcept(pos, expected, id_mode, false, allow_current_user, except_names, unused, except_current_user);
|
||||
return parseBeforeExcept(pos, expected, id_mode, false, allow_current_user, unused, except_names, except_current_user);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -112,13 +107,13 @@ namespace
|
||||
|
||||
bool ParserRolesOrUsersSet::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
{
|
||||
bool all = false;
|
||||
Strings names;
|
||||
bool current_user = false;
|
||||
bool all = false;
|
||||
Strings except_names;
|
||||
bool except_current_user = false;
|
||||
|
||||
if (!parseBeforeExcept(pos, expected, id_mode, allow_all, allow_current_user, names, all, current_user))
|
||||
if (!parseBeforeExcept(pos, expected, id_mode, allow_all, allow_current_user, all, names, current_user))
|
||||
return false;
|
||||
|
||||
parseExceptAndAfterExcept(pos, expected, id_mode, allow_current_user, except_names, except_current_user);
|
||||
@ -132,9 +127,9 @@ bool ParserRolesOrUsersSet::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
||||
result->all = all;
|
||||
result->except_names = std::move(except_names);
|
||||
result->except_current_user = except_current_user;
|
||||
result->allow_users = allow_users;
|
||||
result->allow_roles = allow_roles;
|
||||
result->id_mode = id_mode;
|
||||
result->allow_user_names = allow_user_names;
|
||||
result->allow_role_names = allow_role_names;
|
||||
node = result;
|
||||
return true;
|
||||
}
|
||||
|
@ -6,15 +6,16 @@
|
||||
namespace DB
|
||||
{
|
||||
/** Parses a string like this:
|
||||
* {role|CURRENT_USER} [,...] | NONE | ALL | ALL EXCEPT {role|CURRENT_USER} [,...]
|
||||
* {user_name | role_name | CURRENT_USER | ALL | NONE} [,...]
|
||||
* [EXCEPT {user_name | role_name | CURRENT_USER | ALL | NONE} [,...]]
|
||||
*/
|
||||
class ParserRolesOrUsersSet : public IParserBase
|
||||
{
|
||||
public:
|
||||
ParserRolesOrUsersSet & allowAll(bool allow_all_ = true) { allow_all = allow_all_; return *this; }
|
||||
ParserRolesOrUsersSet & allowUserNames(bool allow_user_names_ = true) { allow_user_names = allow_user_names_; return *this; }
|
||||
ParserRolesOrUsersSet & allowRoleNames(bool allow_role_names_ = true) { allow_role_names = allow_role_names_; return *this; }
|
||||
ParserRolesOrUsersSet & allowUsers(bool allow_users_ = true) { allow_users = allow_users_; return *this; }
|
||||
ParserRolesOrUsersSet & allowCurrentUser(bool allow_current_user_ = true) { allow_current_user = allow_current_user_; return *this; }
|
||||
ParserRolesOrUsersSet & allowRoles(bool allow_roles_ = true) { allow_roles = allow_roles_; return *this; }
|
||||
ParserRolesOrUsersSet & useIDMode(bool id_mode_ = true) { id_mode = id_mode_; return *this; }
|
||||
|
||||
protected:
|
||||
@ -23,9 +24,9 @@ protected:
|
||||
|
||||
private:
|
||||
bool allow_all = false;
|
||||
bool allow_user_names = false;
|
||||
bool allow_role_names = false;
|
||||
bool allow_users = false;
|
||||
bool allow_current_user = false;
|
||||
bool allow_roles = false;
|
||||
bool id_mode = false;
|
||||
};
|
||||
|
||||
|
@ -15,12 +15,12 @@ namespace
|
||||
{
|
||||
ASTPtr ast;
|
||||
ParserRolesOrUsersSet roles_p;
|
||||
roles_p.allowRoleNames().allowAll();
|
||||
roles_p.allowRoles().allowAll();
|
||||
if (!roles_p.parse(pos, ast, expected))
|
||||
return false;
|
||||
|
||||
roles = typeid_cast<std::shared_ptr<ASTRolesOrUsersSet>>(ast);
|
||||
roles->allow_user_names = false;
|
||||
roles->allow_users = false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@ -34,12 +34,12 @@ namespace
|
||||
|
||||
ASTPtr ast;
|
||||
ParserRolesOrUsersSet users_p;
|
||||
users_p.allowUserNames().allowCurrentUser();
|
||||
users_p.allowUsers().allowCurrentUser();
|
||||
if (!users_p.parse(pos, ast, expected))
|
||||
return false;
|
||||
|
||||
to_users = typeid_cast<std::shared_ptr<ASTRolesOrUsersSet>>(ast);
|
||||
to_users->allow_role_names = false;
|
||||
to_users->allow_roles = false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ bool ParserShowGrantsQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
||||
{
|
||||
ASTPtr for_roles_ast;
|
||||
ParserRolesOrUsersSet for_roles_p;
|
||||
for_roles_p.allowUserNames().allowRoleNames().allowAll().allowCurrentUser();
|
||||
for_roles_p.allowUsers().allowRoles().allowAll().allowCurrentUser();
|
||||
if (!for_roles_p.parse(pos, for_roles_ast, expected))
|
||||
return false;
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
namespace DB
|
||||
{
|
||||
using EntityType = IAccessEntity::Type;
|
||||
using Kind = AccessRightsElementWithOptions::Kind;
|
||||
|
||||
NamesAndTypesList StorageSystemGrants::getNamesAndTypes()
|
||||
{
|
||||
@ -64,7 +63,7 @@ void StorageSystemGrants::fillData(MutableColumns & res_columns, const Context &
|
||||
const String * database,
|
||||
const String * table,
|
||||
const String * column,
|
||||
Kind kind,
|
||||
bool is_partial_revoke,
|
||||
bool grant_option)
|
||||
{
|
||||
if (grantee_type == EntityType::USER)
|
||||
@ -119,13 +118,13 @@ void StorageSystemGrants::fillData(MutableColumns & res_columns, const Context &
|
||||
column_column_null_map.push_back(true);
|
||||
}
|
||||
|
||||
column_is_partial_revoke.push_back(kind == Kind::REVOKE);
|
||||
column_is_partial_revoke.push_back(is_partial_revoke);
|
||||
column_grant_option.push_back(grant_option);
|
||||
};
|
||||
|
||||
auto add_rows = [&](const String & grantee_name,
|
||||
IAccessEntity::Type grantee_type,
|
||||
const AccessRightsElementsWithOptions & elements)
|
||||
const AccessRightsElements & elements)
|
||||
{
|
||||
for (const auto & element : elements)
|
||||
{
|
||||
@ -139,13 +138,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, element.kind, element.grant_option);
|
||||
add_row(grantee_name, grantee_type, access_type, database, table, nullptr, element.is_partial_revoke, 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, element.kind, element.grant_option);
|
||||
add_row(grantee_name, grantee_type, access_type, database, table, &column, element.is_partial_revoke, element.grant_option);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -80,15 +80,17 @@ void StorageSystemRoleGrants::fillData(MutableColumns & res_columns, const Conte
|
||||
const GrantedRoles & granted_roles,
|
||||
const RolesOrUsersSet * default_roles)
|
||||
{
|
||||
for (const auto & role_id : granted_roles.roles)
|
||||
for (const auto & element : granted_roles.getElements())
|
||||
{
|
||||
auto role_name = access_control.tryReadName(role_id);
|
||||
if (!role_name)
|
||||
continue;
|
||||
for (const auto & role_id : element.ids)
|
||||
{
|
||||
auto role_name = access_control.tryReadName(role_id);
|
||||
if (!role_name)
|
||||
continue;
|
||||
|
||||
bool is_default = !default_roles || default_roles->match(role_id);
|
||||
bool with_admin_option = granted_roles.roles_with_admin_option.count(role_id);
|
||||
add_row(grantee_name, grantee_type, *role_name, is_default, with_admin_option);
|
||||
bool is_default = !default_roles || default_roles->match(role_id);
|
||||
add_row(grantee_name, grantee_type, *role_name, is_default, element.admin_option);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -166,9 +166,10 @@ def feature(self, node="clickhouse1"):
|
||||
|
||||
with Scenario("I revoke a role on fake cluster, throws exception", requirements=[
|
||||
RQ_SRS_006_RBAC_Revoke_Role_Cluster("1.0")]):
|
||||
with When("I revoke a role from user on a cluster"):
|
||||
exitcode, message = errors.cluster_not_found("fake_cluster")
|
||||
node.query("REVOKE ON CLUSTER fake_cluster role0 FROM user0", exitcode=exitcode, message=message)
|
||||
with setup():
|
||||
with When("I revoke a role from user on a cluster"):
|
||||
exitcode, message = errors.cluster_not_found("fake_cluster")
|
||||
node.query("REVOKE ON CLUSTER fake_cluster role0 FROM user0", exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("I revoke multiple roles from multiple users on cluster", requirements=[
|
||||
RQ_SRS_006_RBAC_Revoke_Role("1.0"),
|
||||
|
Loading…
Reference in New Issue
Block a user