Add WITH IMPLICIT, fix error with implicit grants

This commit is contained in:
pufit 2024-10-02 23:20:33 -04:00
parent 229ead5f91
commit 2a107e3429
14 changed files with 282 additions and 207 deletions

View File

@ -351,7 +351,7 @@ Shows privileges for a user.
**Syntax** **Syntax**
``` sql ``` sql
SHOW GRANTS [FOR user1 [, user2 ...]] SHOW GRANTS [FOR user1 [, user2 ...]] [WITH IMPLICIT]
``` ```
If user is not specified, the query returns privileges for the current user. If user is not specified, the query returns privileges for the current user.

View File

@ -234,7 +234,7 @@ SHOW DICTIONARIES FROM db LIKE '%reg%' LIMIT 2
### Синтаксис {#show-grants-syntax} ### Синтаксис {#show-grants-syntax}
``` sql ``` sql
SHOW GRANTS [FOR user] SHOW GRANTS [FOR user1 [, user2 ...]] [WITH IMPLICIT]
``` ```
Если пользователь не задан, запрос возвращает привилегии текущего пользователя. Если пользователь не задан, запрос возвращает привилегии текущего пользователя.

View File

@ -110,7 +110,7 @@ SHOW DICTIONARIES FROM db LIKE '%reg%' LIMIT 2
### 语法 {#show-grants-syntax} ### 语法 {#show-grants-syntax}
``` sql ``` sql
SHOW GRANTS [FOR user] SHOW GRANTS [FOR user1 [, user2 ...]] [WITH IMPLICIT]
``` ```
如果未指定用户,输出当前用户的权限 如果未指定用户,输出当前用户的权限

View File

@ -1155,9 +1155,6 @@ private:
calculateMinMaxFlags(); calculateMinMaxFlags();
if (!isLeaf())
return;
auto new_flags = function(flags, min_flags_with_children, max_flags_with_children, level, grant_option); auto new_flags = function(flags, min_flags_with_children, max_flags_with_children, level, grant_option);
if (new_flags != flags) if (new_flags != flags)

View File

@ -64,7 +64,27 @@ namespace
} }
AccessRights addImplicitAccessRights(const AccessRights & access, const AccessControl & access_control) std::array<UUID, 1> to_array(const UUID & id)
{
std::array<UUID, 1> ids;
ids[0] = id;
return ids;
}
/// Helper for using in templates.
std::string_view getDatabase() { return {}; }
template <typename... OtherArgs>
std::string_view getDatabase(std::string_view arg1, const OtherArgs &...) { return arg1; }
std::string_view getTableEngine() { return {}; }
template <typename... OtherArgs>
std::string_view getTableEngine(std::string_view arg1, const OtherArgs &...) { return arg1; }
}
AccessRights ContextAccess::addImplicitAccessRights(const AccessRights & access, const AccessControl & access_control)
{ {
AccessFlags max_flags; AccessFlags max_flags;
@ -254,26 +274,6 @@ namespace
} }
std::array<UUID, 1> to_array(const UUID & id)
{
std::array<UUID, 1> ids;
ids[0] = id;
return ids;
}
/// Helper for using in templates.
std::string_view getDatabase() { return {}; }
template <typename... OtherArgs>
std::string_view getDatabase(std::string_view arg1, const OtherArgs &...) { return arg1; }
std::string_view getTableEngine() { return {}; }
template <typename... OtherArgs>
std::string_view getTableEngine(std::string_view arg1, const OtherArgs &...) { return arg1; }
}
std::shared_ptr<const ContextAccess> ContextAccess::fromContext(const ContextPtr & context) std::shared_ptr<const ContextAccess> ContextAccess::fromContext(const ContextPtr & context)
{ {
return ContextAccessWrapper::fromContext(context)->getAccess(); return ContextAccessWrapper::fromContext(context)->getAccess();

View File

@ -133,6 +133,8 @@ public:
/// Checks if grantees are allowed for the current user, throws an exception if not. /// Checks if grantees are allowed for the current user, throws an exception if not.
void checkGranteesAreAllowed(const std::vector<UUID> & grantee_ids) const; void checkGranteesAreAllowed(const std::vector<UUID> & grantee_ids) const;
static AccessRights addImplicitAccessRights(const AccessRights & access, const AccessControl & access_control);
ContextAccess(const AccessControl & access_control_, const Params & params_); ContextAccess(const AccessControl & access_control_, const Params & params_);
~ContextAccess(); ~ContextAccess();

View File

@ -32,7 +32,8 @@ namespace
ASTs getGrantQueriesImpl( ASTs getGrantQueriesImpl(
const T & grantee, const T & grantee,
const AccessControl * access_control /* not used if attach_mode == true */, const AccessControl * access_control /* not used if attach_mode == true */,
bool attach_mode = false) bool attach_mode = false,
bool with_implicit = false)
{ {
ASTs res; ASTs res;
@ -41,7 +42,13 @@ namespace
std::shared_ptr<ASTGrantQuery> current_query = nullptr; std::shared_ptr<ASTGrantQuery> current_query = nullptr;
for (const auto & element : grantee.access.getElements()) AccessRightsElements elements;
if (with_implicit)
elements = ContextAccess::addImplicitAccessRights(grantee.access, *access_control).getElements();
else
elements = grantee.access.getElements();
for (const auto & element : elements)
{ {
if (element.empty()) if (element.empty())
continue; continue;
@ -89,12 +96,13 @@ namespace
ASTs getGrantQueriesImpl( ASTs getGrantQueriesImpl(
const IAccessEntity & entity, const IAccessEntity & entity,
const AccessControl * access_control /* not used if attach_mode == true */, const AccessControl * access_control /* not used if attach_mode == true */,
bool attach_mode = false) bool attach_mode = false,
bool with_implicit = false)
{ {
if (const User * user = typeid_cast<const User *>(&entity)) if (const User * user = typeid_cast<const User *>(&entity))
return getGrantQueriesImpl(*user, access_control, attach_mode); return getGrantQueriesImpl(*user, access_control, attach_mode, with_implicit);
if (const Role * role = typeid_cast<const Role *>(&entity)) if (const Role * role = typeid_cast<const Role *>(&entity))
return getGrantQueriesImpl(*role, access_control, attach_mode); return getGrantQueriesImpl(*role, access_control, attach_mode, with_implicit);
throw Exception(ErrorCodes::LOGICAL_ERROR, "{} is expected to be user or role", entity.formatTypeWithName()); throw Exception(ErrorCodes::LOGICAL_ERROR, "{} is expected to be user or role", entity.formatTypeWithName());
} }
@ -180,23 +188,24 @@ ASTs InterpreterShowGrantsQuery::getGrantQueries() const
auto entities = getEntities(); auto entities = getEntities();
const auto & access_control = getContext()->getAccessControl(); const auto & access_control = getContext()->getAccessControl();
const auto & show_query = query_ptr->as<const ASTShowGrantsQuery &>();
ASTs grant_queries; ASTs grant_queries;
for (const auto & entity : entities) for (const auto & entity : entities)
boost::range::push_back(grant_queries, getGrantQueries(*entity, access_control)); boost::range::push_back(grant_queries, getGrantQueries(*entity, access_control, show_query.with_implicit));
return grant_queries; return grant_queries;
} }
ASTs InterpreterShowGrantsQuery::getGrantQueries(const IAccessEntity & user_or_role, const AccessControl & access_control) ASTs InterpreterShowGrantsQuery::getGrantQueries(const IAccessEntity & user_or_role, const AccessControl & access_control, bool with_implicit)
{ {
return getGrantQueriesImpl(user_or_role, &access_control, false); return getGrantQueriesImpl(user_or_role, &access_control, false, with_implicit);
} }
ASTs InterpreterShowGrantsQuery::getAttachGrantQueries(const IAccessEntity & user_or_role) ASTs InterpreterShowGrantsQuery::getAttachGrantQueries(const IAccessEntity & user_or_role)
{ {
return getGrantQueriesImpl(user_or_role, nullptr, true); return getGrantQueriesImpl(user_or_role, nullptr, true, false);
} }
void registerInterpreterShowGrantsQuery(InterpreterFactory & factory) void registerInterpreterShowGrantsQuery(InterpreterFactory & factory)

View File

@ -20,7 +20,7 @@ public:
BlockIO execute() override; BlockIO execute() override;
static ASTs getGrantQueries(const IAccessEntity & user_or_role, const AccessControl & access_control); static ASTs getGrantQueries(const IAccessEntity & user_or_role, const AccessControl & access_control, bool with_implicit = false);
static ASTs getAttachGrantQueries(const IAccessEntity & user_or_role); static ASTs getAttachGrantQueries(const IAccessEntity & user_or_role);
bool ignoreQuota() const override { return true; } bool ignoreQuota() const override { return true; }

View File

@ -38,5 +38,11 @@ void ASTShowGrantsQuery::formatQueryImpl(const FormatSettings & settings, Format
<< (settings.hilite ? hilite_none : ""); << (settings.hilite ? hilite_none : "");
for_roles->format(settings); for_roles->format(settings);
} }
if (with_implicit)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << " WITH IMPLICIT"
<< (settings.hilite ? hilite_none : "");
}
} }
} }

View File

@ -7,12 +7,13 @@ namespace DB
{ {
class ASTRolesOrUsersSet; class ASTRolesOrUsersSet;
/** SHOW GRANTS [FOR user_name] /** SHOW GRANTS [FOR user1 [, user2 ...]] [WITH IMPLICIT]
*/ */
class ASTShowGrantsQuery : public ASTQueryWithOutput class ASTShowGrantsQuery : public ASTQueryWithOutput
{ {
public: public:
std::shared_ptr<ASTRolesOrUsersSet> for_roles; std::shared_ptr<ASTRolesOrUsersSet> for_roles;
bool with_implicit = false;
String getID(char) const override; String getID(char) const override;
ASTPtr clone() const override; ASTPtr clone() const override;

View File

@ -31,8 +31,13 @@ bool ParserShowGrantsQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
for_roles->current_user = true; for_roles->current_user = true;
} }
bool with_implicit = false;
if (ParserKeyword{Keyword::WITH_IMPLICIT}.ignore(pos, expected))
with_implicit = true;
auto query = std::make_shared<ASTShowGrantsQuery>(); auto query = std::make_shared<ASTShowGrantsQuery>();
query->for_roles = std::move(for_roles); query->for_roles = std::move(for_roles);
query->with_implicit = with_implicit;
node = query; node = query;
return true; return true;

View File

@ -530,6 +530,7 @@ namespace DB
MR_MACROS(WITH_NAME, "WITH NAME") \ MR_MACROS(WITH_NAME, "WITH NAME") \
MR_MACROS(WITH_REPLACE_OPTION, "WITH REPLACE OPTION") \ MR_MACROS(WITH_REPLACE_OPTION, "WITH REPLACE OPTION") \
MR_MACROS(WITH_TIES, "WITH TIES") \ MR_MACROS(WITH_TIES, "WITH TIES") \
MR_MACROS(WITH_IMPLICIT, "WITH IMPLICIT") \
MR_MACROS(WITH, "WITH") \ MR_MACROS(WITH, "WITH") \
MR_MACROS(RECURSIVE, "RECURSIVE") \ MR_MACROS(RECURSIVE, "RECURSIVE") \
MR_MACROS(WK, "WK") \ MR_MACROS(WK, "WK") \

View File

@ -0,0 +1,29 @@
Empty grants
GRANT SOURCES ON *.*
GRANT TABLE ENGINE ON *
GRANT SELECT ON system.aggregate_function_combinators
GRANT SELECT ON system.collations
GRANT SELECT ON system.columns
GRANT SELECT ON system.contributors
GRANT SELECT ON system.current_roles
GRANT SELECT ON system.data_type_families
GRANT SELECT ON system.database_engines
GRANT SELECT ON system.databases
GRANT SELECT ON system.enabled_roles
GRANT SELECT ON system.formats
GRANT SELECT ON system.functions
GRANT SELECT ON system.licenses
GRANT SELECT ON system.one
GRANT SELECT ON system.privileges
GRANT SELECT ON system.quota_usage
GRANT SELECT ON system.settings
GRANT SELECT ON system.table_engines
GRANT SELECT ON system.table_functions
GRANT SELECT ON system.tables
GRANT SELECT ON system.time_zones
Revoke grants
GRANT SHOW DATABASES, SHOW TABLES, SHOW COLUMNS, SELECT, SOURCES ON *.*
GRANT TABLE ENGINE ON *
REVOKE SHOW TABLES, SHOW COLUMNS, SELECT ON test_03247.`table`
OK
0

View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=../shell_config.sh
. "$CURDIR"/../shell_config.sh
user="user03247_${CLICKHOUSE_DATABASE}_$RANDOM"
${CLICKHOUSE_CLIENT} --query "DROP USER IF EXISTS $user;";
${CLICKHOUSE_CLIENT} --query "CREATE USER $user;";
${CLICKHOUSE_CLIENT} --query "GRANT SOURCES ON *.* TO $user;";
echo "Empty grants";
${CLICKHOUSE_CLIENT} --query "SHOW GRANTS FOR $user WITH IMPLICIT;" | sed 's/ TO.*//';
echo "Revoke grants";
${CLICKHOUSE_CLIENT} --query "GRANT SELECT ON *.* TO $user ;";
${CLICKHOUSE_CLIENT} --query "REVOKE SELECT ON test_03247.table FROM $user;";
${CLICKHOUSE_CLIENT} --query "SHOW GRANTS FOR $user WITH IMPLICIT;" | sed 's/ TO.*//' | sed 's/ FROM.*//';
(( $(${CLICKHOUSE_CLIENT} --user $user --query "EXISTS test_03247.table;" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED";
${CLICKHOUSE_CLIENT} --query "EXISTS test_03247.table2;" --user $user;
${CLICKHOUSE_CLIENT} --query "DROP USER IF EXISTS $user;";