Fix tests

This commit is contained in:
pufit 2024-07-03 00:01:08 -04:00
parent f55061ffab
commit fe9638c14d
5 changed files with 100 additions and 41 deletions

View File

@ -429,7 +429,7 @@ public:
template <bool wildcard = false>
bool isGranted(const AccessFlags & flags_) const
{
return min_flags_with_children.contains(flags_);
return wildcard_grant ? flags.contains(flags_) : min_flags_with_children.contains(flags_);
}
template <bool wildcard = false, typename... Args>
@ -444,11 +444,8 @@ public:
const auto & [node, final] = tryGetLeafOrPrefix(name);
if (final)
return node.isGranted<wildcard>(flags_to_check, subnames...);
else if (node.isLeaf())
return node.flags.contains(flags_to_check);
else
return node.min_flags_with_children.contains(flags_to_check);
return node.flags.contains(flags_to_check);
}
template <bool wildcard = false, typename StringT>
@ -531,7 +528,9 @@ public:
static ProtoElements getElements(const Node & node, const Node & node_with_grant_option)
{
ProtoElements res;
getElementsRec(res, node, node_with_grant_option);
Node tmp_node = node;
Node tmp_node_go = node_with_grant_option;
getElementsRec(res, {}, &tmp_node, {}, &tmp_node_go, {}, "");
return res;
}
@ -772,37 +771,100 @@ private:
/// Dumps GRANT/REVOKE elements from the tree with grant options.
static void getElementsRec(
ProtoElements & res,
const Node & node,
const Node & node_go)
boost::container::small_vector<String, 3> full_name,
Node * node,
const AccessFlags & parent_flags,
Node * node_go,
const AccessFlags & parent_flags_go,
String path)
{
auto go_grants = node_go.getElements();
for (auto & grant : go_grants)
{
grant.grant_option = true;
res.push_back(grant);
}
auto grantable_flags = ::DB::getAllGrantableFlags(static_cast<Level>(full_name.size()));
auto parent_fl = parent_flags & grantable_flags;
auto parent_fl_go = parent_flags_go & grantable_flags;
auto flags = node ? node->flags : parent_fl;
auto flags_go = node_go ? node_go->flags : parent_fl_go;
auto revokes = parent_fl - flags;
auto revokes_go = parent_fl_go - flags_go - revokes;
auto grants_go = flags_go - parent_fl_go;
auto grants = flags - parent_fl - grants_go;
auto grants = node.getElements();
for (auto & grant : grants)
Node * target_node = node;
if (!target_node)
target_node = node_go;
/// Inserts into result only meaningful nodes (e.g. wildcards or leafs).
if (target_node && (target_node->isLeaf() || target_node->wildcard_grant))
{
/// Checks if the grant is already covered by `WITH GRANT OPTION` grants.
/// This is highly unoptimized, but much easier to implement instead of previous implementation with double tree traversal.
if (!grant.is_partial_revoke)
boost::container::small_vector<String, 3> new_full_name = full_name;
if (target_node->level != Level::GLOBAL_LEVEL)
{
if (grant.full_name.size() == 0 && node_go.isGranted(grant.access_flags))
continue;
if (grant.full_name.size() == 1 && node_go.isGranted(grant.access_flags, grant.full_name[0]))
continue;
if (grant.full_name.size() == 2 && node_go.isGranted(grant.access_flags, grant.full_name[0], grant.full_name[1]))
continue;
if (grant.full_name.size() == 3 && node_go.isGranted(grant.access_flags, grant.full_name[0], grant.full_name[1], grant.full_name[2]))
continue;
new_full_name.push_back(path);
/// If in leaf, flushes the current path into `full_name`.
if (target_node->isLeaf())
{
full_name.push_back(path);
path.clear();
}
}
res.push_back(grant);
if (revokes)
res.push_back(ProtoElement{revokes, new_full_name, false, true, node->wildcard_grant});
if (revokes_go)
res.push_back(ProtoElement{revokes_go, new_full_name, true, true, node_go->wildcard_grant});
if (grants)
res.push_back(ProtoElement{grants, new_full_name, false, false, node->wildcard_grant});
if (grants_go)
res.push_back(ProtoElement{grants_go, new_full_name, true, false, node_go->wildcard_grant});
}
if (node && node->children)
{
for (auto & child : *node->children)
{
String new_path = path;
new_path.append(child.node_name);
Node * child_node = &child;
Node * child_node_go = nullptr;
if (node_go)
child_node_go = &node_go->getLeaf(child.node_name, child.level, !child.isLeaf());
getElementsRec(res, full_name, child_node, flags, child_node_go, flags_go, new_path);
}
}
if (node_go && node_go->children)
{
for (auto & child : *node_go->children)
{
if (node && node->children)
{
auto starts_with = [&child](const Node & n)
{
if (child.isLeaf())
return n.isLeaf();
return !n.isLeaf() && child.node_name[0] == n.node_name[0];
};
if (auto it = std::find_if(node->children->begin(), node->children->end(), starts_with); it != node->children->end())
continue; /// already processed
}
String new_path = path;
new_path.append(child.node_name);
Node * child_node = nullptr;
Node * child_node_go = &child;
getElementsRec(res, full_name, child_node, flags, child_node_go, flags_go, new_path);
}
}
}
void optimizeChildren()
{
if (!children)

View File

@ -62,10 +62,9 @@ namespace
formatAccessFlagsWithColumns(element, result);
result += " ";
{
WriteBufferFromString buffer(result);
element.formatONClause(buffer);
}
WriteBufferFromOwnString buffer;
element.formatONClause(buffer);
result += buffer.str();
if (with_options)
formatOptions(element.grant_option, element.is_partial_revoke, result);
@ -101,12 +100,9 @@ namespace
if (!next_element_uses_same_table_and_options)
{
part += " ";
String on_clause;
{
WriteBufferFromString buffer(on_clause);
element.formatONClause(buffer);
}
part.append(std::move(on_clause));
WriteBufferFromOwnString buffer;
element.formatONClause(buffer);
part += buffer.str();
if (with_options)
formatOptions(element.grant_option, element.is_partial_revoke, part);

View File

@ -260,7 +260,7 @@ TEST(AccessRights, Union)
rhs.grantWildcardWithGrantOption(AccessType::SELECT, "db1", "tb1");
rhs.revokeWildcardGrantOption(AccessType::SELECT, "db1", "tb1", "col");
lhs.makeUnion(rhs);
ASSERT_EQ(lhs.toString(), "GRANT SELECT ON db1.tb1*, GRANT SELECT ON db1.tb1* WITH GRANT OPTION, REVOKE GRANT OPTION SELECT(col*) ON db1.tb1");
ASSERT_EQ(lhs.toString(), "GRANT SELECT ON db1.tb1* WITH GRANT OPTION, REVOKE GRANT OPTION SELECT(col*) ON db1.tb1");
lhs = {};
rhs = {};

View File

@ -28,7 +28,7 @@ namespace
String res_database, res_table_name;
bool wildcard = false;
if (!parseDatabaseAndTableNameOrAsterisks(pos, expected, res_database, res_table_name, wildcard) || res_database.empty())
if (!parseDatabaseAndTableNameOrAsterisks(pos, expected, res_database, res_table_name, wildcard) || (res_database.empty() && res_table_name.empty()))
{
return false;
}

3
tests/queries/0_stateless/03141_wildcard_grants.sh Normal file → Executable file
View File

@ -5,6 +5,8 @@ CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=../shell_config.sh
. "$CURDIR"/../shell_config.sh
set -e
user1="user03141_1_${CLICKHOUSE_DATABASE}_$RANDOM"
user2="user03141_2_${CLICKHOUSE_DATABASE}_$RANDOM"
user3="user03141_3_${CLICKHOUSE_DATABASE}_$RANDOM"
@ -20,7 +22,6 @@ CREATE TABLE $db.test_table_another_prefix (s String) ENGINE = MergeTree ORDER B
DROP USER IF EXISTS $user1, $user2, $user3;
CREATE USER $user1, $user2, $user3;
GRANT SELECT ON $db.* TO $user1;
EOF
${CLICKHOUSE_CLIENT} --multiquery <<EOF