Small refactor, additional tests

This commit is contained in:
pufit 2023-04-11 19:55:59 -04:00
parent d1fa9596b8
commit 078b8f5399
5 changed files with 76 additions and 38 deletions

View File

@ -40,7 +40,7 @@ GRANT [ON CLUSTER cluster_name] role [,...] TO {user | another_role | CURRENT_US
## Синтаксис присвоения текущих привилегий {#grant-current-grants-syntax}
```sql
GRANT [ON CLUSTER cluster_name] privilege[(column_name [,...])] [,...] ON {db.table|db.*|*.*|table|*} TO {user | role | CURRENT_USER} [,...] [WITH GRANT OPTION] [WITH REPLACE OPTION]
GRANT CURRENT GRANTS[(privilege[(column_name [,...])] [,...] ON {db.table|db.*|*.*|table|*})] TO {user | role | CURRENT_USER} [,...] [WITH GRANT OPTION] [WITH REPLACE OPTION]
```
- `privilege` — Тип привилегии

View File

@ -334,14 +334,22 @@ namespace
template <typename T>
void grantCurrentGrantsTemplate(
T & grantee,
const AccessRightsElements & elements_to_grant,
const AccessRightsElements & elements_to_revoke,
std::shared_ptr<const ContextAccess> current_user_access,
bool grant_option)
const AccessRights & rights_to_grant,
const AccessRightsElements & elements_to_revoke)
{
if (!elements_to_revoke.empty())
grantee.access.revoke(elements_to_revoke);
grantee.access.makeUnion(rights_to_grant);
}
/// Grants current user's grants with grant options to specified user.
void grantCurrentGrants(
IAccessEntity & grantee,
const AccessRightsElements & elements_to_grant,
const AccessRightsElements & elements_to_revoke,
std::shared_ptr<const ContextAccess> current_user_access)
{
/// We need to collect all current user's grant and filter them by grant option.
AccessRightsElements current_user_grantable_elements;
auto available_grant_elements = current_user_access->getAccessRights()->getElements();
@ -360,36 +368,13 @@ namespace
current_user_rights.grant(element);
}
/// If elements_to_grant was not specified it will grant all available for user grants.
/// Otherwise, we will intersect available grants with given set.
if (elements_to_grant.empty())
{
if (!grant_option)
current_user_rights.revokeGrantOption(AccessType::ALL);
AccessRights new_rights(elements_to_grant);
new_rights.makeIntersection(current_user_rights);
grantee.access.makeUnion(current_user_rights);
}
else
{
AccessRights new_rights(elements_to_grant);
new_rights.makeIntersection(current_user_rights);
grantee.access.makeUnion(new_rights);
}
}
/// Grants current user's grants with grant options to specified user.
void grantCurrentGrants(
IAccessEntity & grantee,
const AccessRightsElements & elements_to_grant,
const AccessRightsElements & elements_to_revoke,
std::shared_ptr<const ContextAccess> current_user_access,
bool grant_option)
{
if (auto * user = typeid_cast<User *>(&grantee))
grantCurrentGrantsTemplate(*user, elements_to_grant, elements_to_revoke, current_user_access, grant_option);
grantCurrentGrantsTemplate(*user, new_rights, elements_to_revoke);
else if (auto * role = typeid_cast<Role *>(&grantee))
grantCurrentGrantsTemplate(*role, elements_to_grant, elements_to_revoke, current_user_access, grant_option);
grantCurrentGrantsTemplate(*role, new_rights, elements_to_revoke);
}
/// Updates grants of a specified user or role.
@ -465,7 +450,7 @@ BlockIO InterpreterGrantQuery::execute()
{
auto clone = entity->clone();
if (query.current_grants)
grantCurrentGrants(*clone, elements_to_grant, elements_to_revoke, current_user_access, query.with_grant_option);
grantCurrentGrants(*clone, elements_to_grant, elements_to_revoke, current_user_access);
else
updateGrantedAccessRightsAndRoles(*clone, elements_to_grant, elements_to_revoke, roles_to_grant, roles_to_revoke, query.admin_option);
return clone;

View File

@ -27,7 +27,6 @@ public:
bool replace_access = false;
bool replace_granted_roles = false;
bool current_grants = false;
bool with_grant_option = false;
std::shared_ptr<ASTRolesOrUsersSet> grantees;

View File

@ -296,6 +296,11 @@ bool ParserGrantQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
if (!ParserToken(TokenType::ClosingRoundBracket).ignore(pos, expected))
return false;
}
/// If no elements were specified it will grant all available for user grants.
/// Using `.size() == 0` because `.empty()` is overridden and returns true for NONE elements.
if (elements.size() == 0) // NOLINT
elements.emplace_back(AccessType::ALL);
}
else
{
@ -370,7 +375,6 @@ bool ParserGrantQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
query->replace_access = replace_access;
query->replace_granted_roles = replace_role;
query->current_grants = current_grants;
query->with_grant_option = grant_option;
return true;
}

View File

@ -20,6 +20,9 @@ def start_cluster():
instance.query(
"CREATE TABLE test.table(x UInt32, y UInt32) ENGINE = MergeTree ORDER BY tuple()"
)
instance.query(
"CREATE TABLE test.table2(x UInt32, y UInt32) ENGINE = MergeTree ORDER BY tuple()"
)
instance.query("INSERT INTO test.table VALUES (1,5), (2,10)")
yield cluster
@ -608,19 +611,29 @@ def test_grant_current_grants():
["GRANT CREATE TABLE, CREATE VIEW ON test.* TO C"]
)
instance.query("DROP USER IF EXISTS C")
instance.query("CREATE USER C")
instance.query("GRANT CURRENT GRANTS(NONE ON *.*) TO C", user="A")
assert instance.query("SHOW GRANTS FOR C") == TSV([])
def test_grant_current_grants_with_partial_revoke():
instance.query("CREATE USER A")
instance.query("GRANT CREATE TABLE ON *.* TO A")
instance.query("REVOKE CREATE TABLE ON test.* FROM A")
instance.query("GRANT CREATE TABLE ON test.table TO A WITH GRANT OPTION")
instance.query("GRANT SELECT ON *.* TO A WITH GRANT OPTION")
instance.query("REVOKE SELECT ON test.* FROM A")
instance.query("GRANT SELECT ON test.table TO A WITH GRANT OPTION")
instance.query("GRANT SELECT ON test.table2 TO A")
assert instance.query("SHOW GRANTS FOR A") == TSV(
[
"GRANT CREATE TABLE ON *.* TO A",
"GRANT SELECT ON *.* TO A WITH GRANT OPTION",
"REVOKE SELECT ON test.* FROM A",
"GRANT SELECT ON test.table TO A WITH GRANT OPTION",
"REVOKE SELECT, CREATE TABLE ON test.* FROM A",
"GRANT SELECT, CREATE TABLE ON test.table TO A WITH GRANT OPTION",
"GRANT SELECT ON test.table2 TO A",
]
)
@ -630,7 +643,29 @@ def test_grant_current_grants_with_partial_revoke():
[
"GRANT SELECT ON *.* TO B",
"REVOKE SELECT ON test.* FROM B",
"GRANT SELECT ON test.table TO B",
"GRANT SELECT, CREATE TABLE ON test.table TO B",
]
)
instance.query("DROP USER IF EXISTS B")
instance.query("CREATE USER B")
instance.query("GRANT CURRENT GRANTS TO B WITH GRANT OPTION", user="A")
assert instance.query("SHOW GRANTS FOR B") == TSV(
[
"GRANT SELECT ON *.* TO B WITH GRANT OPTION",
"REVOKE SELECT ON test.* FROM B",
"GRANT SELECT, CREATE TABLE ON test.table TO B WITH GRANT OPTION",
]
)
instance.query("DROP USER IF EXISTS C")
instance.query("CREATE USER C")
instance.query("GRANT SELECT ON test.* TO B")
instance.query("GRANT CURRENT GRANTS TO C", user="B")
assert instance.query("SHOW GRANTS FOR C") == TSV(
[
"GRANT SELECT ON *.* TO C",
"GRANT CREATE TABLE ON test.table TO C",
]
)
@ -660,3 +695,18 @@ def test_current_grants_override():
"GRANT SELECT ON test.table TO B",
]
)
instance.query("DROP USER IF EXISTS B")
instance.query("CREATE USER B")
instance.query("GRANT SELECT ON test.table TO B")
assert instance.query("SHOW GRANTS FOR B") == TSV(
["GRANT SELECT ON test.table TO B"]
)
instance.query("GRANT CURRENT GRANTS TO B WITH REPLACE OPTION", user="A")
assert instance.query("SHOW GRANTS FOR B") == TSV(
[
"GRANT SELECT ON *.* TO B",
"REVOKE SELECT ON test.* FROM B",
]
)