diff --git a/dbms/src/Common/ErrorCodes.cpp b/dbms/src/Common/ErrorCodes.cpp index fa42cde3538..e2ee9720d68 100644 --- a/dbms/src/Common/ErrorCodes.cpp +++ b/dbms/src/Common/ErrorCodes.cpp @@ -456,7 +456,7 @@ namespace ErrorCodes extern const int UNKNOWN_DISK = 479; extern const int UNKNOWN_PROTOCOL = 480; extern const int PATH_ACCESS_DENIED = 481; - + extern const int DICTIONARY_ACCESS_DENIED = 482; extern const int KEEPER_EXCEPTION = 999; extern const int POCO_EXCEPTION = 1000; diff --git a/dbms/src/Functions/FunctionsExternalDictionaries.h b/dbms/src/Functions/FunctionsExternalDictionaries.h index 1ae0c6d3198..231a4be12b2 100644 --- a/dbms/src/Functions/FunctionsExternalDictionaries.h +++ b/dbms/src/Functions/FunctionsExternalDictionaries.h @@ -48,6 +48,7 @@ namespace ErrorCodes extern const int TYPE_MISMATCH; extern const int ILLEGAL_COLUMN; extern const int BAD_ARGUMENTS; + extern const int DICTIONARY_ACCESS_DENIED; } /** Functions that use plug-ins (external) dictionaries. @@ -72,10 +73,12 @@ public: static FunctionPtr create(const Context & context) { - return std::make_shared(context.getExternalDictionaries()); + return std::make_shared(context.getExternalDictionaries(), context); } - FunctionDictHas(const ExternalDictionaries & dictionaries_) : dictionaries(dictionaries_) {} + FunctionDictHas(const ExternalDictionaries & dictionaries_, const Context & context_) + : dictionaries(dictionaries_) + , context(context_) {} String getName() const override { return name; } @@ -124,6 +127,12 @@ private: auto dict = dictionaries.getDictionary(dict_name_col->getValue()); const auto dict_ptr = dict.get(); + if (!context.hasDictionaryAccessRights(dict_ptr->getName())) + { + throw Exception{"For function " + getName() + ", cannot access dictionary " + + dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED}; + } + if (!executeDispatchSimple(block, arguments, result, dict_ptr) && !executeDispatchSimple(block, arguments, result, dict_ptr) && !executeDispatchSimple(block, arguments, result, dict_ptr) && @@ -183,6 +192,7 @@ private: } const ExternalDictionaries & dictionaries; + const Context & context; }; @@ -217,10 +227,12 @@ public: static FunctionPtr create(const Context & context) { - return std::make_shared(context.getExternalDictionaries()); + return std::make_shared(context.getExternalDictionaries(), context); } - FunctionDictGetString(const ExternalDictionaries & dictionaries_) : dictionaries(dictionaries_) {} + FunctionDictGetString(const ExternalDictionaries & dictionaries_, const Context & context_) + : dictionaries(dictionaries_) + , context(context_) {} String getName() const override { return name; } @@ -290,6 +302,12 @@ private: auto dict = dictionaries.getDictionary(dict_name_col->getValue()); const auto dict_ptr = dict.get(); + if (!context.hasDictionaryAccessRights(dict_ptr->getName())) + { + throw Exception{"For function " + getName() + ", cannot access dictionary " + + dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED}; + } + if (!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && @@ -402,6 +420,7 @@ private: } const ExternalDictionaries & dictionaries; + const Context & context; }; @@ -412,10 +431,12 @@ public: static FunctionPtr create(const Context & context) { - return std::make_shared(context.getExternalDictionaries()); + return std::make_shared(context.getExternalDictionaries(), context); } - FunctionDictGetStringOrDefault(const ExternalDictionaries & dictionaries_) : dictionaries(dictionaries_) {} + FunctionDictGetStringOrDefault(const ExternalDictionaries & dictionaries_, const Context & context_) + : dictionaries(dictionaries_) + , context(context_) {} String getName() const override { return name; } @@ -467,6 +488,12 @@ private: auto dict = dictionaries.getDictionary(dict_name_col->getValue()); const auto dict_ptr = dict.get(); + if (!context.hasDictionaryAccessRights(dict_ptr->getName())) + { + throw Exception{"For function " + getName() + ", cannot access dictionary " + + dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED}; + } + if (!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && @@ -605,6 +632,7 @@ private: } const ExternalDictionaries & dictionaries; + const Context & context; }; @@ -727,11 +755,12 @@ public: static FunctionPtr create(const Context & context, UInt32 dec_scale = 0) { - return std::make_shared(context.getExternalDictionaries(), dec_scale); + return std::make_shared(context.getExternalDictionaries(), context, dec_scale); } - FunctionDictGet(const ExternalDictionaries & dictionaries_, UInt32 dec_scale = 0) + FunctionDictGet(const ExternalDictionaries & dictionaries_, const Context & context_, UInt32 dec_scale = 0) : dictionaries(dictionaries_) + , context(context_) , decimal_scale(dec_scale) {} @@ -801,6 +830,12 @@ private: auto dict = dictionaries.getDictionary(dict_name_col->getValue()); const auto dict_ptr = dict.get(); + if (!context.hasDictionaryAccessRights(dict_ptr->getName())) + { + throw Exception{"For function " + getName() + ", cannot access dictionary " + + dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED}; + } + if (!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && @@ -949,6 +984,7 @@ private: } const ExternalDictionaries & dictionaries; + const Context & context; UInt32 decimal_scale; }; @@ -998,11 +1034,12 @@ public: static FunctionPtr create(const Context & context, UInt32 dec_scale = 0) { - return std::make_shared(context.getExternalDictionaries(), dec_scale); + return std::make_shared(context.getExternalDictionaries(), context, dec_scale); } - FunctionDictGetOrDefault(const ExternalDictionaries & dictionaries_, UInt32 dec_scale = 0) + FunctionDictGetOrDefault(const ExternalDictionaries & dictionaries_, const Context & context_, UInt32 dec_scale = 0) : dictionaries(dictionaries_) + , context(context_) , decimal_scale(dec_scale) {} @@ -1057,6 +1094,12 @@ private: auto dict = dictionaries.getDictionary(dict_name_col->getValue()); const auto dict_ptr = dict.get(); + if (!context.hasDictionaryAccessRights(dict_ptr->getName())) + { + throw Exception{"For function " + getName() + ", cannot access dictionary " + + dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED}; + } + if (!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && @@ -1242,6 +1285,7 @@ private: } const ExternalDictionaries & dictionaries; + const Context & context; UInt32 decimal_scale; }; @@ -1580,10 +1624,12 @@ public: static FunctionPtr create(const Context & context) { - return std::make_shared(context.getExternalDictionaries()); + return std::make_shared(context.getExternalDictionaries(), context); } - FunctionDictGetHierarchy(const ExternalDictionaries & dictionaries_) : dictionaries(dictionaries_) {} + FunctionDictGetHierarchy(const ExternalDictionaries & dictionaries_, const Context & context_) + : dictionaries(dictionaries_) + , context(context_) {} String getName() const override { return name; } @@ -1625,6 +1671,12 @@ private: auto dict = dictionaries.getDictionary(dict_name_col->getValue()); const auto dict_ptr = dict.get(); + if (!context.hasDictionaryAccessRights(dict_ptr->getName())) + { + throw Exception{"For function " + getName() + ", cannot access dictionary " + + dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED}; + } + if (!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr)) @@ -1727,6 +1779,7 @@ private: } const ExternalDictionaries & dictionaries; + const Context & context; }; @@ -1737,10 +1790,12 @@ public: static FunctionPtr create(const Context & context) { - return std::make_shared(context.getExternalDictionaries()); + return std::make_shared(context.getExternalDictionaries(), context); } - FunctionDictIsIn(const ExternalDictionaries & dictionaries_) : dictionaries(dictionaries_) {} + FunctionDictIsIn(const ExternalDictionaries & dictionaries_, const Context & context_) + : dictionaries(dictionaries_) + , context(context_) {} String getName() const override { return name; } @@ -1785,6 +1840,12 @@ private: auto dict = dictionaries.getDictionary(dict_name_col->getValue()); const auto dict_ptr = dict.get(); + if (!context.hasDictionaryAccessRights(dict_ptr->getName())) + { + throw Exception{"For function " + getName() + ", cannot access dictionary " + + dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED}; + } + if (!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr)) @@ -1889,6 +1950,7 @@ private: } const ExternalDictionaries & dictionaries; + const Context & context; }; diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index d34128cc2b0..086d060c171 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -709,6 +709,13 @@ bool Context::hasDatabaseAccessRights(const String & database_name) const shared->users_manager->hasAccessToDatabase(client_info.current_user, database_name); } +bool Context::hasDictionaryAccessRights(const String & dictionary_name) const +{ + auto lock = getLock(); + return client_info.current_user.empty() || + shared->users_manager->hasAccessToDictionary(client_info.current_user, dictionary_name); +} + void Context::checkDatabaseAccessRightsImpl(const std::string & database_name) const { if (client_info.current_user.empty() || (database_name == "system")) diff --git a/dbms/src/Interpreters/Context.h b/dbms/src/Interpreters/Context.h index bc96e3b0548..9c001916347 100644 --- a/dbms/src/Interpreters/Context.h +++ b/dbms/src/Interpreters/Context.h @@ -254,6 +254,8 @@ public: bool hasDatabaseAccessRights(const String & database_name) const; void assertTableExists(const String & database_name, const String & table_name) const; + bool hasDictionaryAccessRights(const String & dictionary_name) const; + /** The parameter check_database_access_rights exists to not check the permissions of the database again, * when assertTableDoesntExist or assertDatabaseExists is called inside another function that already * made this check. diff --git a/dbms/src/Interpreters/IUsersManager.h b/dbms/src/Interpreters/IUsersManager.h index 340b36a4ad4..5cfaa413975 100644 --- a/dbms/src/Interpreters/IUsersManager.h +++ b/dbms/src/Interpreters/IUsersManager.h @@ -30,6 +30,9 @@ public: /// Check if the user has access to the database. virtual bool hasAccessToDatabase(const String & user_name, const String & database_name) const = 0; + + // Check if the user has access to the dictionary + virtual bool hasAccessToDictionary(const String & user_name, const String & dictionary_name) const = 0; }; } diff --git a/dbms/src/Interpreters/Users.cpp b/dbms/src/Interpreters/Users.cpp index 2dff8c32a69..86e37f0b729 100644 --- a/dbms/src/Interpreters/Users.cpp +++ b/dbms/src/Interpreters/Users.cpp @@ -326,6 +326,21 @@ User::User(const String & name_, const String & config_elem, const Poco::Util::A } } + /// Fill list of allowed dictionaries. + const auto config_dictionary_sub_elem = config_elem + ".allow_dictionaries"; + if (config.has(config_dictionary_sub_elem)) + { + Poco::Util::AbstractConfiguration::Keys config_keys; + config.keys(config_dictionary_sub_elem, config_keys); + + dictionaries.reserve(config_keys.size()); + for (const auto & key : config_keys) + { + const auto dictionary_name = config.getString(config_dictionary_sub_elem + "." + key); + dictionaries.insert(dictionary_name); + } + } + /// Read properties per "database.table" /// Only tables are expected to have properties, so that all the keys inside "database" are table names. const auto config_databases = config_elem + ".databases"; diff --git a/dbms/src/Interpreters/Users.h b/dbms/src/Interpreters/Users.h index 8baeec57e50..090bc693e9a 100644 --- a/dbms/src/Interpreters/Users.h +++ b/dbms/src/Interpreters/Users.h @@ -67,6 +67,10 @@ struct User using DatabaseSet = std::unordered_set; DatabaseSet databases; + /// List of allowed dictionaries. + using DictionarySet = std::unordered_set; + DictionarySet dictionaries; + /// Table properties. using PropertyMap = std::unordered_map; using TableMap = std::unordered_map; diff --git a/dbms/src/Interpreters/UsersManager.cpp b/dbms/src/Interpreters/UsersManager.cpp index 123d4bd8704..ee6293c3ee2 100644 --- a/dbms/src/Interpreters/UsersManager.cpp +++ b/dbms/src/Interpreters/UsersManager.cpp @@ -138,4 +138,14 @@ bool UsersManager::hasAccessToDatabase(const std::string & user_name, const std: return user->databases.empty() || user->databases.count(database_name); } +bool UsersManager::hasAccessToDictionary(const std::string & user_name, const std::string & dictionary_name) const +{ + auto it = users.find(user_name); + + if (users.end() == it) + throw Exception("Unknown user " + user_name, ErrorCodes::UNKNOWN_USER); + + auto user = it->second; + return user->dictionaries.empty() || user->dictionaries.count(dictionary_name); +} } diff --git a/dbms/src/Interpreters/UsersManager.h b/dbms/src/Interpreters/UsersManager.h index 771372e7c9e..94d35a8231e 100644 --- a/dbms/src/Interpreters/UsersManager.h +++ b/dbms/src/Interpreters/UsersManager.h @@ -23,6 +23,7 @@ public: UserPtr getUser(const String & user_name) const override; bool hasAccessToDatabase(const String & user_name, const String & database_name) const override; + bool hasAccessToDictionary(const String & user_name, const String & dictionary_name) const override; private: using Container = std::map; diff --git a/docs/en/operations/access_rights.md b/docs/en/operations/access_rights.md index 0984eea43ae..9168cdf333b 100644 --- a/docs/en/operations/access_rights.md +++ b/docs/en/operations/access_rights.md @@ -56,6 +56,9 @@ Users are recorded in the `users` section. Here is a fragment of the `users.xml` test + + test + ``` @@ -93,6 +96,8 @@ Then specify the quota to be used (see the section "[Quotas](quotas.md#quotas)") In the optional `` section, you can also specify a list of databases that the user can access. By default, all databases are available to the user. You can specify the `default` database. In this case, the user will receive access to the database by default. +In the optional `` section, you can also specify a list of dictionaries that the user can access. By default, all dictionaries are available to the user. + Access to the `system` database is always allowed (since this database is used for processing queries). The user can get a list of all databases and tables in them by using `SHOW` queries or system tables, even if access to individual databases isn't allowed.