Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Whitelist for dictionary from the current connected database #6907

Merged
merged 9 commits into from
Sep 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion dbms/src/Common/ErrorCodes.cpp
Expand Up @@ -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;
Expand Down
90 changes: 76 additions & 14 deletions dbms/src/Functions/FunctionsExternalDictionaries.h
Expand Up @@ -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.
Expand All @@ -72,10 +73,12 @@ class FunctionDictHas final : public IFunction

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

Expand Down Expand Up @@ -124,6 +127,12 @@ class FunctionDictHas final : public IFunction
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) &&
Expand Down Expand Up @@ -183,6 +192,7 @@ class FunctionDictHas final : public IFunction
}

const ExternalDictionaries & dictionaries;
const Context & context;
};


Expand Down Expand Up @@ -217,10 +227,12 @@ class FunctionDictGetString final : public IFunction

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

Expand Down Expand Up @@ -290,6 +302,12 @@ class FunctionDictGetString final : public IFunction
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) &&
Expand Down Expand Up @@ -402,6 +420,7 @@ class FunctionDictGetString final : public IFunction
}

const ExternalDictionaries & dictionaries;
const Context & context;
};


Expand All @@ -412,10 +431,12 @@ class FunctionDictGetStringOrDefault final : public IFunction

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

Expand Down Expand Up @@ -467,6 +488,12 @@ class FunctionDictGetStringOrDefault final : public IFunction
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) &&
Expand Down Expand Up @@ -605,6 +632,7 @@ class FunctionDictGetStringOrDefault final : public IFunction
}

const ExternalDictionaries & dictionaries;
const Context & context;
};


Expand Down Expand Up @@ -727,11 +755,12 @@ class FunctionDictGet final : public IFunction

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)
{}

Expand Down Expand Up @@ -801,6 +830,12 @@ class FunctionDictGet final : public IFunction
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) &&
Expand Down Expand Up @@ -949,6 +984,7 @@ class FunctionDictGet final : public IFunction
}

const ExternalDictionaries & dictionaries;
const Context & context;
UInt32 decimal_scale;
};

Expand Down Expand Up @@ -998,11 +1034,12 @@ class FunctionDictGetOrDefault final : public IFunction

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)
{}

Expand Down Expand Up @@ -1057,6 +1094,12 @@ class FunctionDictGetOrDefault final : public IFunction
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) &&
Expand Down Expand Up @@ -1242,6 +1285,7 @@ class FunctionDictGetOrDefault final : public IFunction
}

const ExternalDictionaries & dictionaries;
const Context & context;
UInt32 decimal_scale;
};

Expand Down Expand Up @@ -1580,10 +1624,12 @@ class FunctionDictGetHierarchy final : public IFunction

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

Expand Down Expand Up @@ -1625,6 +1671,12 @@ class FunctionDictGetHierarchy final : public IFunction
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))
Expand Down Expand Up @@ -1727,6 +1779,7 @@ class FunctionDictGetHierarchy final : public IFunction
}

const ExternalDictionaries & dictionaries;
const Context & context;
};


Expand All @@ -1737,10 +1790,12 @@ class FunctionDictIsIn final : public IFunction

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

Expand Down Expand Up @@ -1785,6 +1840,12 @@ class FunctionDictIsIn final : public IFunction
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))
Expand Down Expand Up @@ -1889,6 +1950,7 @@ class FunctionDictIsIn final : public IFunction
}

const ExternalDictionaries & dictionaries;
const Context & context;
};


Expand Down
7 changes: 7 additions & 0 deletions dbms/src/Interpreters/Context.cpp
Expand Up @@ -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"))
Expand Down
2 changes: 2 additions & 0 deletions dbms/src/Interpreters/Context.h
Expand Up @@ -254,6 +254,8 @@ class Context
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.
Expand Down
3 changes: 3 additions & 0 deletions dbms/src/Interpreters/IUsersManager.h
Expand Up @@ -30,6 +30,9 @@ class IUsersManager

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

}
15 changes: 15 additions & 0 deletions dbms/src/Interpreters/Users.cpp
Expand Up @@ -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";
Expand Down
4 changes: 4 additions & 0 deletions dbms/src/Interpreters/Users.h
Expand Up @@ -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 */>;
Expand Down
10 changes: 10 additions & 0 deletions dbms/src/Interpreters/UsersManager.cpp
Expand Up @@ -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);
}
}
1 change: 1 addition & 0 deletions dbms/src/Interpreters/UsersManager.h
Expand Up @@ -23,6 +23,7 @@ class UsersManager : public IUsersManager
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>;
Expand Down
5 changes: 5 additions & 0 deletions docs/en/operations/access_rights.md
Expand Up @@ -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>
```
Expand Down Expand Up @@ -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.
Expand Down