Remove non-partial revoke.

This commit is contained in:
Vitaly Baranov 2020-03-05 20:02:11 +03:00
parent 4af36ee5a4
commit c7a10665d7
8 changed files with 231 additions and 367 deletions

View File

@ -23,13 +23,6 @@ namespace
COLUMN_LEVEL,
};
enum RevokeMode
{
NORMAL_REVOKE_MODE, /// for AccessRights::revoke()
PARTIAL_REVOKE_MODE, /// for AccessRights::partialRevoke()
FULL_REVOKE_MODE, /// for AccessRights::fullRevoke()
};
struct Helper
{
static const Helper & instance()
@ -41,6 +34,7 @@ namespace
const AccessFlags database_level_flags = AccessFlags::databaseLevel();
const AccessFlags table_level_flags = AccessFlags::tableLevel();
const AccessFlags column_level_flags = AccessFlags::columnLevel();
const AccessFlags show_flag = AccessType::SHOW;
const AccessFlags exists_flag = AccessType::EXISTS;
const AccessFlags create_table_flag = AccessType::CREATE_TABLE;
@ -61,13 +55,10 @@ struct AccessRights::Node
public:
std::shared_ptr<const String> node_name;
Level level = GLOBAL_LEVEL;
AccessFlags explicit_grants;
AccessFlags partial_revokes;
AccessFlags inherited_access; /// the access inherited from the parent node
AccessFlags raw_access; /// raw_access = (inherited_access - partial_revokes) | explicit_grants
AccessFlags access; /// access = raw_access | implicit_access
AccessFlags min_access; /// min_access = access & child[0].access & ... | child[N-1].access
AccessFlags max_access; /// max_access = access | child[0].access | ... | child[N-1].access
AccessFlags access; /// access = (inherited_access - partial_revokes) | explicit_grants
AccessFlags final_access; /// final_access = access | implicit_access
AccessFlags min_access; /// min_access = final_access & child[0].final_access & ... & child[N-1].final_access
AccessFlags max_access; /// max_access = final_access | child[0].final_access | ... | child[N-1].final_access
std::unique_ptr<std::unordered_map<std::string_view, Node>> children;
Node() = default;
@ -80,11 +71,8 @@ public:
node_name = src.node_name;
level = src.level;
inherited_access = src.inherited_access;
explicit_grants = src.explicit_grants;
partial_revokes = src.partial_revokes;
raw_access = src.raw_access;
access = src.access;
final_access = src.final_access;
min_access = src.min_access;
max_access = src.max_access;
if (src.children)
@ -94,9 +82,9 @@ public:
return *this;
}
void grant(AccessFlags access_to_grant, const Helper & helper)
void grant(AccessFlags flags, const Helper & helper)
{
if (!access_to_grant)
if (!flags)
return;
if (level == GLOBAL_LEVEL)
@ -105,126 +93,77 @@ public:
}
else if (level == DATABASE_LEVEL)
{
AccessFlags grantable = access_to_grant & helper.database_level_flags;
AccessFlags grantable = flags & helper.database_level_flags;
if (!grantable)
throw Exception(access_to_grant.toString() + " cannot be granted on the database level", ErrorCodes::INVALID_GRANT);
access_to_grant = grantable;
throw Exception(flags.toString() + " cannot be granted on the database level", ErrorCodes::INVALID_GRANT);
flags = grantable;
}
else if (level == TABLE_LEVEL)
{
AccessFlags grantable = access_to_grant & helper.table_level_flags;
AccessFlags grantable = flags & helper.table_level_flags;
if (!grantable)
throw Exception(access_to_grant.toString() + " cannot be granted on the table level", ErrorCodes::INVALID_GRANT);
access_to_grant = grantable;
throw Exception(flags.toString() + " cannot be granted on the table level", ErrorCodes::INVALID_GRANT);
flags = grantable;
}
else if (level == COLUMN_LEVEL)
{
AccessFlags grantable = access_to_grant & helper.column_level_flags;
AccessFlags grantable = flags & helper.column_level_flags;
if (!grantable)
throw Exception(access_to_grant.toString() + " cannot be granted on the column level", ErrorCodes::INVALID_GRANT);
access_to_grant = grantable;
throw Exception(flags.toString() + " cannot be granted on the column level", ErrorCodes::INVALID_GRANT);
flags = grantable;
}
AccessFlags new_explicit_grants = access_to_grant - partial_revokes;
if (level == TABLE_LEVEL)
removeExplicitGrantsRec(new_explicit_grants);
removePartialRevokesRec(access_to_grant);
explicit_grants |= new_explicit_grants;
calculateAllAccessRec(helper);
addGrantsRec(flags);
calculateFinalAccessRec(helper);
}
template <typename ... Args>
void grant(const AccessFlags & access_to_grant, const Helper & helper, const std::string_view & name, const Args &... subnames)
void grant(const AccessFlags & flags, const Helper & helper, const std::string_view & name, const Args &... subnames)
{
auto & child = getChild(name);
child.grant(access_to_grant, helper, subnames...);
eraseChildIfEmpty(child);
calculateImplicitAccess(helper);
calculateMinAndMaxAccess();
child.grant(flags, helper, subnames...);
eraseChildIfPossible(child);
calculateFinalAccess(helper);
}
template <typename StringT>
void grant(const AccessFlags & access_to_grant, const Helper & helper, const std::vector<StringT> & names)
void grant(const AccessFlags & flags, const Helper & helper, const std::vector<StringT> & names)
{
for (const auto & name : names)
{
auto & child = getChild(name);
child.grant(access_to_grant, helper);
eraseChildIfEmpty(child);
child.grant(flags, helper);
eraseChildIfPossible(child);
}
calculateImplicitAccess(helper);
calculateMinAndMaxAccess();
calculateFinalAccess(helper);
}
template <int mode>
void revoke(const AccessFlags & access_to_revoke, const Helper & helper)
void revoke(const AccessFlags & flags, const Helper & helper)
{
if constexpr (mode == NORMAL_REVOKE_MODE)
{ // NOLINT
if (level == TABLE_LEVEL)
removeExplicitGrantsRec(access_to_revoke);
else
removeExplicitGrants(access_to_revoke);
}
else if constexpr (mode == PARTIAL_REVOKE_MODE)
{
if (level == TABLE_LEVEL)
removeExplicitGrantsRec(access_to_revoke);
else
removeExplicitGrants(access_to_revoke);
AccessFlags new_partial_revokes = access_to_revoke - explicit_grants;
removePartialRevokesRec(new_partial_revokes);
partial_revokes |= new_partial_revokes;
}
else /// mode == FULL_REVOKE_MODE
{
AccessFlags new_partial_revokes = access_to_revoke - explicit_grants;
removeExplicitGrantsRec(access_to_revoke);
removePartialRevokesRec(new_partial_revokes);
partial_revokes |= new_partial_revokes;
}
calculateAllAccessRec(helper);
removeGrantsRec(flags);
calculateFinalAccessRec(helper);
}
template <int mode, typename... Args>
void revoke(const AccessFlags & access_to_revoke, const Helper & helper, const std::string_view & name, const Args &... subnames)
template <typename... Args>
void revoke(const AccessFlags & flags, const Helper & helper, const std::string_view & name, const Args &... subnames)
{
Node * child;
if (mode == NORMAL_REVOKE_MODE)
{
if (!(child = tryGetChild(name)))
return;
}
else
child = &getChild(name);
auto & child = getChild(name);
child->revoke<mode>(access_to_revoke, helper, subnames...);
eraseChildIfEmpty(*child);
calculateImplicitAccess(helper);
calculateMinAndMaxAccess();
child.revoke(flags, helper, subnames...);
eraseChildIfPossible(child);
calculateFinalAccess(helper);
}
template <int mode, typename StringT>
void revoke(const AccessFlags & access_to_revoke, const Helper & helper, const std::vector<StringT> & names)
template <typename StringT>
void revoke(const AccessFlags & flags, const Helper & helper, const std::vector<StringT> & names)
{
Node * child;
for (const auto & name : names)
{
if (mode == NORMAL_REVOKE_MODE)
{
if (!(child = tryGetChild(name)))
continue;
}
else
child = &getChild(name);
child->revoke<mode>(access_to_revoke, helper);
eraseChildIfEmpty(*child);
auto & child = getChild(name);
child.revoke(flags, helper);
eraseChildIfPossible(child);
}
calculateImplicitAccess(helper);
calculateMinAndMaxAccess();
calculateFinalAccess(helper);
}
bool isGranted(const AccessFlags & flags) const
@ -244,7 +183,7 @@ public:
if (child)
return child->isGranted(flags, subnames...);
else
return access.contains(flags);
return final_access.contains(flags);
}
template <typename StringT>
@ -265,7 +204,7 @@ public:
}
else
{
if (!access.contains(flags))
if (!final_access.contains(flags))
return false;
}
}
@ -274,7 +213,7 @@ public:
friend bool operator ==(const Node & left, const Node & right)
{
if ((left.explicit_grants != right.explicit_grants) || (left.partial_revokes != right.partial_revokes))
if (left.access != right.access)
return false;
if (!left.children)
@ -287,33 +226,24 @@ public:
friend bool operator!=(const Node & left, const Node & right) { return !(left == right); }
bool isEmpty() const
{
return !explicit_grants && !partial_revokes && !children;
}
void merge(const Node & other, const Helper & helper)
{
mergeRawAccessRec(other);
calculateGrantsAndPartialRevokesRec();
calculateAllAccessRec(helper);
mergeAccessRec(other);
calculateFinalAccessRec(helper);
}
void traceTree(Poco::Logger * log) const
void logTree(Poco::Logger * log) const
{
LOG_TRACE(log, "Tree(" << level << "): name=" << (node_name ? *node_name : "NULL")
<< ", explicit_grants=" << explicit_grants.toString()
<< ", partial_revokes=" << partial_revokes.toString()
<< ", inherited_access=" << inherited_access.toString()
<< ", raw_access=" << raw_access.toString()
<< ", access=" << access.toString()
<< ", final_access=" << final_access.toString()
<< ", min_access=" << min_access.toString()
<< ", max_access=" << max_access.toString()
<< ", num_children=" << (children ? children->size() : 0));
if (children)
{
for (auto & child : *children | boost::adaptors::map_values)
child.traceTree(log);
child.logTree(log);
}
}
@ -349,14 +279,13 @@ private:
Node & new_child = (*children)[*new_child_name];
new_child.node_name = std::move(new_child_name);
new_child.level = static_cast<Level>(level + 1);
new_child.inherited_access = raw_access;
new_child.raw_access = raw_access;
new_child.access = access;
return new_child;
}
void eraseChildIfEmpty(Node & child)
void eraseChildIfPossible(Node & child)
{
if (!child.isEmpty())
if (!canEraseChild(child))
return;
auto it = children->find(*child.node_name);
children->erase(it);
@ -364,46 +293,59 @@ private:
children = nullptr;
}
void calculateImplicitAccess(const Helper & helper)
bool canEraseChild(const Node & child) const
{
access = raw_access;
if (access & helper.database_level_flags)
access |= helper.show_flag | helper.exists_flag;
else if ((level >= DATABASE_LEVEL) && children)
access |= helper.exists_flag;
if ((level == GLOBAL_LEVEL) && (access & helper.create_table_flag))
access |= helper.create_temporary_table_flag;
return (access == child.access) && !child.children;
}
void calculateMinAndMaxAccess()
void addGrantsRec(const AccessFlags & flags)
{
min_access = access;
max_access = access;
access |= flags;
if (children)
{
for (const auto & child : *children | boost::adaptors::map_values)
for (auto it = children->begin(); it != children->end();)
{
min_access &= child.min_access;
max_access |= child.max_access;
auto & child = it->second;
child.addGrantsRec(flags);
if (canEraseChild(child))
it = children->erase(it);
else
++it;
}
if (children->empty())
children = nullptr;
}
}
void calculateAllAccessRec(const Helper & helper)
void removeGrantsRec(const AccessFlags & flags)
{
partial_revokes &= inherited_access;
raw_access = (inherited_access - partial_revokes) | explicit_grants;
access &= ~flags;
if (children)
{
for (auto it = children->begin(); it != children->end();)
{
auto & child = it->second;
child.removeGrantsRec(flags);
if (canEraseChild(child))
it = children->erase(it);
else
++it;
}
if (children->empty())
children = nullptr;
}
}
void calculateFinalAccessRec(const Helper & helper)
{
/// Traverse tree.
if (children)
{
for (auto it = children->begin(); it != children->end();)
{
auto & child = it->second;
child.inherited_access = raw_access;
child.calculateAllAccessRec(helper);
if (child.isEmpty())
child.calculateFinalAccessRec(helper);
if (canEraseChild(child))
it = children->erase(it);
else
++it;
@ -412,64 +354,59 @@ private:
children = nullptr;
}
calculateImplicitAccess(helper);
calculateMinAndMaxAccess();
calculateFinalAccess(helper);
}
void removeExplicitGrants(const AccessFlags & change)
void calculateFinalAccess(const Helper & helper)
{
explicit_grants -= change;
}
void removeExplicitGrantsRec(const AccessFlags & change)
{
removeExplicitGrants(change);
/// Calculate min and max access among children.
AccessFlags min_access_among_children = AccessType::ALL;
AccessFlags max_access_among_children;
if (children)
{
for (auto & child : *children | boost::adaptors::map_values)
child.removeExplicitGrantsRec(change);
for (const auto & child : *children | boost::adaptors::map_values)
{
min_access &= child.min_access;
max_access |= child.max_access;
}
}
/// Calculate implicit access:
AccessFlags implicit_access;
if (access & helper.database_level_flags)
implicit_access |= helper.show_flag | helper.exists_flag;
else if ((level >= DATABASE_LEVEL) && children)
implicit_access |= helper.exists_flag;
if ((level == GLOBAL_LEVEL) && (final_access & helper.create_table_flag))
implicit_access |= helper.create_temporary_table_flag;
final_access = access | implicit_access;
/// Calculate min and max access:
/// min_access = final_access & child[0].final_access & ... & child[N-1].final_access
/// max_access = final_access | child[0].final_access | ... | child[N-1].final_access
min_access = final_access & min_access_among_children;
max_access = final_access | max_access_among_children;
}
void removePartialRevokesRec(const AccessFlags & change)
{
partial_revokes -= change;
if (children)
{
for (auto & child : *children | boost::adaptors::map_values)
child.removePartialRevokesRec(change);
}
}
void mergeRawAccessRec(const Node & rhs)
void mergeAccessRec(const Node & rhs)
{
if (rhs.children)
{
for (const auto & [rhs_childname, rhs_child] : *rhs.children)
getChild(rhs_childname).mergeRawAccessRec(rhs_child);
getChild(rhs_childname).mergeAccessRec(rhs_child);
}
raw_access |= rhs.raw_access;
access |= rhs.access;
if (children)
{
for (auto & [lhs_childname, lhs_child] : *children)
{
lhs_child.inherited_access = raw_access;
if (!rhs.tryGetChild(lhs_childname))
lhs_child.raw_access |= rhs.raw_access;
lhs_child.access |= rhs.access;
}
}
}
void calculateGrantsAndPartialRevokesRec()
{
explicit_grants = raw_access - inherited_access;
partial_revokes = inherited_access - raw_access;
if (children)
{
for (auto & child : *children | boost::adaptors::map_values)
child.calculateGrantsAndPartialRevokesRec();
}
}
};
@ -514,165 +451,150 @@ void AccessRights::clear()
template <typename... Args>
void AccessRights::grantImpl(const AccessFlags & access, const Args &... args)
void AccessRights::grantImpl(const AccessFlags & flags, const Args &... args)
{
if (!root)
root = std::make_unique<Node>();
root->grant(access, Helper::instance(), args...);
if (root->isEmpty())
root->grant(flags, Helper::instance(), args...);
if (!root->access && !root->children)
root = nullptr;
}
void AccessRights::grantImpl(const AccessRightsElement & element, std::string_view current_database)
void AccessRights::grant(const AccessFlags & flags) { grantImpl(flags); }
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database) { grantImpl(flags, database); }
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) { grantImpl(flags, database, table); }
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) { grantImpl(flags, database, table, column); }
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { grantImpl(flags, database, table, columns); }
void AccessRights::grant(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) { grantImpl(flags, database, table, columns); }
void AccessRights::grant(const AccessRightsElement & element, std::string_view current_database)
{
if (element.any_database)
{
grantImpl(element.access_flags);
grant(element.access_flags);
}
else if (element.any_table)
{
if (element.database.empty())
grantImpl(element.access_flags, checkCurrentDatabase(current_database));
grant(element.access_flags, checkCurrentDatabase(current_database));
else
grantImpl(element.access_flags, element.database);
grant(element.access_flags, element.database);
}
else if (element.any_column)
{
if (element.database.empty())
grantImpl(element.access_flags, checkCurrentDatabase(current_database), element.table);
grant(element.access_flags, checkCurrentDatabase(current_database), element.table);
else
grantImpl(element.access_flags, element.database, element.table);
grant(element.access_flags, element.database, element.table);
}
else
{
if (element.database.empty())
grantImpl(element.access_flags, checkCurrentDatabase(current_database), element.table, element.columns);
grant(element.access_flags, checkCurrentDatabase(current_database), element.table, element.columns);
else
grantImpl(element.access_flags, element.database, element.table, element.columns);
grant(element.access_flags, element.database, element.table, element.columns);
}
}
void AccessRights::grantImpl(const AccessRightsElements & elements, std::string_view current_database)
void AccessRights::grant(const AccessRightsElements & elements, std::string_view current_database)
{
for (const auto & element : elements)
grantImpl(element, current_database);
grant(element, current_database);
}
void AccessRights::grant(const AccessFlags & access) { grantImpl(access); }
void AccessRights::grant(const AccessFlags & access, const std::string_view & database) { grantImpl(access, database); }
void AccessRights::grant(const AccessFlags & access, const std::string_view & database, const std::string_view & table) { grantImpl(access, database, table); }
void AccessRights::grant(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) { grantImpl(access, database, table, column); }
void AccessRights::grant(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { grantImpl(access, database, table, columns); }
void AccessRights::grant(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) { grantImpl(access, database, table, columns); }
void AccessRights::grant(const AccessRightsElement & element, std::string_view current_database) { grantImpl(element, current_database); }
void AccessRights::grant(const AccessRightsElements & elements, std::string_view current_database) { grantImpl(elements, current_database); }
template <int mode, typename... Args>
void AccessRights::revokeImpl(const AccessFlags & access, const Args &... args)
template <typename... Args>
void AccessRights::revokeImpl(const AccessFlags & flags, const Args &... args)
{
if (!root)
return;
root->revoke<mode>(access, Helper::instance(), args...);
if (root->isEmpty())
root->revoke(flags, Helper::instance(), args...);
if (!root->access && !root->children)
root = nullptr;
}
template <int mode>
void AccessRights::revokeImpl(const AccessRightsElement & element, std::string_view current_database)
void AccessRights::revoke(const AccessFlags & flags) { revokeImpl(flags); }
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database) { revokeImpl(flags, database); }
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) { revokeImpl(flags, database, table); }
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) { revokeImpl(flags, database, table, column); }
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { revokeImpl(flags, database, table, columns); }
void AccessRights::revoke(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) { revokeImpl(flags, database, table, columns); }
void AccessRights::revoke(const AccessRightsElement & element, std::string_view current_database)
{
if (element.any_database)
{
revokeImpl<mode>(element.access_flags);
revoke(element.access_flags);
}
else if (element.any_table)
{
if (element.database.empty())
revokeImpl<mode>(element.access_flags, checkCurrentDatabase(current_database));
revoke(element.access_flags, checkCurrentDatabase(current_database));
else
revokeImpl<mode>(element.access_flags, element.database);
revoke(element.access_flags, element.database);
}
else if (element.any_column)
{
if (element.database.empty())
revokeImpl<mode>(element.access_flags, checkCurrentDatabase(current_database), element.table);
revoke(element.access_flags, checkCurrentDatabase(current_database), element.table);
else
revokeImpl<mode>(element.access_flags, element.database, element.table);
revoke(element.access_flags, element.database, element.table);
}
else
{
if (element.database.empty())
revokeImpl<mode>(element.access_flags, checkCurrentDatabase(current_database), element.table, element.columns);
revoke(element.access_flags, checkCurrentDatabase(current_database), element.table, element.columns);
else
revokeImpl<mode>(element.access_flags, element.database, element.table, element.columns);
revoke(element.access_flags, element.database, element.table, element.columns);
}
}
template <int mode>
void AccessRights::revokeImpl(const AccessRightsElements & elements, std::string_view current_database)
void AccessRights::revoke(const AccessRightsElements & elements, std::string_view current_database)
{
for (const auto & element : elements)
revokeImpl<mode>(element, current_database);
revoke(element, current_database);
}
void AccessRights::revoke(const AccessFlags & access) { revokeImpl<NORMAL_REVOKE_MODE>(access); }
void AccessRights::revoke(const AccessFlags & access, const std::string_view & database) { revokeImpl<NORMAL_REVOKE_MODE>(access, database); }
void AccessRights::revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table) { revokeImpl<NORMAL_REVOKE_MODE>(access, database, table); }
void AccessRights::revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) { revokeImpl<NORMAL_REVOKE_MODE>(access, database, table, column); }
void AccessRights::revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { revokeImpl<NORMAL_REVOKE_MODE>(access, database, table, columns); }
void AccessRights::revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) { revokeImpl<NORMAL_REVOKE_MODE>(access, database, table, columns); }
void AccessRights::revoke(const AccessRightsElement & element, std::string_view current_database) { revokeImpl<NORMAL_REVOKE_MODE>(element, current_database); }
void AccessRights::revoke(const AccessRightsElements & elements, std::string_view current_database) { revokeImpl<NORMAL_REVOKE_MODE>(elements, current_database); }
void AccessRights::partialRevoke(const AccessFlags & access) { revokeImpl<PARTIAL_REVOKE_MODE>(access); }
void AccessRights::partialRevoke(const AccessFlags & access, const std::string_view & database) { revokeImpl<PARTIAL_REVOKE_MODE>(access, database); }
void AccessRights::partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table) { revokeImpl<PARTIAL_REVOKE_MODE>(access, database, table); }
void AccessRights::partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) { revokeImpl<PARTIAL_REVOKE_MODE>(access, database, table, column); }
void AccessRights::partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { revokeImpl<PARTIAL_REVOKE_MODE>(access, database, table, columns); }
void AccessRights::partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) { revokeImpl<PARTIAL_REVOKE_MODE>(access, database, table, columns); }
void AccessRights::partialRevoke(const AccessRightsElement & element, std::string_view current_database) { revokeImpl<PARTIAL_REVOKE_MODE>(element, current_database); }
void AccessRights::partialRevoke(const AccessRightsElements & elements, std::string_view current_database) { revokeImpl<PARTIAL_REVOKE_MODE>(elements, current_database); }
void AccessRights::fullRevoke(const AccessFlags & access) { revokeImpl<FULL_REVOKE_MODE>(access); }
void AccessRights::fullRevoke(const AccessFlags & access, const std::string_view & database) { revokeImpl<FULL_REVOKE_MODE>(access, database); }
void AccessRights::fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table) { revokeImpl<FULL_REVOKE_MODE>(access, database, table); }
void AccessRights::fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) { revokeImpl<FULL_REVOKE_MODE>(access, database, table, column); }
void AccessRights::fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { revokeImpl<FULL_REVOKE_MODE>(access, database, table, columns); }
void AccessRights::fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) { revokeImpl<FULL_REVOKE_MODE>(access, database, table, columns); }
void AccessRights::fullRevoke(const AccessRightsElement & element, std::string_view current_database) { revokeImpl<FULL_REVOKE_MODE>(element, current_database); }
void AccessRights::fullRevoke(const AccessRightsElements & elements, std::string_view current_database) { revokeImpl<FULL_REVOKE_MODE>(elements, current_database); }
AccessRights::Elements AccessRights::getElements() const
{
if (!root)
return {};
Elements res;
if (root->explicit_grants)
res.grants.push_back({root->explicit_grants});
auto global_access = root->access;
if (global_access)
res.grants.push_back({global_access});
if (root->children)
{
for (const auto & [db_name, db_node] : *root->children)
{
if (db_node.partial_revokes)
res.partial_revokes.push_back({db_node.partial_revokes, db_name});
if (db_node.explicit_grants)
res.grants.push_back({db_node.explicit_grants, db_name});
auto db_grants = db_node.access - global_access;
auto db_partial_revokes = global_access - db_node.access;
if (db_partial_revokes)
res.partial_revokes.push_back({db_partial_revokes, db_name});
if (db_grants)
res.grants.push_back({db_grants, db_name});
if (db_node.children)
{
for (const auto & [table_name, table_node] : *db_node.children)
{
if (table_node.partial_revokes)
res.partial_revokes.push_back({table_node.partial_revokes, db_name, table_name});
if (table_node.explicit_grants)
res.grants.push_back({table_node.explicit_grants, db_name, table_name});
auto table_grants = table_node.access - db_node.access;
auto table_partial_revokes = db_node.access - table_node.access;
if (table_partial_revokes)
res.partial_revokes.push_back({table_partial_revokes, db_name, table_name});
if (table_grants)
res.grants.push_back({table_grants, db_name, table_name});
if (table_node.children)
{
for (const auto & [column_name, column_node] : *table_node.children)
{
if (column_node.partial_revokes)
res.partial_revokes.push_back({column_node.partial_revokes, db_name, table_name, column_name});
if (column_node.explicit_grants)
res.grants.push_back({column_node.explicit_grants, db_name, table_name, column_name});
auto column_grants = column_node.access - table_node.access;
auto column_partial_revokes = table_node.access - column_node.access;
if (column_partial_revokes)
res.partial_revokes.push_back({column_partial_revokes, db_name, table_name, column_name});
if (column_grants)
res.grants.push_back({column_grants, db_name, table_name, column_name});
}
}
}
@ -706,59 +628,57 @@ String AccessRights::toString() const
template <typename... Args>
bool AccessRights::isGrantedImpl(const AccessFlags & access, const Args &... args) const
bool AccessRights::isGrantedImpl(const AccessFlags & flags, const Args &... args) const
{
if (!root)
return access.isEmpty();
return root->isGranted(access, args...);
return flags.isEmpty();
return root->isGranted(flags, args...);
}
bool AccessRights::isGrantedImpl(const AccessRightsElement & element, std::string_view current_database) const
bool AccessRights::isGranted(const AccessFlags & flags) const { return isGrantedImpl(flags); }
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database) const { return isGrantedImpl(flags, database); }
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table) const { return isGrantedImpl(flags, database, table); }
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return isGrantedImpl(flags, database, table, column); }
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return isGrantedImpl(flags, database, table, columns); }
bool AccessRights::isGranted(const AccessFlags & flags, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return isGrantedImpl(flags, database, table, columns); }
bool AccessRights::isGranted(const AccessRightsElement & element, std::string_view current_database) const
{
if (element.any_database)
{
return isGrantedImpl(element.access_flags);
return isGranted(element.access_flags);
}
else if (element.any_table)
{
if (element.database.empty())
return isGrantedImpl(element.access_flags, checkCurrentDatabase(current_database));
return isGranted(element.access_flags, checkCurrentDatabase(current_database));
else
return isGrantedImpl(element.access_flags, element.database);
return isGranted(element.access_flags, element.database);
}
else if (element.any_column)
{
if (element.database.empty())
return isGrantedImpl(element.access_flags, checkCurrentDatabase(current_database), element.table);
return isGranted(element.access_flags, checkCurrentDatabase(current_database), element.table);
else
return isGrantedImpl(element.access_flags, element.database, element.table);
return isGranted(element.access_flags, element.database, element.table);
}
else
{
if (element.database.empty())
return isGrantedImpl(element.access_flags, checkCurrentDatabase(current_database), element.table, element.columns);
return isGranted(element.access_flags, checkCurrentDatabase(current_database), element.table, element.columns);
else
return isGrantedImpl(element.access_flags, element.database, element.table, element.columns);
return isGranted(element.access_flags, element.database, element.table, element.columns);
}
}
bool AccessRights::isGrantedImpl(const AccessRightsElements & elements, std::string_view current_database) const
bool AccessRights::isGranted(const AccessRightsElements & elements, std::string_view current_database) const
{
for (const auto & element : elements)
if (!isGrantedImpl(element, current_database))
if (!isGranted(element, current_database))
return false;
return true;
}
bool AccessRights::isGranted(const AccessFlags & access) const { return isGrantedImpl(access); }
bool AccessRights::isGranted(const AccessFlags & access, const std::string_view & database) const { return isGrantedImpl(access, database); }
bool AccessRights::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { return isGrantedImpl(access, database, table); }
bool AccessRights::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return isGrantedImpl(access, database, table, column); }
bool AccessRights::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return isGrantedImpl(access, database, table, columns); }
bool AccessRights::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return isGrantedImpl(access, database, table, columns); }
bool AccessRights::isGranted(const AccessRightsElement & element, std::string_view current_database) const { return isGrantedImpl(element, current_database); }
bool AccessRights::isGranted(const AccessRightsElements & elements, std::string_view current_database) const { return isGrantedImpl(elements, current_database); }
bool operator ==(const AccessRights & left, const AccessRights & right)
{
@ -780,17 +700,17 @@ void AccessRights::merge(const AccessRights & other)
if (other.root)
{
root->merge(*other.root, Helper::instance());
if (root->isEmpty())
if (!root->access && !root->children)
root = nullptr;
}
}
void AccessRights::traceTree() const
void AccessRights::logTree() const
{
auto * log = &Poco::Logger::get("AccessRights");
if (root)
root->traceTree(log);
root->logTree(log);
else
LOG_TRACE(log, "Tree: NULL");
}

View File

@ -23,7 +23,7 @@ public:
bool isEmpty() const;
/// Revokes everything. It's the same as fullRevoke(AccessType::ALL).
/// Revokes everything. It's the same as revoke(AccessType::ALL).
void clear();
/// Grants access on a specified database/table/column.
@ -38,10 +38,7 @@ public:
void grant(const AccessRightsElements & elements, std::string_view current_database = {});
/// Revokes a specified access granted earlier on a specified database/table/column.
/// Does nothing if the specified access is not granted.
/// If the specified access is granted but on upper level (e.g. database for table, table for columns)
/// or lower level, the function also does nothing.
/// This function implements the standard SQL REVOKE behaviour.
/// For example, revoke(AccessType::ALL) revokes all grants at all, just like clear();
void revoke(const AccessFlags & access);
void revoke(const AccessFlags & access, const std::string_view & database);
void revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table);
@ -51,32 +48,6 @@ public:
void revoke(const AccessRightsElement & element, std::string_view current_database = {});
void revoke(const AccessRightsElements & elements, std::string_view current_database = {});
/// Revokes a specified access granted earlier on a specified database/table/column or on lower levels.
/// The function also restricts access if it's granted on upper level.
/// For example, an access could be granted on a database and then revoked on a table in this database.
/// This function implements the MySQL REVOKE behaviour with partial_revokes is ON.
void partialRevoke(const AccessFlags & access);
void partialRevoke(const AccessFlags & access, const std::string_view & database);
void partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table);
void partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column);
void partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns);
void partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns);
void partialRevoke(const AccessRightsElement & element, std::string_view current_database = {});
void partialRevoke(const AccessRightsElements & elements, std::string_view current_database = {});
/// Revokes a specified access granted earlier on a specified database/table/column or on lower levels.
/// The function also restricts access if it's granted on upper level.
/// For example, fullRevoke(AccessType::ALL) revokes all grants at all, just like clear();
/// fullRevoke(AccessType::SELECT, db) means it's not allowed to execute SELECT in that database anymore (from any table).
void fullRevoke(const AccessFlags & access);
void fullRevoke(const AccessFlags & access, const std::string_view & database);
void fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table);
void fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column);
void fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns);
void fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns);
void fullRevoke(const AccessRightsElement & element, std::string_view current_database = {});
void fullRevoke(const AccessRightsElements & elements, std::string_view current_database = {});
/// Returns the information about all the access granted.
struct Elements
{
@ -89,12 +60,12 @@ public:
String toString() const;
/// Whether a specified access granted.
bool isGranted(const AccessFlags & access) const;
bool isGranted(const AccessFlags & access, const std::string_view & database) const;
bool isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const;
bool isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
bool isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
bool isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
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, std::string_view current_database = {}) const;
bool isGranted(const AccessRightsElements & elements, std::string_view current_database = {}) const;
@ -107,22 +78,13 @@ public:
private:
template <typename... Args>
void grantImpl(const AccessFlags & access, const Args &... args);
void grantImpl(const AccessRightsElement & element, std::string_view current_database);
void grantImpl(const AccessRightsElements & elements, std::string_view current_database);
template <int mode, typename... Args>
void revokeImpl(const AccessFlags & access, const Args &... args);
template <int mode>
void revokeImpl(const AccessRightsElement & element, std::string_view current_database);
template <int mode>
void revokeImpl(const AccessRightsElements & elements, std::string_view current_database);
void grantImpl(const AccessFlags & flags, const Args &... args);
template <typename... Args>
bool isGrantedImpl(const AccessFlags & access, const Args &... args) const;
void revokeImpl(const AccessFlags & flags, const Args &... args);
template <typename... Args>
bool isGrantedImpl(const AccessFlags & flags, const Args &... args) const;
bool isGrantedImpl(const AccessRightsElement & element, std::string_view current_database) const;
bool isGrantedImpl(const AccessRightsElements & elements, std::string_view current_database) const;
@ -130,7 +92,7 @@ private:
template <typename... Args>
AccessFlags getAccessImpl(const Args &... args) const;
void traceTree() const;
void logTree() const;
struct Node;
std::unique_ptr<Node> root;

View File

@ -444,19 +444,19 @@ boost::shared_ptr<const AccessRights> AccessRightsContext::calculateResultAccess
result.grant(AccessType::SELECT, DatabaseCatalog::TEMPORARY_DATABASE);
if (readonly_)
result.fullRevoke(write_table_access | all_dcl | AccessType::SYSTEM | AccessType::KILL);
result.revoke(write_table_access | all_dcl | AccessType::SYSTEM | AccessType::KILL);
if (readonly_ || !allow_ddl_)
result.fullRevoke(table_and_dictionary_ddl);
result.revoke(table_and_dictionary_ddl);
if (readonly_ && grant_option)
result.fullRevoke(AccessType::ALL);
result.revoke(AccessType::ALL);
if (readonly_ == 1)
{
/// Table functions are forbidden in readonly mode.
/// For example, for readonly = 2 - allowed.
result.fullRevoke(AccessType::CREATE_TEMPORARY_TABLE | AccessType::TABLE_FUNCTIONS);
result.revoke(AccessType::CREATE_TEMPORARY_TABLE | AccessType::TABLE_FUNCTIONS);
}
else if (readonly_ == 2)
{
@ -465,7 +465,7 @@ boost::shared_ptr<const AccessRights> AccessRightsContext::calculateResultAccess
}
if (!allow_introspection_)
result.fullRevoke(AccessType::INTROSPECTION);
result.revoke(AccessType::INTROSPECTION);
result_access_cache[cache_index].store(result_ptr);

View File

@ -141,14 +141,14 @@ namespace
if (databases)
{
user->access.fullRevoke(AccessFlags::databaseLevel());
user->access.revoke(AccessFlags::databaseLevel());
for (const String & database : *databases)
user->access.grant(AccessFlags::databaseLevel(), database);
}
if (dictionaries)
{
user->access.fullRevoke(AccessType::dictGet, IDictionary::NO_DATABASE_TAG);
user->access.revoke(AccessType::dictGet, IDictionary::NO_DATABASE_TAG);
for (const String & dictionary : *dictionaries)
user->access.grant(AccessType::dictGet, IDictionary::NO_DATABASE_TAG, dictionary);
}

View File

@ -395,7 +395,6 @@ struct Settings : public SettingsCollection<Settings>
M(SettingBool, allow_experimental_alter_materialized_view_structure, false, "Allow atomic alter on Materialized views. Work in progress.", 0) \
M(SettingBool, enable_early_constant_folding, true, "Enable query optimization where we analyze function and subqueries results and rewrite query if there're constants there", 0) \
\
M(SettingBool, partial_revokes, false, "Makes it possible to revoke privileges partially.", 0) \
M(SettingBool, deduplicate_blocks_in_dependent_materialized_views, false, "Should deduplicate blocks for materialized views if the block is not a duplicate for the table. Use true to always deduplicate in dependent tables.", 0) \
M(SettingBool, use_compact_format_in_distributed_parts_names, false, "Changes format of directories names for distributed table insert parts.", 0) \
\

View File

@ -14,7 +14,7 @@ namespace DB
namespace
{
template <typename T>
void updateFromQueryImpl(T & grantee, const ASTGrantQuery & query, const std::vector<UUID> & roles_from_query, const String & current_database, bool partial_revokes)
void updateFromQueryImpl(T & grantee, const ASTGrantQuery & query, const std::vector<UUID> & roles_from_query, const String & current_database)
{
using Kind = ASTGrantQuery::Kind;
if (!query.access_rights_elements.empty())
@ -25,12 +25,6 @@ namespace
if (query.grant_option)
grantee.access_with_grant_option.grant(query.access_rights_elements, current_database);
}
else if (partial_revokes)
{
grantee.access_with_grant_option.partialRevoke(query.access_rights_elements, current_database);
if (!query.grant_option)
grantee.access.partialRevoke(query.access_rights_elements, current_database);
}
else
{
grantee.access_with_grant_option.revoke(query.access_rights_elements, current_database);
@ -79,19 +73,18 @@ BlockIO InterpreterGrantQuery::execute()
std::vector<UUID> to_roles = GenericRoleSet{*query.to_roles, access_control, context.getUserID()}.getMatchingUsersAndRoles(access_control);
String current_database = context.getCurrentDatabase();
bool partial_revokes = context.getSettingsRef().partial_revokes;
auto update_func = [&](const AccessEntityPtr & entity) -> AccessEntityPtr
{
auto clone = entity->clone();
if (auto user = typeid_cast<std::shared_ptr<User>>(clone))
{
updateFromQueryImpl(*user, query, roles_from_query, current_database, partial_revokes);
updateFromQueryImpl(*user, query, roles_from_query, current_database);
return user;
}
else if (auto role = typeid_cast<std::shared_ptr<Role>>(clone))
{
updateFromQueryImpl(*role, query, roles_from_query, current_database, partial_revokes);
updateFromQueryImpl(*role, query, roles_from_query, current_database);
return role;
}
else
@ -109,7 +102,7 @@ void InterpreterGrantQuery::updateUserFromQuery(User & user, const ASTGrantQuery
std::vector<UUID> roles_from_query;
if (query.roles)
roles_from_query = GenericRoleSet{*query.roles}.getMatchingIDs();
updateFromQueryImpl(user, query, roles_from_query, {}, true);
updateFromQueryImpl(user, query, roles_from_query, {});
}
@ -118,7 +111,7 @@ void InterpreterGrantQuery::updateRoleFromQuery(Role & role, const ASTGrantQuery
std::vector<UUID> roles_from_query;
if (query.roles)
roles_from_query = GenericRoleSet{*query.roles}.getMatchingIDs();
updateFromQueryImpl(role, query, roles_from_query, {}, true);
updateFromQueryImpl(role, query, roles_from_query, {});
}
}

View File

@ -1,5 +1,2 @@
A
GRANT SELECT ON *.* TO test_user_01074
B
GRANT SELECT ON *.* TO test_user_01074
REVOKE SELECT ON db.* FROM test_user_01074

View File

@ -1,15 +1,8 @@
DROP USER IF EXISTS test_user_01074;
CREATE USER test_user_01074;
SELECT 'A';
SET partial_revokes=0;
GRANT SELECT ON *.* TO test_user_01074;
REVOKE SELECT ON db.* FROM test_user_01074;
SHOW GRANTS FOR test_user_01074;
SELECT 'B';
SET partial_revokes=1;
REVOKE SELECT ON db.* FROM test_user_01074;
SHOW GRANTS FOR test_user_01074;
DROP USER test_user_01074;