diff --git a/src/Access/ExtendedRoleSet.cpp b/src/Access/ExtendedRoleSet.cpp index b59dc7ac232..eed475bc3cc 100644 --- a/src/Access/ExtendedRoleSet.cpp +++ b/src/Access/ExtendedRoleSet.cpp @@ -51,25 +51,25 @@ ExtendedRoleSet::ExtendedRoleSet(const boost::container::flat_set & ids_) ExtendedRoleSet::ExtendedRoleSet(const ASTExtendedRoleSet & ast) { - init(ast, nullptr, nullptr); + init(ast, nullptr); } -ExtendedRoleSet::ExtendedRoleSet(const ASTExtendedRoleSet & ast, const UUID & current_user_id) +ExtendedRoleSet::ExtendedRoleSet(const ASTExtendedRoleSet & ast, const std::optional & current_user_id) { - init(ast, nullptr, ¤t_user_id); + init(ast, nullptr, current_user_id); } ExtendedRoleSet::ExtendedRoleSet(const ASTExtendedRoleSet & ast, const AccessControlManager & manager) { - init(ast, &manager, nullptr); + init(ast, &manager); } -ExtendedRoleSet::ExtendedRoleSet(const ASTExtendedRoleSet & ast, const AccessControlManager & manager, const UUID & current_user_id) +ExtendedRoleSet::ExtendedRoleSet(const ASTExtendedRoleSet & ast, const AccessControlManager & manager, const std::optional & current_user_id) { - init(ast, &manager, ¤t_user_id); + init(ast, &manager, current_user_id); } -void ExtendedRoleSet::init(const ASTExtendedRoleSet & ast, const AccessControlManager * manager, const UUID * current_user_id) +void ExtendedRoleSet::init(const ASTExtendedRoleSet & ast, const AccessControlManager * manager, const std::optional & current_user_id) { all = ast.all; diff --git a/src/Access/ExtendedRoleSet.h b/src/Access/ExtendedRoleSet.h index 61a4db6e0ae..486b4277337 100644 --- a/src/Access/ExtendedRoleSet.h +++ b/src/Access/ExtendedRoleSet.h @@ -32,9 +32,9 @@ struct ExtendedRoleSet /// The constructor from AST requires the AccessControlManager if `ast.id_mode == false`. ExtendedRoleSet(const ASTExtendedRoleSet & ast); - ExtendedRoleSet(const ASTExtendedRoleSet & ast, const UUID & current_user_id); + ExtendedRoleSet(const ASTExtendedRoleSet & ast, const std::optional & current_user_id); ExtendedRoleSet(const ASTExtendedRoleSet & ast, const AccessControlManager & manager); - ExtendedRoleSet(const ASTExtendedRoleSet & ast, const AccessControlManager & manager, const UUID & current_user_id); + ExtendedRoleSet(const ASTExtendedRoleSet & ast, const AccessControlManager & manager, const std::optional & current_user_id); std::shared_ptr toAST() const; String toString() const; @@ -69,7 +69,7 @@ struct ExtendedRoleSet boost::container::flat_set except_ids; private: - void init(const ASTExtendedRoleSet & ast, const AccessControlManager * manager = nullptr, const UUID * current_user_id = nullptr); + void init(const ASTExtendedRoleSet & ast, const AccessControlManager * manager = nullptr, const std::optional & current_user_id = {}); }; } diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index 4dc72948f8a..5e98a0267ca 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -665,12 +665,10 @@ String Context::getUserName() const return access->getUserName(); } -UUID Context::getUserID() const +std::optional Context::getUserID() const { auto lock = getLock(); - if (!user_id) - throw Exception("No current user", ErrorCodes::LOGICAL_ERROR); - return *user_id; + return user_id; } diff --git a/src/Interpreters/Context.h b/src/Interpreters/Context.h index e5b33e43614..b34a0e0c542 100644 --- a/src/Interpreters/Context.h +++ b/src/Interpreters/Context.h @@ -233,7 +233,7 @@ public: UserPtr getUser() const; String getUserName() const; - UUID getUserID() const; + std::optional getUserID() const; void setCurrentRoles(const std::vector & current_roles_); void setCurrentRolesDefault(); diff --git a/src/Interpreters/DDLWorker.cpp b/src/Interpreters/DDLWorker.cpp index eaee356264d..4a39cc6b8a1 100644 --- a/src/Interpreters/DDLWorker.cpp +++ b/src/Interpreters/DDLWorker.cpp @@ -1377,4 +1377,9 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & cont } +BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & context) +{ + return executeDDLQueryOnCluster(query_ptr_, context, {}); +} + } diff --git a/src/Interpreters/DDLWorker.h b/src/Interpreters/DDLWorker.h index 32b7cd5f172..62eba97032e 100644 --- a/src/Interpreters/DDLWorker.h +++ b/src/Interpreters/DDLWorker.h @@ -24,6 +24,7 @@ struct DDLTask; /// Pushes distributed DDL query to the queue BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, AccessRightsElements && query_required_access); +BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context); class DDLWorker diff --git a/src/Interpreters/InterpreterCreateQuotaQuery.cpp b/src/Interpreters/InterpreterCreateQuotaQuery.cpp index 4b64615dd36..13e772965ff 100644 --- a/src/Interpreters/InterpreterCreateQuotaQuery.cpp +++ b/src/Interpreters/InterpreterCreateQuotaQuery.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -76,10 +77,16 @@ void updateQuotaFromQueryImpl(Quota & quota, const ASTCreateQuotaQuery & query, BlockIO InterpreterCreateQuotaQuery::execute() { - const auto & query = query_ptr->as(); + auto & query = query_ptr->as(); auto & access_control = context.getAccessControlManager(); context.checkAccess(query.alter ? AccessType::ALTER_QUOTA : AccessType::CREATE_QUOTA); + if (!query.cluster.empty()) + { + query.replaceCurrentUserTagWithName(context.getUserName()); + return executeDDLQueryOnCluster(query_ptr, context); + } + std::optional roles_from_query; if (query.roles) roles_from_query = ExtendedRoleSet{*query.roles, access_control, context.getUserID()}; diff --git a/src/Interpreters/InterpreterCreateRoleQuery.cpp b/src/Interpreters/InterpreterCreateRoleQuery.cpp index f64462d443b..ed9135b2bb6 100644 --- a/src/Interpreters/InterpreterCreateRoleQuery.cpp +++ b/src/Interpreters/InterpreterCreateRoleQuery.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,9 @@ BlockIO InterpreterCreateRoleQuery::execute() else context.checkAccess(AccessType::CREATE_ROLE); + if (!query.cluster.empty()) + return executeDDLQueryOnCluster(query_ptr, context); + std::optional settings_from_query; if (query.settings) settings_from_query = SettingsProfileElements{*query.settings, access_control}; diff --git a/src/Interpreters/InterpreterCreateRowPolicyQuery.cpp b/src/Interpreters/InterpreterCreateRowPolicyQuery.cpp index 9f8cad51140..c3de3876c46 100644 --- a/src/Interpreters/InterpreterCreateRowPolicyQuery.cpp +++ b/src/Interpreters/InterpreterCreateRowPolicyQuery.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -63,10 +64,16 @@ namespace BlockIO InterpreterCreateRowPolicyQuery::execute() { - const auto & query = query_ptr->as(); + auto & query = query_ptr->as(); auto & access_control = context.getAccessControlManager(); context.checkAccess(query.alter ? AccessType::ALTER_ROW_POLICY : AccessType::CREATE_ROW_POLICY); + if (!query.cluster.empty()) + { + query.replaceCurrentUserTagWithName(context.getUserName()); + return executeDDLQueryOnCluster(query_ptr, context); + } + std::optional roles_from_query; if (query.roles) roles_from_query = ExtendedRoleSet{*query.roles, access_control, context.getUserID()}; diff --git a/src/Interpreters/InterpreterCreateSettingsProfileQuery.cpp b/src/Interpreters/InterpreterCreateSettingsProfileQuery.cpp index 9d110a69516..cb0b5587bdc 100644 --- a/src/Interpreters/InterpreterCreateSettingsProfileQuery.cpp +++ b/src/Interpreters/InterpreterCreateSettingsProfileQuery.cpp @@ -1,6 +1,8 @@ #include #include +#include #include +#include #include #include #include @@ -49,13 +51,19 @@ namespace BlockIO InterpreterCreateSettingsProfileQuery::execute() { - const auto & query = query_ptr->as(); + auto & query = query_ptr->as(); auto & access_control = context.getAccessControlManager(); if (query.alter) context.checkAccess(AccessType::ALTER_SETTINGS_PROFILE); else context.checkAccess(AccessType::CREATE_SETTINGS_PROFILE); + if (!query.cluster.empty()) + { + query.replaceCurrentUserTagWithName(context.getUserName()); + return executeDDLQueryOnCluster(query_ptr, context); + } + std::optional settings_from_query; if (query.settings) settings_from_query = SettingsProfileElements{*query.settings, access_control}; diff --git a/src/Interpreters/InterpreterCreateUserQuery.cpp b/src/Interpreters/InterpreterCreateUserQuery.cpp index 5dba1fefc9c..78c7cc222ae 100644 --- a/src/Interpreters/InterpreterCreateUserQuery.cpp +++ b/src/Interpreters/InterpreterCreateUserQuery.cpp @@ -1,10 +1,11 @@ #include #include #include +#include #include +#include #include #include -#include #include #include @@ -67,7 +68,7 @@ namespace BlockIO InterpreterCreateUserQuery::execute() { - const auto & query = query_ptr->as(); + auto & query = query_ptr->as(); auto & access_control = context.getAccessControlManager(); auto access = context.getAccess(); access->checkAccess(query.alter ? AccessType::ALTER_USER : AccessType::CREATE_USER); @@ -83,6 +84,9 @@ BlockIO InterpreterCreateUserQuery::execute() } } + if (!query.cluster.empty()) + return executeDDLQueryOnCluster(query_ptr, context); + std::optional settings_from_query; if (query.settings) settings_from_query = SettingsProfileElements{*query.settings, access_control}; diff --git a/src/Interpreters/InterpreterDropAccessEntityQuery.cpp b/src/Interpreters/InterpreterDropAccessEntityQuery.cpp index 191fa233097..e67e0659796 100644 --- a/src/Interpreters/InterpreterDropAccessEntityQuery.cpp +++ b/src/Interpreters/InterpreterDropAccessEntityQuery.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,9 @@ BlockIO InterpreterDropAccessEntityQuery::execute() std::type_index type = getType(query.kind); context.checkAccess(getRequiredAccessType(query.kind)); + if (!query.cluster.empty()) + return executeDDLQueryOnCluster(query_ptr, context); + if (query.kind == Kind::ROW_POLICY) { Strings full_names; diff --git a/src/Interpreters/InterpreterGrantQuery.cpp b/src/Interpreters/InterpreterGrantQuery.cpp index 5d215ff3a93..a5f13dbbbfe 100644 --- a/src/Interpreters/InterpreterGrantQuery.cpp +++ b/src/Interpreters/InterpreterGrantQuery.cpp @@ -1,6 +1,8 @@ #include #include +#include #include +#include #include #include #include @@ -59,7 +61,7 @@ namespace BlockIO InterpreterGrantQuery::execute() { - const auto & query = query_ptr->as(); + auto & query = query_ptr->as(); auto & access_control = context.getAccessControlManager(); auto access = context.getAccess(); access->checkGrantOption(query.access_rights_elements); @@ -72,6 +74,12 @@ BlockIO InterpreterGrantQuery::execute() access->checkAdminOption(role_from_query); } + if (!query.cluster.empty()) + { + query.replaceCurrentUserTagWithName(context.getUserName()); + return executeDDLQueryOnCluster(query_ptr, context); + } + std::vector to_roles = ExtendedRoleSet{*query.to_roles, access_control, context.getUserID()}.getMatchingIDs(access_control); String current_database = context.getCurrentDatabase(); diff --git a/src/Parsers/ASTCreateQuotaQuery.cpp b/src/Parsers/ASTCreateQuotaQuery.cpp index 7613fce6167..8fa0dbb0d31 100644 --- a/src/Parsers/ASTCreateQuotaQuery.cpp +++ b/src/Parsers/ASTCreateQuotaQuery.cpp @@ -135,6 +135,8 @@ void ASTCreateQuotaQuery::formatImpl(const FormatSettings & settings, FormatStat settings.ostr << " " << backQuoteIfNeed(name); + formatOnCluster(settings); + if (!new_name.empty()) formatRenameTo(new_name, settings); @@ -146,4 +148,12 @@ void ASTCreateQuotaQuery::formatImpl(const FormatSettings & settings, FormatStat if (roles && (!roles->empty() || alter)) formatToRoles(*roles, settings); } + + +void ASTCreateQuotaQuery::replaceCurrentUserTagWithName(const String & current_user_name) +{ + if (roles) + roles->replaceCurrentUserTagWithName(current_user_name); +} + } diff --git a/src/Parsers/ASTCreateQuotaQuery.h b/src/Parsers/ASTCreateQuotaQuery.h index 2968c2cc607..09ceaea9825 100644 --- a/src/Parsers/ASTCreateQuotaQuery.h +++ b/src/Parsers/ASTCreateQuotaQuery.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -25,7 +26,7 @@ class ASTExtendedRoleSet; * UNSET TRACKING} [,...]] * [TO {role [,...] | ALL | ALL EXCEPT role [,...]}] */ -class ASTCreateQuotaQuery : public IAST +class ASTCreateQuotaQuery : public IAST, public ASTQueryWithOnCluster { public: bool alter = false; @@ -58,5 +59,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); + ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster(clone()); } }; } diff --git a/src/Parsers/ASTCreateRoleQuery.cpp b/src/Parsers/ASTCreateRoleQuery.cpp index 3d69e4dac59..f3873f7a3eb 100644 --- a/src/Parsers/ASTCreateRoleQuery.cpp +++ b/src/Parsers/ASTCreateRoleQuery.cpp @@ -54,6 +54,8 @@ void ASTCreateRoleQuery::formatImpl(const FormatSettings & format, FormatState & format.ostr << " " << backQuoteIfNeed(name); + formatOnCluster(format); + if (!new_name.empty()) formatRenameTo(new_name, format); diff --git a/src/Parsers/ASTCreateRoleQuery.h b/src/Parsers/ASTCreateRoleQuery.h index 69bb9896fa3..ab306dd5dec 100644 --- a/src/Parsers/ASTCreateRoleQuery.h +++ b/src/Parsers/ASTCreateRoleQuery.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace DB @@ -15,7 +16,7 @@ class ASTSettingsProfileElements; * [RENAME TO new_name] * [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...] */ -class ASTCreateRoleQuery : public IAST +class ASTCreateRoleQuery : public IAST, public ASTQueryWithOnCluster { public: bool alter = false; @@ -33,5 +34,6 @@ public: String getID(char) const override; ASTPtr clone() const override; void formatImpl(const FormatSettings & format, FormatState &, FormatStateStacked) const override; + ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster(clone()); } }; } diff --git a/src/Parsers/ASTCreateRowPolicyQuery.cpp b/src/Parsers/ASTCreateRowPolicyQuery.cpp index 9102ec1da72..9b36f5500c1 100644 --- a/src/Parsers/ASTCreateRowPolicyQuery.cpp +++ b/src/Parsers/ASTCreateRowPolicyQuery.cpp @@ -157,6 +157,8 @@ void ASTCreateRowPolicyQuery::formatImpl(const FormatSettings & settings, Format settings.ostr << " " << backQuoteIfNeed(policy_name) << (settings.hilite ? hilite_keyword : "") << " ON " << (settings.hilite ? hilite_none : "") << (database.empty() ? String{} : backQuoteIfNeed(database) + ".") << table_name; + formatOnCluster(settings); + if (!new_policy_name.empty()) formatRenameTo(new_policy_name, settings); @@ -168,4 +170,11 @@ void ASTCreateRowPolicyQuery::formatImpl(const FormatSettings & settings, Format if (roles && (!roles->empty() || alter)) formatToRoles(*roles, settings); } + + +void ASTCreateRowPolicyQuery::replaceCurrentUserTagWithName(const String & current_user_name) +{ + if (roles) + roles->replaceCurrentUserTagWithName(current_user_name); +} } diff --git a/src/Parsers/ASTCreateRowPolicyQuery.h b/src/Parsers/ASTCreateRowPolicyQuery.h index e58ed0ec46c..85ba674eeb0 100644 --- a/src/Parsers/ASTCreateRowPolicyQuery.h +++ b/src/Parsers/ASTCreateRowPolicyQuery.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -25,7 +26,7 @@ class ASTExtendedRoleSet; * [WITH CHECK {condition | NONE}] [,...] * [TO {role [,...] | ALL | ALL EXCEPT role [,...]}] */ -class ASTCreateRowPolicyQuery : public IAST +class ASTCreateRowPolicyQuery : public IAST, public ASTQueryWithOnCluster { public: bool alter = false; @@ -47,5 +48,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); + ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster(clone()); } }; } diff --git a/src/Parsers/ASTCreateSettingsProfileQuery.cpp b/src/Parsers/ASTCreateSettingsProfileQuery.cpp index a5a5556baf3..8db82b0e1cb 100644 --- a/src/Parsers/ASTCreateSettingsProfileQuery.cpp +++ b/src/Parsers/ASTCreateSettingsProfileQuery.cpp @@ -61,6 +61,8 @@ void ASTCreateSettingsProfileQuery::formatImpl(const FormatSettings & format, Fo format.ostr << " " << backQuoteIfNeed(name); + formatOnCluster(format); + if (!new_name.empty()) formatRenameTo(new_name, format); @@ -71,4 +73,10 @@ void ASTCreateSettingsProfileQuery::formatImpl(const FormatSettings & format, Fo formatToRoles(*to_roles, format); } + +void ASTCreateSettingsProfileQuery::replaceCurrentUserTagWithName(const String & current_user_name) +{ + if (to_roles) + to_roles->replaceCurrentUserTagWithName(current_user_name); +} } diff --git a/src/Parsers/ASTCreateSettingsProfileQuery.h b/src/Parsers/ASTCreateSettingsProfileQuery.h index b3a60853e57..cc133397db4 100644 --- a/src/Parsers/ASTCreateSettingsProfileQuery.h +++ b/src/Parsers/ASTCreateSettingsProfileQuery.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace DB @@ -16,7 +17,7 @@ class ASTExtendedRoleSet; * [RENAME TO new_name] * [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...] */ -class ASTCreateSettingsProfileQuery : public IAST +class ASTCreateSettingsProfileQuery : public IAST, public ASTQueryWithOnCluster { public: bool alter = false; @@ -36,5 +37,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); + ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster(clone()); } }; } diff --git a/src/Parsers/ASTCreateUserQuery.cpp b/src/Parsers/ASTCreateUserQuery.cpp index c3cbc366d88..d901ed8f5a1 100644 --- a/src/Parsers/ASTCreateUserQuery.cpp +++ b/src/Parsers/ASTCreateUserQuery.cpp @@ -184,6 +184,8 @@ void ASTCreateUserQuery::formatImpl(const FormatSettings & format, FormatState & format.ostr << " " << backQuoteIfNeed(name); + formatOnCluster(format); + if (!new_name.empty()) formatRenameTo(new_name, format); diff --git a/src/Parsers/ASTCreateUserQuery.h b/src/Parsers/ASTCreateUserQuery.h index 643db2660af..5a5cc0d9550 100644 --- a/src/Parsers/ASTCreateUserQuery.h +++ b/src/Parsers/ASTCreateUserQuery.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -23,7 +24,7 @@ class ASTSettingsProfileElements; * [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 +class ASTCreateUserQuery : public IAST, public ASTQueryWithOnCluster { public: bool alter = false; @@ -49,5 +50,6 @@ public: String getID(char) const override; ASTPtr clone() const override; void formatImpl(const FormatSettings & format, FormatState &, FormatStateStacked) const override; + ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster(clone()); } }; } diff --git a/src/Parsers/ASTDropAccessEntityQuery.cpp b/src/Parsers/ASTDropAccessEntityQuery.cpp index 3896128ceb5..06a820bfbb5 100644 --- a/src/Parsers/ASTDropAccessEntityQuery.cpp +++ b/src/Parsers/ASTDropAccessEntityQuery.cpp @@ -75,5 +75,7 @@ void ASTDropAccessEntityQuery::formatImpl(const FormatSettings & settings, Forma settings.ostr << ' ' << backQuoteIfNeed(name); } } + + formatOnCluster(settings); } } diff --git a/src/Parsers/ASTDropAccessEntityQuery.h b/src/Parsers/ASTDropAccessEntityQuery.h index 5f0b46bd896..a3b358dcfb9 100644 --- a/src/Parsers/ASTDropAccessEntityQuery.h +++ b/src/Parsers/ASTDropAccessEntityQuery.h @@ -2,6 +2,7 @@ #include #include +#include namespace DB @@ -13,7 +14,7 @@ namespace DB * DROP [ROW] POLICY [IF EXISTS] name [,...] ON [database.]table [,...] * DROP [SETTINGS] PROFILE [IF EXISTS] name [,...] */ -class ASTDropAccessEntityQuery : public IAST +class ASTDropAccessEntityQuery : public IAST, public ASTQueryWithOnCluster { public: enum class Kind @@ -34,5 +35,6 @@ public: String getID(char) const override; ASTPtr clone() const override; void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override; + ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster(clone()); } }; } diff --git a/src/Parsers/ASTExtendedRoleSet.cpp b/src/Parsers/ASTExtendedRoleSet.cpp index 3ac1052897d..9eb06a6a101 100644 --- a/src/Parsers/ASTExtendedRoleSet.cpp +++ b/src/Parsers/ASTExtendedRoleSet.cpp @@ -72,4 +72,21 @@ void ASTExtendedRoleSet::formatImpl(const FormatSettings & settings, FormatState } } } + + +void ASTExtendedRoleSet::replaceCurrentUserTagWithName(const String & current_user_name) +{ + if (current_user) + { + names.push_back(current_user_name); + current_user = false; + } + + if (except_current_user) + { + except_names.push_back(current_user_name); + except_current_user = false; + } +} + } diff --git a/src/Parsers/ASTExtendedRoleSet.h b/src/Parsers/ASTExtendedRoleSet.h index 84190211087..8d619e5d6a0 100644 --- a/src/Parsers/ASTExtendedRoleSet.h +++ b/src/Parsers/ASTExtendedRoleSet.h @@ -18,6 +18,7 @@ public: bool id_mode = false; /// If true then `names` and `except_names` keeps UUIDs, not names. bool empty() const { return names.empty() && !current_user && !all; } + void replaceCurrentUserTagWithName(const String & current_user_name); String getID(char) const override { return "ExtendedRoleSet"; } ASTPtr clone() const override { return std::make_shared(*this); } diff --git a/src/Parsers/ASTGrantQuery.cpp b/src/Parsers/ASTGrantQuery.cpp index 94521d790f2..f91a5416011 100644 --- a/src/Parsers/ASTGrantQuery.cpp +++ b/src/Parsers/ASTGrantQuery.cpp @@ -122,19 +122,22 @@ 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.hilite ? IAST::hilite_none : "") << " "; + << (settings.hilite ? IAST::hilite_none : ""); + + formatOnCluster(settings); if (kind == Kind::REVOKE) { if (grant_option) - settings.ostr << (settings.hilite ? hilite_keyword : "") << "GRANT OPTION FOR " << (settings.hilite ? hilite_none : ""); + settings.ostr << (settings.hilite ? hilite_keyword : "") << " GRANT OPTION FOR" << (settings.hilite ? hilite_none : ""); else if (admin_option) - settings.ostr << (settings.hilite ? hilite_keyword : "") << "ADMIN OPTION FOR " << (settings.hilite ? hilite_none : ""); + settings.ostr << (settings.hilite ? hilite_keyword : "") << " ADMIN OPTION FOR" << (settings.hilite ? hilite_none : ""); } if ((!!roles + !access_rights_elements.empty()) != 1) throw Exception("Either roles or access rights elements should be set", ErrorCodes::LOGICAL_ERROR); + settings.ostr << " "; if (roles) roles->format(settings); else @@ -150,4 +153,11 @@ void ASTGrantQuery::formatImpl(const FormatSettings & settings, FormatState &, F settings.ostr << (settings.hilite ? hilite_keyword : "") << " WITH ADMIN OPTION" << (settings.hilite ? hilite_none : ""); } } + + +void ASTGrantQuery::replaceCurrentUserTagWithName(const String & current_user_name) +{ + if (to_roles) + to_roles->replaceCurrentUserTagWithName(current_user_name); +} } diff --git a/src/Parsers/ASTGrantQuery.h b/src/Parsers/ASTGrantQuery.h index 95b5f0b8448..e1ad8dc5dc5 100644 --- a/src/Parsers/ASTGrantQuery.h +++ b/src/Parsers/ASTGrantQuery.h @@ -2,6 +2,7 @@ #include #include +#include namespace DB @@ -15,7 +16,7 @@ class ASTExtendedRoleSet; * GRANT role [,...] TO {user_name | role_name | CURRENT_USER} [,...] [WITH ADMIN OPTION] * REVOKE [ADMIN OPTION FOR] role [,...] FROM {user_name | role_name | CURRENT_USER} [,...] | ALL | ALL EXCEPT {user_name | role_name | CURRENT_USER} [,...] */ -class ASTGrantQuery : public IAST +class ASTGrantQuery : public IAST, public ASTQueryWithOnCluster { public: enum class Kind @@ -34,5 +35,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); + ASTPtr getRewrittenASTWithoutOnCluster(const std::string &) const override { return removeOnCluster(clone()); } }; } diff --git a/src/Parsers/ParserCreateQuotaQuery.cpp b/src/Parsers/ParserCreateQuotaQuery.cpp index 9a6afec6941..66e72ee4968 100644 --- a/src/Parsers/ParserCreateQuotaQuery.cpp +++ b/src/Parsers/ParserCreateQuotaQuery.cpp @@ -238,6 +238,13 @@ bool ParserCreateQuotaQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expe if (!parseIdentifierOrStringLiteral(pos, expected, name)) return false; + String cluster; + if (ParserKeyword{"ON"}.ignore(pos, expected)) + { + if (!ASTQueryWithOnCluster::parse(pos, cluster, expected)) + return false; + } + String new_name; std::optional key_type; std::vector all_limits; @@ -266,6 +273,7 @@ bool ParserCreateQuotaQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expe query->if_exists = if_exists; query->if_not_exists = if_not_exists; query->or_replace = or_replace; + query->cluster = std::move(cluster); query->name = std::move(name); query->new_name = std::move(new_name); query->key_type = key_type; diff --git a/src/Parsers/ParserCreateRoleQuery.cpp b/src/Parsers/ParserCreateRoleQuery.cpp index e2b42c976b4..05143108480 100644 --- a/src/Parsers/ParserCreateRoleQuery.cpp +++ b/src/Parsers/ParserCreateRoleQuery.cpp @@ -80,6 +80,13 @@ bool ParserCreateRoleQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec if (!parseRoleName(pos, expected, name)) return false; + String cluster; + if (ParserKeyword{"ON"}.ignore(pos, expected)) + { + if (!ASTQueryWithOnCluster::parse(pos, cluster, expected)) + return false; + } + String new_name; std::shared_ptr settings; while (true) @@ -101,6 +108,7 @@ bool ParserCreateRoleQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec query->if_exists = if_exists; query->if_not_exists = if_not_exists; query->or_replace = or_replace; + query->cluster = std::move(cluster); query->name = std::move(name); query->new_name = std::move(new_name); query->settings = std::move(settings); diff --git a/src/Parsers/ParserCreateRowPolicyQuery.cpp b/src/Parsers/ParserCreateRowPolicyQuery.cpp index ab0fbc87e12..8bfe54b87b2 100644 --- a/src/Parsers/ParserCreateRowPolicyQuery.cpp +++ b/src/Parsers/ParserCreateRowPolicyQuery.cpp @@ -243,6 +243,13 @@ bool ParserCreateRowPolicyQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & || !parseDatabaseAndTableName(pos, expected, database, table_name)) return false; + String cluster; + if (ParserKeyword{"ON"}.ignore(pos, expected)) + { + if (!ASTQueryWithOnCluster::parse(pos, cluster, expected)) + return false; + } + String new_policy_name; std::optional is_restrictive; std::vector> conditions; @@ -272,6 +279,7 @@ bool ParserCreateRowPolicyQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & query->if_exists = if_exists; query->if_not_exists = if_not_exists; query->or_replace = or_replace; + query->cluster = std::move(cluster); query->name_parts = std::move(name_parts); query->new_policy_name = std::move(new_policy_name); query->is_restrictive = is_restrictive; diff --git a/src/Parsers/ParserCreateSettingsProfileQuery.cpp b/src/Parsers/ParserCreateSettingsProfileQuery.cpp index c7c9e064f6c..4d3ed2f6e63 100644 --- a/src/Parsers/ParserCreateSettingsProfileQuery.cpp +++ b/src/Parsers/ParserCreateSettingsProfileQuery.cpp @@ -96,6 +96,13 @@ bool ParserCreateSettingsProfileQuery::parseImpl(Pos & pos, ASTPtr & node, Expec if (!parseIdentifierOrStringLiteral(pos, expected, name)) return false; + String cluster; + if (ParserKeyword{"ON"}.ignore(pos, expected)) + { + if (!ASTQueryWithOnCluster::parse(pos, cluster, expected)) + return false; + } + String new_name; std::shared_ptr settings; while (true) @@ -120,6 +127,7 @@ bool ParserCreateSettingsProfileQuery::parseImpl(Pos & pos, ASTPtr & node, Expec query->if_exists = if_exists; query->if_not_exists = if_not_exists; query->or_replace = or_replace; + query->cluster = std::move(cluster); query->name = std::move(name); query->new_name = std::move(new_name); query->settings = std::move(settings); diff --git a/src/Parsers/ParserCreateUserQuery.cpp b/src/Parsers/ParserCreateUserQuery.cpp index 7564c02da45..3968c26d42e 100644 --- a/src/Parsers/ParserCreateUserQuery.cpp +++ b/src/Parsers/ParserCreateUserQuery.cpp @@ -290,6 +290,13 @@ bool ParserCreateUserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec if (!parseUserName(pos, expected, name, host_pattern)) return false; + String cluster; + if (ParserKeyword{"ON"}.ignore(pos, expected)) + { + if (!ASTQueryWithOnCluster::parse(pos, cluster, expected)) + return false; + } + String new_name; std::optional new_host_pattern; std::optional authentication; @@ -341,6 +348,7 @@ bool ParserCreateUserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec query->if_exists = if_exists; query->if_not_exists = if_not_exists; query->or_replace = or_replace; + query->cluster = std::move(cluster); query->name = std::move(name); query->new_name = std::move(new_name); query->authentication = std::move(authentication); diff --git a/src/Parsers/ParserDropAccessEntityQuery.cpp b/src/Parsers/ParserDropAccessEntityQuery.cpp index 23e18d7d32c..ecda1691240 100644 --- a/src/Parsers/ParserDropAccessEntityQuery.cpp +++ b/src/Parsers/ParserDropAccessEntityQuery.cpp @@ -117,10 +117,18 @@ bool ParserDropAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & return false; } + String cluster; + if (ParserKeyword{"ON"}.ignore(pos, expected)) + { + if (!ASTQueryWithOnCluster::parse(pos, cluster, expected)) + return false; + } + auto query = std::make_shared(kind); node = query; query->if_exists = if_exists; + query->cluster = std::move(cluster); query->names = std::move(names); query->row_policies_names = std::move(row_policies_names); diff --git a/src/Parsers/ParserGrantQuery.cpp b/src/Parsers/ParserGrantQuery.cpp index aaf3dca6d78..f6eecbe5dba 100644 --- a/src/Parsers/ParserGrantQuery.cpp +++ b/src/Parsers/ParserGrantQuery.cpp @@ -259,6 +259,13 @@ bool ParserGrantQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) else return false; + String cluster; + if (ParserKeyword{"ON"}.ignore(pos, expected)) + { + if (!ASTQueryWithOnCluster::parse(pos, cluster, expected)) + return false; + } + bool grant_option = false; bool admin_option = false; if (kind == Kind::REVOKE) @@ -296,6 +303,7 @@ bool ParserGrantQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) query->kind = kind; query->attach = attach; + query->cluster = std::move(cluster); query->access_rights_elements = std::move(elements); query->roles = std::move(roles); query->to_roles = std::move(to_roles); diff --git a/tests/integration/test_access_control_on_cluster/__init__.py b/tests/integration/test_access_control_on_cluster/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/integration/test_access_control_on_cluster/configs/config.d/clusters.xml b/tests/integration/test_access_control_on_cluster/configs/config.d/clusters.xml new file mode 100644 index 00000000000..741f862d162 --- /dev/null +++ b/tests/integration/test_access_control_on_cluster/configs/config.d/clusters.xml @@ -0,0 +1,22 @@ + + + + + + ch1 + 9000 + + + ch2 + 9000 + + + + + ch3 + 9000 + + + + + diff --git a/tests/integration/test_access_control_on_cluster/configs/users.d/access_management.xml b/tests/integration/test_access_control_on_cluster/configs/users.d/access_management.xml new file mode 100644 index 00000000000..7e799cb7b10 --- /dev/null +++ b/tests/integration/test_access_control_on_cluster/configs/users.d/access_management.xml @@ -0,0 +1,7 @@ + + + + 1 + + + diff --git a/tests/integration/test_access_control_on_cluster/test.py b/tests/integration/test_access_control_on_cluster/test.py new file mode 100644 index 00000000000..6ca4ac15398 --- /dev/null +++ b/tests/integration/test_access_control_on_cluster/test.py @@ -0,0 +1,41 @@ +import time +import pytest +from helpers.cluster import ClickHouseCluster +from helpers.client import QueryRuntimeException + +cluster = ClickHouseCluster(__file__) +ch1 = cluster.add_instance('ch1', config_dir="configs", with_zookeeper=True) +ch2 = cluster.add_instance('ch2', config_dir="configs", with_zookeeper=True) +ch3 = cluster.add_instance('ch3', config_dir="configs", with_zookeeper=True) + +@pytest.fixture(scope="module", autouse=True) +def started_cluster(): + try: + cluster.start() + yield cluster + + finally: + cluster.shutdown() + + +def test_access_control_on_cluster(): + ch1.query("CREATE USER Alex ON CLUSTER 'cluster'") + assert ch1.query("SHOW CREATE USER Alex") == "CREATE USER Alex\n" + assert ch2.query("SHOW CREATE USER Alex") == "CREATE USER Alex\n" + assert ch3.query("SHOW CREATE USER Alex") == "CREATE USER Alex\n" + + ch2.query("GRANT ON CLUSTER 'cluster' SELECT ON *.* TO Alex") + assert ch1.query("SHOW GRANTS FOR Alex") == "GRANT SELECT ON *.* TO Alex\n" + assert ch2.query("SHOW GRANTS FOR Alex") == "GRANT SELECT ON *.* TO Alex\n" + assert ch3.query("SHOW GRANTS FOR Alex") == "GRANT SELECT ON *.* TO Alex\n" + + ch3.query("REVOKE ON CLUSTER 'cluster' SELECT ON *.* FROM Alex") + assert ch1.query("SHOW GRANTS FOR Alex") == "" + assert ch2.query("SHOW GRANTS FOR Alex") == "" + assert ch3.query("SHOW GRANTS FOR Alex") == "" + + ch2.query("DROP USER Alex ON CLUSTER 'cluster'") + assert "User `Alex` not found" in ch1.query_and_get_error("SHOW CREATE USER Alex") + assert "User `Alex` not found" in ch2.query_and_get_error("SHOW CREATE USER Alex") + assert "User `Alex` not found" in ch3.query_and_get_error("SHOW CREATE USER Alex") +