Merge pull request #6907 from PerformanceVision/dict_allow_databases

Whitelist for dictionary from the current connected database
This commit is contained in:
Vitaly Baranov 2019-09-16 11:52:25 +03:00 committed by GitHub
commit fae612c3e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 124 additions and 15 deletions

View File

@ -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;

View File

@ -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;
};

View File

@ -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"))

View File

@ -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.

View File

@ -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;
};
}

View File

@ -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";

View File

@ -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 */>;

View File

@ -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);
}
}

View File

@ -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>;

View File

@ -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.