mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-24 16:42:05 +00:00
Merge pull request #6907 from PerformanceVision/dict_allow_databases
Whitelist for dictionary from the current connected database
This commit is contained in:
commit
fae612c3e7
@ -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;
|
||||
|
@ -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<FunctionDictHas>(context.getExternalDictionaries());
|
||||
return std::make_shared<FunctionDictHas>(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<String>());
|
||||
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<FlatDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchSimple<HashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchSimple<CacheDictionary>(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<FunctionDictGetString>(context.getExternalDictionaries());
|
||||
return std::make_shared<FunctionDictGetString>(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<String>());
|
||||
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<FlatDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<CacheDictionary>(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<FunctionDictGetStringOrDefault>(context.getExternalDictionaries());
|
||||
return std::make_shared<FunctionDictGetStringOrDefault>(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<String>());
|
||||
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<FlatDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<CacheDictionary>(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<FunctionDictGet>(context.getExternalDictionaries(), dec_scale);
|
||||
return std::make_shared<FunctionDictGet>(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<String>());
|
||||
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<FlatDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<CacheDictionary>(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<FunctionDictGetOrDefault>(context.getExternalDictionaries(), dec_scale);
|
||||
return std::make_shared<FunctionDictGetOrDefault>(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<String>());
|
||||
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<FlatDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<CacheDictionary>(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<FunctionDictGetHierarchy>(context.getExternalDictionaries());
|
||||
return std::make_shared<FunctionDictGetHierarchy>(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<String>());
|
||||
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<FlatDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<CacheDictionary>(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<FunctionDictIsIn>(context.getExternalDictionaries());
|
||||
return std::make_shared<FunctionDictIsIn>(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<String>());
|
||||
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<FlatDictionary>(block, arguments, result, dict_ptr)
|
||||
&& !executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr)
|
||||
&& !executeDispatch<CacheDictionary>(block, arguments, result, dict_ptr))
|
||||
@ -1889,6 +1950,7 @@ private:
|
||||
}
|
||||
|
||||
const ExternalDictionaries & dictionaries;
|
||||
const Context & context;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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"))
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -67,6 +67,10 @@ struct User
|
||||
using DatabaseSet = std::unordered_set<std::string>;
|
||||
DatabaseSet databases;
|
||||
|
||||
/// List of allowed dictionaries.
|
||||
using DictionarySet = std::unordered_set<std::string>;
|
||||
DictionarySet dictionaries;
|
||||
|
||||
/// Table properties.
|
||||
using PropertyMap = std::unordered_map<std::string /* name */, std::string /* value */>;
|
||||
using TableMap = std::unordered_map<std::string /* table */, PropertyMap /* properties */>;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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<String, UserPtr>;
|
||||
|
@ -56,6 +56,9 @@ Users are recorded in the `users` section. Here is a fragment of the `users.xml`
|
||||
<allow_databases>
|
||||
<database>test</database>
|
||||
</allow_databases>
|
||||
<allow_dictionaries>
|
||||
<dictionary>test</dictionary>
|
||||
</allow_dictionaries>
|
||||
</web>
|
||||
</users>
|
||||
```
|
||||
@ -93,6 +96,8 @@ Then specify the quota to be used (see the section "[Quotas](quotas.md#quotas)")
|
||||
|
||||
In the optional `<allow_databases>` 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 `<allow_dictionaries>` 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.
|
||||
|
Loading…
Reference in New Issue
Block a user