Check access rights via new interface.

This commit is contained in:
Vitaly Baranov 2020-01-24 19:20:36 +03:00
parent 9e910222da
commit 2aa58193c8
73 changed files with 1333 additions and 383 deletions

View File

@ -74,7 +74,7 @@ public:
static AccessFlags columnLevel();
private:
static constexpr size_t NUM_FLAGS = 64;
static constexpr size_t NUM_FLAGS = 128;
using Flags = std::bitset<NUM_FLAGS>;
Flags flags;
@ -240,9 +240,177 @@ private:
auto select = std::make_unique<Node>("SELECT", next_flag++, COLUMN_LEVEL);
auto insert = std::make_unique<Node>("INSERT", next_flag++, COLUMN_LEVEL);
ext::push_back(all, std::move(select), std::move(insert));
auto update = std::make_unique<Node>("UPDATE", next_flag++, COLUMN_LEVEL);
ext::push_back(update->aliases, "ALTER UPDATE");
auto delet = std::make_unique<Node>("DELETE", next_flag++, TABLE_LEVEL);
ext::push_back(all, std::move(select), std::move(insert), std::move(update), std::move(delet));
ext::push_back(delet->aliases, "ALTER DELETE");
auto add_column = std::make_unique<Node>("ADD COLUMN", next_flag++, COLUMN_LEVEL);
add_column->aliases.push_back("ALTER ADD COLUMN");
auto modify_column = std::make_unique<Node>("MODIFY COLUMN", next_flag++, COLUMN_LEVEL);
modify_column->aliases.push_back("ALTER MODIFY COLUMN");
auto drop_column = std::make_unique<Node>("DROP COLUMN", next_flag++, COLUMN_LEVEL);
drop_column->aliases.push_back("ALTER DROP COLUMN");
auto comment_column = std::make_unique<Node>("COMMENT COLUMN", next_flag++, COLUMN_LEVEL);
comment_column->aliases.push_back("ALTER COMMENT COLUMN");
auto clear_column = std::make_unique<Node>("CLEAR COLUMN", next_flag++, COLUMN_LEVEL);
clear_column->aliases.push_back("ALTER CLEAR COLUMN");
auto alter_column = std::make_unique<Node>("ALTER COLUMN", std::move(add_column), std::move(modify_column), std::move(drop_column), std::move(comment_column), std::move(clear_column));
auto alter_order_by = std::make_unique<Node>("ALTER ORDER BY", next_flag++, TABLE_LEVEL);
alter_order_by->aliases.push_back("MODIFY ORDER BY");
alter_order_by->aliases.push_back("ALTER MODIFY ORDER BY");
auto add_index = std::make_unique<Node>("ADD INDEX", next_flag++, TABLE_LEVEL);
add_index->aliases.push_back("ALTER ADD INDEX");
auto drop_index = std::make_unique<Node>("DROP INDEX", next_flag++, TABLE_LEVEL);
drop_index->aliases.push_back("ALTER DROP INDEX");
auto materialize_index = std::make_unique<Node>("MATERIALIZE INDEX", next_flag++, TABLE_LEVEL);
materialize_index->aliases.push_back("ALTER MATERIALIZE INDEX");
auto clear_index = std::make_unique<Node>("CLEAR INDEX", next_flag++, TABLE_LEVEL);
clear_index->aliases.push_back("ALTER CLEAR INDEX");
auto index = std::make_unique<Node>("INDEX", std::move(alter_order_by), std::move(add_index), std::move(drop_index), std::move(materialize_index), std::move(clear_index));
index->aliases.push_back("ALTER INDEX");
auto add_constraint = std::make_unique<Node>("ADD CONSTRAINT", next_flag++, TABLE_LEVEL);
add_constraint->aliases.push_back("ALTER ADD CONSTRAINT");
auto drop_constraint = std::make_unique<Node>("DROP CONSTRAINT", next_flag++, TABLE_LEVEL);
drop_constraint->aliases.push_back("ALTER DROP CONSTRAINT");
auto alter_constraint = std::make_unique<Node>("CONSTRAINT", std::move(add_constraint), std::move(drop_constraint));
alter_constraint->aliases.push_back("ALTER CONSTRAINT");
auto modify_ttl = std::make_unique<Node>("MODIFY TTL", next_flag++, TABLE_LEVEL);
modify_ttl->aliases.push_back("ALTER MODIFY TTL");
auto modify_setting = std::make_unique<Node>("MODIFY SETTING", next_flag++, TABLE_LEVEL);
modify_setting->aliases.push_back("ALTER MODIFY SETTING");
auto move_partition = std::make_unique<Node>("MOVE PARTITION", next_flag++, TABLE_LEVEL);
ext::push_back(move_partition->aliases, "ALTER MOVE PARTITION", "MOVE PART", "ALTER MOVE PART");
auto fetch_partition = std::make_unique<Node>("FETCH PARTITION", next_flag++, TABLE_LEVEL);
ext::push_back(fetch_partition->aliases, "ALTER FETCH PARTITION");
auto freeze_partition = std::make_unique<Node>("FREEZE PARTITION", next_flag++, TABLE_LEVEL);
ext::push_back(freeze_partition->aliases, "ALTER FREEZE PARTITION");
auto alter_table = std::make_unique<Node>("ALTER TABLE", std::move(update), std::move(delet), std::move(alter_column), std::move(index), std::move(alter_constraint), std::move(modify_ttl), std::move(modify_setting), std::move(move_partition), std::move(fetch_partition), std::move(freeze_partition));
auto refresh_view = std::make_unique<Node>("REFRESH VIEW", next_flag++, VIEW_LEVEL);
ext::push_back(refresh_view->aliases, "ALTER LIVE VIEW REFRESH");
auto modify_view_query = std::make_unique<Node>("MODIFY VIEW QUERY", next_flag++, VIEW_LEVEL);
auto alter_view = std::make_unique<Node>("ALTER VIEW", std::move(refresh_view), std::move(modify_view_query));
auto alter = std::make_unique<Node>("ALTER", std::move(alter_table), std::move(alter_view));
ext::push_back(all, std::move(alter));
auto create_database = std::make_unique<Node>("CREATE DATABASE", next_flag++, DATABASE_LEVEL);
ext::push_back(create_database->aliases, "ATTACH DATABASE");
auto create_table = std::make_unique<Node>("CREATE TABLE", next_flag++, TABLE_LEVEL);
ext::push_back(create_table->aliases, "ATTACH TABLE");
auto create_view = std::make_unique<Node>("CREATE VIEW", next_flag++, VIEW_LEVEL);
ext::push_back(create_view->aliases, "ATTACH VIEW");
auto create_dictionary = std::make_unique<Node>("CREATE DICTIONARY", next_flag++, DICTIONARY_LEVEL);
ext::push_back(create_dictionary->aliases, "ATTACH DICTIONARY");
auto create = std::make_unique<Node>("CREATE", std::move(create_database), std::move(create_table), std::move(create_view), std::move(create_dictionary));
ext::push_back(create->aliases, "ATTACH");
ext::push_back(all, std::move(create));
auto create_temporary_table = std::make_unique<Node>("CREATE TEMPORARY TABLE", next_flag++, GLOBAL_LEVEL);
ext::push_back(all, std::move(create_temporary_table));
auto drop_database = std::make_unique<Node>("DROP DATABASE", next_flag++, DATABASE_LEVEL);
auto drop_table = std::make_unique<Node>("DROP TABLE", next_flag++, TABLE_LEVEL);
auto drop_view = std::make_unique<Node>("DROP VIEW", next_flag++, VIEW_LEVEL);
auto drop_dictionary = std::make_unique<Node>("DROP DICTIONARY", next_flag++, DICTIONARY_LEVEL);
auto drop = std::make_unique<Node>("DROP", std::move(drop_database), std::move(drop_table), std::move(drop_view), std::move(drop_dictionary));
ext::push_back(all, std::move(drop));
auto detach_database = std::make_unique<Node>("DETACH DATABASE", next_flag++, DATABASE_LEVEL);
auto detach_table = std::make_unique<Node>("DETACH TABLE", next_flag++, TABLE_LEVEL);
auto detach_view = std::make_unique<Node>("DETACH VIEW", next_flag++, VIEW_LEVEL);
auto detach_dictionary = std::make_unique<Node>("DETACH DICTIONARY", next_flag++, DICTIONARY_LEVEL);
auto detach = std::make_unique<Node>("DETACH", std::move(detach_database), std::move(detach_table), std::move(detach_view), std::move(detach_dictionary));
ext::push_back(all, std::move(detach));
auto truncate_table = std::make_unique<Node>("TRUNCATE TABLE", next_flag++, TABLE_LEVEL);
auto truncate_view = std::make_unique<Node>("TRUNCATE VIEW", next_flag++, VIEW_LEVEL);
auto truncate = std::make_unique<Node>("TRUNCATE", std::move(truncate_table), std::move(truncate_view));
ext::push_back(all, std::move(truncate));
auto optimize = std::make_unique<Node>("OPTIMIZE", next_flag++, TABLE_LEVEL);
optimize->aliases.push_back("OPTIMIZE TABLE");
ext::push_back(all, std::move(optimize));
auto kill_query = std::make_unique<Node>("KILL QUERY", next_flag++, GLOBAL_LEVEL);
auto kill_mutation = std::make_unique<Node>("KILL MUTATION", next_flag++, TABLE_LEVEL);
auto kill = std::make_unique<Node>("KILL", std::move(kill_query), std::move(kill_mutation));
ext::push_back(all, std::move(kill));
auto create_user = std::make_unique<Node>("CREATE USER", next_flag++, GLOBAL_LEVEL);
ext::push_back(create_user->aliases, "ALTER USER", "DROP USER", "CREATE ROLE", "DROP ROLE", "CREATE POLICY", "ALTER POLICY", "DROP POLICY", "CREATE QUOTA", "ALTER QUOTA", "DROP QUOTA");
ext::push_back(all, std::move(create_user));
auto shutdown = std::make_unique<Node>("SHUTDOWN", next_flag++, GLOBAL_LEVEL);
ext::push_back(shutdown->aliases, "SYSTEM SHUTDOWN", "SYSTEM KILL");
auto drop_cache = std::make_unique<Node>("DROP CACHE", next_flag++, GLOBAL_LEVEL);
ext::push_back(drop_cache->aliases, "SYSTEM DROP CACHE", "DROP DNS CACHE", "SYSTEM DROP DNS CACHE", "DROP MARK CACHE", "SYSTEM DROP MARK CACHE", "DROP UNCOMPRESSED CACHE", "SYSTEM DROP UNCOMPRESSED CACHE", "DROP COMPILED EXPRESSION CACHE", "SYSTEM DROP COMPILED EXPRESSION CACHE");
auto reload_config = std::make_unique<Node>("RELOAD CONFIG", next_flag++, GLOBAL_LEVEL);
ext::push_back(reload_config->aliases, "SYSTEM RELOAD CONFIG");
auto reload_dictionary = std::make_unique<Node>("RELOAD DICTIONARY", next_flag++, GLOBAL_LEVEL);
ext::push_back(reload_dictionary->aliases, "SYSTEM RELOAD DICTIONARY", "RELOAD DICTIONARIES", "SYSTEM RELOAD DICTIONARIES", "RELOAD EMBEDDED DICTIONARIES", "SYSTEM RELOAD EMBEDDED DICTIONARIES");
auto stop_merges = std::make_unique<Node>("STOP MERGES", next_flag++, TABLE_LEVEL);
ext::push_back(stop_merges->aliases, "SYSTEM STOP MERGES", "START MERGES", "SYSTEM START MERGES");
auto stop_ttl_merges = std::make_unique<Node>("STOP TTL MERGES", next_flag++, TABLE_LEVEL);
ext::push_back(stop_ttl_merges->aliases, "SYSTEM STOP TTL MERGES", "START TTL MERGES", "SYSTEM START TTL MERGES");
auto stop_fetches = std::make_unique<Node>("STOP FETCHES", next_flag++, TABLE_LEVEL);
ext::push_back(stop_fetches->aliases, "SYSTEM STOP FETCHES", "START FETCHES", "SYSTEM START FETCHES");
auto stop_moves = std::make_unique<Node>("STOP MOVES", next_flag++, TABLE_LEVEL);
ext::push_back(stop_moves->aliases, "SYSTEM STOP MOVES", "START MOVES", "SYSTEM START MOVES");
auto stop_distributed_sends = std::make_unique<Node>("STOP DISTRIBUTED SENDS", next_flag++, TABLE_LEVEL);
ext::push_back(stop_distributed_sends->aliases, "SYSTEM STOP DISTRIBUTED SENDS", "START DISTRIBUTED SENDS", "SYSTEM START DISTRIBUTED SENDS");
auto stop_replicated_sends = std::make_unique<Node>("STOP REPLICATED SENDS", next_flag++, TABLE_LEVEL);
ext::push_back(stop_replicated_sends->aliases, "SYSTEM STOP REPLICATED SENDS", "START REPLICATED SENDS", "SYSTEM START REPLICATED SENDS");
auto stop_replication_queues = std::make_unique<Node>("STOP REPLICATION QUEUES", next_flag++, TABLE_LEVEL);
ext::push_back(stop_replication_queues->aliases, "SYSTEM STOP REPLICATION QUEUES", "START REPLICATION QUEUES", "SYSTEM START REPLICATION QUEUES");
auto sync_replica = std::make_unique<Node>("SYNC REPLICA", next_flag++, TABLE_LEVEL);
ext::push_back(sync_replica->aliases, "SYSTEM SYNC REPLICA");
auto restart_replica = std::make_unique<Node>("RESTART REPLICA", next_flag++, TABLE_LEVEL);
ext::push_back(restart_replica->aliases, "SYSTEM RESTART REPLICA");
auto flush_distributed = std::make_unique<Node>("FLUSH DISTRIBUTED", next_flag++, TABLE_LEVEL);
ext::push_back(flush_distributed->aliases, "SYSTEM FLUSH DISTRIBUTED");
auto flush_logs = std::make_unique<Node>("FLUSH LOGS", next_flag++, GLOBAL_LEVEL);
ext::push_back(flush_logs->aliases, "SYSTEM FLUSH LOGS");
auto system = std::make_unique<Node>("SYSTEM", std::move(shutdown), std::move(drop_cache), std::move(reload_config), std::move(reload_dictionary), std::move(stop_merges), std::move(stop_ttl_merges), std::move(stop_fetches), std::move(stop_moves), std::move(stop_distributed_sends), std::move(stop_replicated_sends), std::move(stop_replication_queues), std::move(sync_replica), std::move(restart_replica), std::move(flush_distributed), std::move(flush_logs));
ext::push_back(all, std::move(system));
auto dict_get = std::make_unique<Node>("dictGet()", next_flag++, DICTIONARY_LEVEL);
dict_get->aliases.push_back("dictHas()");
dict_get->aliases.push_back("dictGetHierarchy()");
dict_get->aliases.push_back("dictIsIn()");
ext::push_back(all, std::move(dict_get));
auto address_to_line = std::make_unique<Node>("addressToLine()", next_flag++, GLOBAL_LEVEL);
auto address_to_symbol = std::make_unique<Node>("addressToSymbol()", next_flag++, GLOBAL_LEVEL);
auto demangle = std::make_unique<Node>("demangle()", next_flag++, GLOBAL_LEVEL);
auto introspection = std::make_unique<Node>("INTROSPECTION", std::move(address_to_line), std::move(address_to_symbol), std::move(demangle));
ext::push_back(introspection->aliases, "INTROSPECTION FUNCTIONS");
ext::push_back(all, std::move(introspection));
auto file = std::make_unique<Node>("file()", next_flag++, GLOBAL_LEVEL);
auto url = std::make_unique<Node>("url()", next_flag++, GLOBAL_LEVEL);
auto input = std::make_unique<Node>("input()", next_flag++, GLOBAL_LEVEL);
auto values = std::make_unique<Node>("values()", next_flag++, GLOBAL_LEVEL);
auto numbers = std::make_unique<Node>("numbers()", next_flag++, GLOBAL_LEVEL);
auto merge = std::make_unique<Node>("merge()", next_flag++, DATABASE_LEVEL);
auto remote = std::make_unique<Node>("remote()", next_flag++, GLOBAL_LEVEL);
ext::push_back(remote->aliases, "remoteSecure()", "cluster()");
auto mysql = std::make_unique<Node>("mysql()", next_flag++, GLOBAL_LEVEL);
auto odbc = std::make_unique<Node>("odbc()", next_flag++, GLOBAL_LEVEL);
auto jdbc = std::make_unique<Node>("jdbc()", next_flag++, GLOBAL_LEVEL);
auto hdfs = std::make_unique<Node>("hdfs()", next_flag++, GLOBAL_LEVEL);
auto s3 = std::make_unique<Node>("s3()", next_flag++, GLOBAL_LEVEL);
auto table_functions = std::make_unique<Node>("TABLE FUNCTIONS", std::move(file), std::move(url), std::move(input), std::move(values), std::move(numbers), std::move(merge), std::move(remote), std::move(mysql), std::move(odbc), std::move(jdbc), std::move(hdfs), std::move(s3));
ext::push_back(all, std::move(table_functions));
flags_to_keyword_tree_ = std::make_unique<Node>("ALL", std::move(all));
flags_to_keyword_tree_->aliases.push_back("ALL PRIVILEGES");

View File

@ -43,6 +43,8 @@ namespace
const AccessFlags column_level_flags = AccessFlags::columnLevel();
const AccessFlags show_flag = AccessType::SHOW;
const AccessFlags exists_flag = AccessType::EXISTS;
const AccessFlags create_table_flag = AccessType::CREATE_TABLE;
const AccessFlags create_temporary_table_flag = AccessType::CREATE_TEMPORARY_TABLE;
};
}
@ -324,6 +326,9 @@ private:
access |= helper.show_flag | helper.exists_flag;
else if ((level >= DATABASE_LEVEL) && children)
access |= helper.exists_flag;
if ((level == GLOBAL_LEVEL) && (access & helper.create_table_flag))
access |= helper.create_temporary_table_flag;
}
void calculateMinAndMaxAccess()

View File

@ -249,7 +249,28 @@ const AccessRights & AccessRightsContext::calculateResultAccess(UInt64 readonly_
result = granted_to_user;
/// TODO
static const AccessFlags table_ddl = AccessType::CREATE_DATABASE | AccessType::CREATE_TABLE | AccessType::CREATE_VIEW
| AccessType::ALTER_TABLE | AccessType::ALTER_VIEW | AccessType::DROP_DATABASE | AccessType::DROP_TABLE | AccessType::DROP_VIEW
| AccessType::DETACH_DATABASE | AccessType::DETACH_TABLE | AccessType::DETACH_VIEW | AccessType::TRUNCATE;
static const AccessFlags dictionary_ddl = AccessType::CREATE_DICTIONARY | AccessType::DROP_DICTIONARY | AccessType::DETACH_DICTIONARY;
static const AccessFlags table_and_dictionary_ddl = table_ddl | dictionary_ddl;
static const AccessFlags write_table_access = AccessType::INSERT | AccessType::OPTIMIZE;
if (readonly_)
result.fullRevoke(write_table_access | AccessType::SYSTEM);
if (readonly_ || !allow_ddl_)
result.fullRevoke(table_and_dictionary_ddl);
if (readonly_ == 1)
{
/// Table functions are forbidden in readonly mode.
/// For example, for readonly = 2 - allowed.
result.fullRevoke(AccessType::CREATE_TEMPORARY_TABLE | AccessType::TABLE_FUNCTIONS);
}
if (!allow_introspection_)
result.fullRevoke(AccessType::INTROSPECTION);
return result;
}

View File

@ -1,4 +1,5 @@
#include <Access/AccessRightsElement.h>
#include <Dictionaries/IDictionary.h>
#include <Common/quoteString.h>
@ -49,7 +50,7 @@ String AccessRightsElement::toString() const
if (any_database)
msg += "*.";
else if (!database.empty())
else if (!database.empty() && (database != IDictionary::NO_DATABASE_TAG))
msg += backQuoteIfNeed(database) + ".";
if (any_table)

View File

@ -25,9 +25,130 @@ enum class AccessType
INSERT,
UPDATE, /// allows to execute ALTER UPDATE
DELETE, /// allows to execute ALTER DELETE
ADD_COLUMN,
DROP_COLUMN,
MODIFY_COLUMN,
COMMENT_COLUMN,
CLEAR_COLUMN,
ALTER_COLUMN, /// allow to execute ALTER {ADD|DROP|MODIFY...} COLUMN
ALTER_ORDER_BY,
ADD_INDEX,
DROP_INDEX,
MATERIALIZE_INDEX,
CLEAR_INDEX,
INDEX, /// allows to execute ALTER ORDER BY or ALTER {ADD|DROP...} INDEX
ADD_CONSTRAINT,
DROP_CONSTRAINT,
ALTER_CONSTRAINT, /// allows to execute ALTER {ADD|DROP} CONSTRAINT
MODIFY_TTL, /// allows to execute ALTER MODIFY TTL
MODIFY_SETTING, /// allows to execute ALTER MODIFY SETTING
MOVE_PARTITION,
FETCH_PARTITION,
FREEZE_PARTITION,
ALTER_TABLE, /// allows to execute ALTER TABLE ...
REFRESH_VIEW, /// allows to execute ALTER LIVE VIEW REFRESH
MODIFY_VIEW_QUERY, /// allows to execute ALTER TABLE MODIFY QUERY
ALTER_VIEW, /// allows to execute ALTER LIVE VIEW REFRESH, ALTER TABLE MODIFY QUERY
ALTER, /// allows to execute ALTER {TABLE|LIVE VIEW} ...
CREATE_DATABASE, /// allows to execute {CREATE|ATTACH} DATABASE
CREATE_TABLE, /// allows to execute {CREATE|ATTACH} TABLE
CREATE_VIEW, /// allows to execute {CREATE|ATTACH} VIEW
CREATE_DICTIONARY, /// allows to execute {CREATE|ATTACH} DICTIONARY
CREATE_TEMPORARY_TABLE, /// allows to create and manipulate temporary tables and views.
CREATE, /// allows to execute {CREATE|ATTACH} [TEMPORARY] {DATABASE|TABLE|VIEW|DICTIONARY}
ATTACH_DATABASE, /// allows to execute {CREATE|ATTACH} DATABASE
ATTACH_TABLE, /// allows to execute {CREATE|ATTACH} TABLE
ATTACH_VIEW, /// allows to execute {CREATE|ATTACH} VIEW
ATTACH_DICTIONARY, /// allows to execute {CREATE|ATTACH} DICTIONARY
ATTACH, /// allows to execute {CREATE|ATTACH} {DATABASE|TABLE|VIEW|DICTIONARY}
DROP_DATABASE,
DROP_TABLE,
DROP_VIEW,
DROP_DICTIONARY,
DROP, /// allows to execute DROP {DATABASE|TABLE|VIEW|DICTIONARY}
DETACH_DATABASE,
DETACH_TABLE,
DETACH_VIEW,
DETACH_DICTIONARY,
DETACH, /// allows to execute DETACH {DATABASE|TABLE|VIEW|DICTIONARY}
TRUNCATE_TABLE,
TRUNCATE_VIEW,
TRUNCATE, /// allows to execute TRUNCATE {TABLE|VIEW}
OPTIMIZE, /// allows to execute OPTIMIZE TABLE
KILL_QUERY, /// allows to kill a query started by another user (anyone can kill his own queries)
KILL_MUTATION, /// allows to kill a mutation
KILL, /// allows to execute KILL {MUTATION|QUERY}
CREATE_USER, /// allows to create, alter and drop users, roles, quotas, row policies.
ALTER_USER,
DROP_USER,
CREATE_ROLE,
DROP_ROLE,
CREATE_POLICY,
ALTER_POLICY,
DROP_POLICY,
CREATE_QUOTA,
ALTER_QUOTA,
DROP_QUOTA,
SHUTDOWN,
DROP_CACHE,
RELOAD_CONFIG,
RELOAD_DICTIONARY,
STOP_MERGES,
STOP_TTL_MERGES,
STOP_FETCHES,
STOP_MOVES,
STOP_DISTRIBUTED_SENDS,
STOP_REPLICATED_SENDS,
STOP_REPLICATION_QUEUES,
SYNC_REPLICA,
RESTART_REPLICA,
FLUSH_DISTRIBUTED,
FLUSH_LOGS,
SYSTEM, /// allows to execute SYSTEM {SHUTDOWN|RELOAD CONFIG|...}
dictGet, /// allows to execute functions dictGet, dictHas, dictGetHierarchy, dictIsIn
dictHas, /// allows to execute functions dictGet, dictHas, dictGetHierarchy, dictIsIn
dictGetHierarchy, /// allows to execute functions dictGet, dictHas, dictGetHierarchy, dictIsIn
dictIsIn, /// allows to execute functions dictGet, dictHas, dictGetHierarchy, dictIsIn
addressToLine, /// allows to execute function addressToLine
addressToSymbol, /// allows to execute function addressToSymbol
demangle, /// allows to execute function demangle
INTROSPECTION, /// allows to execute functions addressToLine, addressToSymbol, demangle
file,
url,
input,
values,
numbers,
merge,
remote,
mysql,
odbc,
jdbc,
hdfs,
s3,
TABLE_FUNCTIONS, /// allows to execute any table function
};
constexpr size_t MAX_ACCESS_TYPE = static_cast<size_t>(AccessType::DELETE) + 1;
constexpr size_t MAX_ACCESS_TYPE = static_cast<size_t>(AccessType::TABLE_FUNCTIONS) + 1;
std::string_view toString(AccessType type);
@ -74,6 +195,127 @@ namespace impl
ACCESS_TYPE_TO_KEYWORD_CASE(UPDATE);
ACCESS_TYPE_TO_KEYWORD_CASE(DELETE);
ACCESS_TYPE_TO_KEYWORD_CASE(ADD_COLUMN);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_COLUMN);
ACCESS_TYPE_TO_KEYWORD_CASE(MODIFY_COLUMN);
ACCESS_TYPE_TO_KEYWORD_CASE(COMMENT_COLUMN);
ACCESS_TYPE_TO_KEYWORD_CASE(CLEAR_COLUMN);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_COLUMN);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_ORDER_BY);
ACCESS_TYPE_TO_KEYWORD_CASE(ADD_INDEX);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_INDEX);
ACCESS_TYPE_TO_KEYWORD_CASE(MATERIALIZE_INDEX);
ACCESS_TYPE_TO_KEYWORD_CASE(CLEAR_INDEX);
ACCESS_TYPE_TO_KEYWORD_CASE(INDEX);
ACCESS_TYPE_TO_KEYWORD_CASE(ADD_CONSTRAINT);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_CONSTRAINT);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_CONSTRAINT);
ACCESS_TYPE_TO_KEYWORD_CASE(MODIFY_TTL);
ACCESS_TYPE_TO_KEYWORD_CASE(MODIFY_SETTING);
ACCESS_TYPE_TO_KEYWORD_CASE(MOVE_PARTITION);
ACCESS_TYPE_TO_KEYWORD_CASE(FETCH_PARTITION);
ACCESS_TYPE_TO_KEYWORD_CASE(FREEZE_PARTITION);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(REFRESH_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(MODIFY_VIEW_QUERY);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_DATABASE);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_DICTIONARY);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_TEMPORARY_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE);
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH_DATABASE);
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH_DICTIONARY);
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_DATABASE);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_DICTIONARY);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP);
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH_DATABASE);
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH_DICTIONARY);
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH);
ACCESS_TYPE_TO_KEYWORD_CASE(TRUNCATE_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(TRUNCATE_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(TRUNCATE);
ACCESS_TYPE_TO_KEYWORD_CASE(OPTIMIZE);
ACCESS_TYPE_TO_KEYWORD_CASE(KILL_QUERY);
ACCESS_TYPE_TO_KEYWORD_CASE(KILL_MUTATION);
ACCESS_TYPE_TO_KEYWORD_CASE(KILL);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_USER);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_USER);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_USER);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_ROLE);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_ROLE);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_POLICY);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_POLICY);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_POLICY);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_QUOTA);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_QUOTA);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_QUOTA);
ACCESS_TYPE_TO_KEYWORD_CASE(SHUTDOWN);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_CACHE);
ACCESS_TYPE_TO_KEYWORD_CASE(RELOAD_CONFIG);
ACCESS_TYPE_TO_KEYWORD_CASE(RELOAD_DICTIONARY);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_MERGES);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_TTL_MERGES);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_FETCHES);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_MOVES);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_DISTRIBUTED_SENDS);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_REPLICATED_SENDS);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_REPLICATION_QUEUES);
ACCESS_TYPE_TO_KEYWORD_CASE(SYNC_REPLICA);
ACCESS_TYPE_TO_KEYWORD_CASE(RESTART_REPLICA);
ACCESS_TYPE_TO_KEYWORD_CASE(FLUSH_DISTRIBUTED);
ACCESS_TYPE_TO_KEYWORD_CASE(FLUSH_LOGS);
ACCESS_TYPE_TO_KEYWORD_CASE(SYSTEM);
ACCESS_TYPE_TO_KEYWORD_CASE(dictGet);
ACCESS_TYPE_TO_KEYWORD_CASE(dictHas);
ACCESS_TYPE_TO_KEYWORD_CASE(dictGetHierarchy);
ACCESS_TYPE_TO_KEYWORD_CASE(dictIsIn);
ACCESS_TYPE_TO_KEYWORD_CASE(addressToLine);
ACCESS_TYPE_TO_KEYWORD_CASE(addressToSymbol);
ACCESS_TYPE_TO_KEYWORD_CASE(demangle);
ACCESS_TYPE_TO_KEYWORD_CASE(INTROSPECTION);
ACCESS_TYPE_TO_KEYWORD_CASE(file);
ACCESS_TYPE_TO_KEYWORD_CASE(url);
ACCESS_TYPE_TO_KEYWORD_CASE(input);
ACCESS_TYPE_TO_KEYWORD_CASE(values);
ACCESS_TYPE_TO_KEYWORD_CASE(numbers);
ACCESS_TYPE_TO_KEYWORD_CASE(merge);
ACCESS_TYPE_TO_KEYWORD_CASE(remote);
ACCESS_TYPE_TO_KEYWORD_CASE(mysql);
ACCESS_TYPE_TO_KEYWORD_CASE(odbc);
ACCESS_TYPE_TO_KEYWORD_CASE(jdbc);
ACCESS_TYPE_TO_KEYWORD_CASE(hdfs);
ACCESS_TYPE_TO_KEYWORD_CASE(s3);
ACCESS_TYPE_TO_KEYWORD_CASE(TABLE_FUNCTIONS);
#undef ACCESS_TYPE_TO_KEYWORD_CASE
}

View File

@ -28,8 +28,22 @@ struct IDictionaryBase : public IExternalLoadable
virtual const std::string & getDatabase() const = 0;
virtual const std::string & getName() const = 0;
virtual const std::string & getFullName() const = 0;
const std::string & getLoadableName() const override { return getFullName(); }
/// Specifies that no database is used.
/// Sometimes we cannot simply use an empty string for that because an empty string is
/// usually replaced with the current database.
static constexpr char NO_DATABASE_TAG[] = "<no_database>";
std::string_view getDatabaseOrNoDatabaseTag() const
{
const std::string & database = getDatabase();
if (!database.empty())
return database;
return NO_DATABASE_TAG;
}
virtual std::string getTypeName() const = 0;
virtual size_t getBytesAllocated() const = 0;

View File

@ -48,7 +48,6 @@ 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_loader.
@ -126,12 +125,7 @@ private:
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
const auto dict_ptr = dict.get();
if (!context.hasDictionaryAccessRights(dict_ptr->getFullName()))
{
throw Exception{"For function " + getName() + ", cannot access dictionary "
+ dict->getFullName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
}
context.checkAccess(AccessType::dictHas, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!executeDispatchSimple<FlatDictionary>(block, arguments, result, dict_ptr) &&
!executeDispatchSimple<HashedDictionary>(block, arguments, result, dict_ptr) &&
@ -301,12 +295,7 @@ private:
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
const auto dict_ptr = dict.get();
if (!context.hasDictionaryAccessRights(dict_ptr->getFullName()))
{
throw Exception{"For function " + getName() + ", cannot access dictionary "
+ dict->getFullName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
}
context.checkAccess(AccessType::dictGet, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
@ -487,12 +476,7 @@ private:
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
const auto dict_ptr = dict.get();
if (!context.hasDictionaryAccessRights(dict_ptr->getFullName()))
{
throw Exception{"For function " + getName() + ", cannot access dictionary "
+ dict->getFullName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
}
context.checkAccess(AccessType::dictGet, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
@ -829,12 +813,7 @@ private:
auto dict = dictionaries_loader.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};
}
context.checkAccess(AccessType::dictGet, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
@ -1093,12 +1072,7 @@ private:
auto dict = dictionaries_loader.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};
}
context.checkAccess(AccessType::dictGet, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
@ -1670,12 +1644,7 @@ private:
auto dict = dictionaries_loader.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};
}
context.checkAccess(AccessType::dictGetHierarchy, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
@ -1839,12 +1808,7 @@ private:
auto dict = dictionaries_loader.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};
}
context.checkAccess(AccessType::dictIsIn, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr)
&& !executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr)

View File

@ -28,7 +28,6 @@ namespace ErrorCodes
extern const int ILLEGAL_COLUMN;
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int FUNCTION_NOT_ALLOWED;
}
class FunctionAddressToLine : public IFunction
@ -37,9 +36,7 @@ public:
static constexpr auto name = "addressToLine";
static FunctionPtr create(const Context & context)
{
if (!context.getSettingsRef().allow_introspection_functions)
throw Exception("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
context.checkAccess(AccessType::addressToLine);
return std::make_shared<FunctionAddressToLine>();
}

View File

@ -19,7 +19,6 @@ namespace ErrorCodes
extern const int ILLEGAL_COLUMN;
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int FUNCTION_NOT_ALLOWED;
}
class FunctionAddressToSymbol : public IFunction
@ -28,8 +27,7 @@ public:
static constexpr auto name = "addressToSymbol";
static FunctionPtr create(const Context & context)
{
if (!context.getSettingsRef().allow_introspection_functions)
throw Exception("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
context.checkAccess(AccessType::addressToSymbol);
return std::make_shared<FunctionAddressToSymbol>();
}

View File

@ -17,7 +17,6 @@ namespace ErrorCodes
extern const int ILLEGAL_COLUMN;
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int FUNCTION_NOT_ALLOWED;
}
class FunctionDemangle : public IFunction
@ -26,9 +25,7 @@ public:
static constexpr auto name = "demangle";
static FunctionPtr create(const Context & context)
{
if (!context.getSettingsRef().allow_introspection_functions)
throw Exception("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
context.checkAccess(AccessType::demangle);
return std::make_shared<FunctionDemangle>();
}

View File

@ -30,29 +30,23 @@ inline void forEachTable(Context & context, F && f)
void ActionLocksManager::add(StorageActionBlockType action_type)
{
forEachTable(global_context, [&] (const StoragePtr & table)
{
ActionLock action_lock = table->getActionLock(action_type);
if (!action_lock.expired())
{
std::lock_guard lock(mutex);
storage_locks[table.get()][action_type] = std::move(action_lock);
}
});
forEachTable(global_context, [&](const StoragePtr & table) { add(table, action_type); });
}
void ActionLocksManager::add(const String & database_name, const String & table_name, StorageActionBlockType action_type)
{
if (auto table = global_context.tryGetTable(database_name, table_name))
{
ActionLock action_lock = table->getActionLock(action_type);
add(table, action_type);
}
if (!action_lock.expired())
{
std::lock_guard lock(mutex);
storage_locks[table.get()][action_type] = std::move(action_lock);
}
void ActionLocksManager::add(const StoragePtr & table, StorageActionBlockType action_type)
{
ActionLock action_lock = table->getActionLock(action_type);
if (!action_lock.expired())
{
std::lock_guard lock(mutex);
storage_locks[table.get()][action_type] = std::move(action_lock);
}
}
@ -67,12 +61,15 @@ void ActionLocksManager::remove(StorageActionBlockType action_type)
void ActionLocksManager::remove(const String & database_name, const String & table_name, StorageActionBlockType action_type)
{
if (auto table = global_context.tryGetTable(database_name, table_name))
{
std::lock_guard lock(mutex);
remove(table, action_type);
}
if (storage_locks.count(table.get()))
storage_locks[table.get()].erase(action_type);
}
void ActionLocksManager::remove(const StoragePtr & table, StorageActionBlockType action_type)
{
std::lock_guard lock(mutex);
if (storage_locks.count(table.get()))
storage_locks[table.get()].erase(action_type);
}
void ActionLocksManager::cleanExpired()

View File

@ -24,11 +24,13 @@ public:
void add(StorageActionBlockType action_type);
/// Add new lock for a table if it has not been already added
void add(const String & database_name, const String & table_name, StorageActionBlockType action_type);
void add(const StoragePtr & table, StorageActionBlockType action_type);
/// Remove locks for all tables
void remove(StorageActionBlockType action_type);
/// Removes a lock for a table if it exists
void remove(const String & database_name, const String & table_name, StorageActionBlockType action_type);
void remove(const StoragePtr & table, StorageActionBlockType action_type);
/// Removes all locks of non-existing tables
void cleanExpired();

View File

@ -1242,7 +1242,7 @@ private:
};
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & context, NameSet && query_databases)
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & context, AccessRightsElements && query_required_access)
{
/// Remove FORMAT <fmt> and INTO OUTFILE <file> if exists
ASTPtr query_ptr = query_ptr_->clone();
@ -1271,51 +1271,77 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & cont
ClusterPtr cluster = context.getCluster(query->cluster);
DDLWorker & ddl_worker = context.getDDLWorker();
/// Check database access rights, assume that all servers have the same users config
NameSet databases_to_access;
const String & current_database = context.getCurrentDatabase();
/// Enumerate hosts which will be used to send query.
Cluster::AddressesWithFailover shards = cluster->getShardsAddresses();
std::vector<HostID> hosts;
bool use_shard_default_db = false;
bool use_local_default_db = false;
for (const auto & shard : shards)
{
for (const auto & addr : shard)
{
hosts.emplace_back(addr);
}
/// Expand empty database name to shards' default (o current) database name
for (const String & database : query_databases)
if (hosts.empty())
throw Exception("No hosts defined to execute distributed DDL query", ErrorCodes::LOGICAL_ERROR);
/// The current database in a distributed query need to be replaced with either
/// the local current database or a shard's default database.
bool need_replace_current_database
= (std::find_if(
query_required_access.begin(),
query_required_access.end(),
[](const AccessRightsElement & elem) { return elem.isEmptyDatabase(); })
!= query_required_access.end());
if (need_replace_current_database)
{
bool use_local_default_database = false;
Strings shard_default_databases;
for (const auto & shard : shards)
{
for (const auto & addr : shard)
{
if (database.empty())
{
bool has_shard_default_db = !addr.default_database.empty();
use_shard_default_db |= has_shard_default_db;
use_local_default_db |= !has_shard_default_db;
databases_to_access.emplace(has_shard_default_db ? addr.default_database : current_database);
}
if (!addr.default_database.empty())
shard_default_databases.push_back(addr.default_database);
else
databases_to_access.emplace(database);
use_local_default_database = true;
}
}
std::sort(shard_default_databases.begin(), shard_default_databases.end());
shard_default_databases.erase(std::unique(shard_default_databases.begin(), shard_default_databases.end()), shard_default_databases.end());
assert(use_local_default_database || !shard_default_databases.empty());
if (use_local_default_database && !shard_default_databases.empty())
throw Exception("Mixed local default DB and shard default DB in DDL query", ErrorCodes::NOT_IMPLEMENTED);
if (use_local_default_database)
{
const String & current_database = context.getCurrentDatabase();
AddDefaultDatabaseVisitor visitor(current_database);
visitor.visitDDL(query_ptr);
query_required_access.replaceEmptyDatabase(current_database);
}
else
{
size_t old_num_elements = query_required_access.size();
for (size_t i = 0; i != old_num_elements; ++i)
{
auto & element = query_required_access[i];
if (element.isEmptyDatabase())
{
element.setDatabase(shard_default_databases[0]);
for (size_t j = 1; j != shard_default_databases.size(); ++j)
{
query_required_access.push_back(element);
query_required_access.back().setDatabase(shard_default_databases[j]);
}
}
}
}
}
if (use_shard_default_db && use_local_default_db)
throw Exception("Mixed local default DB and shard default DB in DDL query", ErrorCodes::NOT_IMPLEMENTED);
if (databases_to_access.empty())
throw Exception("No databases to access in distributed DDL query", ErrorCodes::LOGICAL_ERROR);
for (const String & database : databases_to_access)
context.checkDatabaseAccessRights(database);
if (use_local_default_db)
{
AddDefaultDatabaseVisitor visitor(current_database);
visitor.visitDDL(query_ptr);
}
/// Check access rights, assume that all servers have the same users config
context.checkAccess(query_required_access);
DDLLogEntry entry;
entry.hosts = std::move(hosts);

View File

@ -17,12 +17,13 @@ namespace DB
{
class ASTAlterQuery;
class AccessRightsElements;
struct DDLLogEntry;
struct DDLTask;
/// Pushes distributed DDL query to the queue
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, NameSet && query_databases);
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, AccessRightsElements && query_required_access);
class DDLWorker

View File

@ -4,6 +4,7 @@
#include <Interpreters/AddDefaultDatabaseVisitor.h>
#include <Interpreters/Context.h>
#include <Parsers/ASTAlterQuery.h>
#include <Parsers/ASTAssignment.h>
#include <Storages/IStorage.h>
#include <Storages/AlterCommands.h>
#include <Storages/MutationCommands.h>
@ -36,7 +37,9 @@ BlockIO InterpreterAlterQuery::execute()
const auto & alter = query_ptr->as<ASTAlterQuery &>();
if (!alter.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, {alter.database});
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccess());
context.checkAccess(getRequiredAccess());
const String & table_name = alter.table;
String database_name = alter.database.empty() ? context.getCurrentDatabase() : alter.database;
@ -113,4 +116,158 @@ BlockIO InterpreterAlterQuery::execute()
return {};
}
AccessRightsElements InterpreterAlterQuery::getRequiredAccess() const
{
AccessRightsElements required_access;
const auto & alter = query_ptr->as<ASTAlterQuery &>();
for (ASTAlterCommand * command_ast : alter.command_list->commands)
{
auto column_name = [&]() -> String { return getIdentifierName(command_ast->column); };
auto column_name_from_col_decl = [&]() -> std::string_view { return command_ast->col_decl->as<ASTColumnDeclaration &>().name; };
auto column_names_from_update_assignments = [&]() -> std::vector<std::string_view>
{
std::vector<std::string_view> column_names;
for (const ASTPtr & assignment_ast : command_ast->update_assignments->children)
column_names.emplace_back(assignment_ast->as<const ASTAssignment &>().column_name);
return column_names;
};
switch (command_ast->type)
{
case ASTAlterCommand::UPDATE:
{
required_access.emplace_back(AccessType::UPDATE, alter.database, alter.table, column_names_from_update_assignments());
break;
}
case ASTAlterCommand::DELETE:
{
required_access.emplace_back(AccessType::DELETE, alter.database, alter.table);
break;
}
case ASTAlterCommand::ADD_COLUMN:
{
required_access.emplace_back(AccessType::ADD_COLUMN, alter.database, alter.table, column_name_from_col_decl());
break;
}
case ASTAlterCommand::DROP_COLUMN:
{
if (command_ast->clear_column)
required_access.emplace_back(AccessType::CLEAR_COLUMN, alter.database, alter.table, column_name());
else
required_access.emplace_back(AccessType::DROP_COLUMN, alter.database, alter.table, column_name());
break;
}
case ASTAlterCommand::MODIFY_COLUMN:
{
required_access.emplace_back(AccessType::MODIFY_COLUMN, alter.database, alter.table, column_name_from_col_decl());
break;
}
case ASTAlterCommand::COMMENT_COLUMN:
{
required_access.emplace_back(AccessType::COMMENT_COLUMN, alter.database, alter.table, column_name());
break;
}
case ASTAlterCommand::MODIFY_ORDER_BY:
{
required_access.emplace_back(AccessType::ALTER_ORDER_BY, alter.database, alter.table);
break;
}
case ASTAlterCommand::ADD_INDEX:
{
required_access.emplace_back(AccessType::ADD_INDEX, alter.database, alter.table);
break;
}
case ASTAlterCommand::DROP_INDEX:
{
if (command_ast->clear_index)
required_access.emplace_back(AccessType::CLEAR_INDEX, alter.database, alter.table);
else
required_access.emplace_back(AccessType::DROP_INDEX, alter.database, alter.table);
break;
}
case ASTAlterCommand::MATERIALIZE_INDEX:
{
required_access.emplace_back(AccessType::MATERIALIZE_INDEX, alter.database, alter.table);
break;
}
case ASTAlterCommand::ADD_CONSTRAINT:
{
required_access.emplace_back(AccessType::ADD_CONSTRAINT, alter.database, alter.table);
break;
}
case ASTAlterCommand::DROP_CONSTRAINT:
{
required_access.emplace_back(AccessType::DROP_CONSTRAINT, alter.database, alter.table);
break;
}
case ASTAlterCommand::MODIFY_TTL:
{
required_access.emplace_back(AccessType::MODIFY_TTL, alter.database, alter.table);
break;
}
case ASTAlterCommand::MODIFY_SETTING:
{
required_access.emplace_back(AccessType::MODIFY_SETTING, alter.database, alter.table);
break;
}
case ASTAlterCommand::ATTACH_PARTITION:
{
required_access.emplace_back(AccessType::INSERT, alter.database, alter.table);
break;
}
case ASTAlterCommand::DROP_PARTITION: [[fallthrough]];
case ASTAlterCommand::DROP_DETACHED_PARTITION:
{
required_access.emplace_back(AccessType::DELETE, alter.database, alter.table);
break;
}
case ASTAlterCommand::MOVE_PARTITION:
{
if ((command_ast->move_destination_type == PartDestinationType::DISK)
|| (command_ast->move_destination_type == PartDestinationType::VOLUME))
{
required_access.emplace_back(AccessType::MOVE_PARTITION, alter.database, alter.table);
}
else if (command_ast->move_destination_type == PartDestinationType::TABLE)
{
required_access.emplace_back(AccessType::SELECT | AccessType::DELETE, alter.database, alter.table);
required_access.emplace_back(AccessType::INSERT, command_ast->to_database, command_ast->to_table);
}
break;
}
case ASTAlterCommand::REPLACE_PARTITION:
{
required_access.emplace_back(AccessType::SELECT, command_ast->from_database, command_ast->from_table);
required_access.emplace_back(AccessType::DELETE | AccessType::INSERT, alter.database, alter.table);
break;
}
case ASTAlterCommand::FETCH_PARTITION:
{
required_access.emplace_back(AccessType::FETCH_PARTITION, alter.database, alter.table);
break;
}
case ASTAlterCommand::FREEZE_PARTITION: [[fallthrough]];
case ASTAlterCommand::FREEZE_ALL:
{
required_access.emplace_back(AccessType::FREEZE_PARTITION, alter.database, alter.table);
break;
}
case ASTAlterCommand::MODIFY_QUERY:
{
required_access.emplace_back(AccessType::MODIFY_VIEW_QUERY, alter.database, alter.table);
break;
}
case ASTAlterCommand::LIVE_VIEW_REFRESH:
{
required_access.emplace_back(AccessType::REFRESH_VIEW, alter.database, alter.table);
break;
}
case ASTAlterCommand::NO_TYPE: break;
}
}
return required_access;
}
}

View File

@ -6,8 +6,9 @@
namespace DB
{
class Context;
class AccessRightsElements;
/** Allows you add or remove a column in the table.
* It also allows you to manipulate the partitions of the MergeTree family tables.
@ -20,6 +21,8 @@ public:
BlockIO execute() override;
private:
AccessRightsElements getRequiredAccess() const;
ASTPtr query_ptr;
const Context & context;

View File

@ -40,6 +40,7 @@ BlockIO InterpreterCheckQuery::execute()
const String & table_name = check.table;
String database_name = check.database.empty() ? context.getCurrentDatabase() : check.database;
context.checkAccess(AccessType::SHOW, database_name, table_name);
StoragePtr table = context.getTable(database_name, table_name);
auto check_results = table->checkData(query_ptr, context);

View File

@ -79,9 +79,6 @@ InterpreterCreateQuery::InterpreterCreateQuery(const ASTPtr & query_ptr_, Contex
BlockIO InterpreterCreateQuery::createDatabase(ASTCreateQuery & create)
{
if (!create.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, {create.database});
String database_name = create.database;
auto guard = context.getDDLGuard(database_name, "");
@ -538,15 +535,6 @@ void InterpreterCreateQuery::setEngine(ASTCreateQuery & create) const
BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create)
{
if (!create.cluster.empty())
{
NameSet databases{create.database};
if (!create.to_table.empty())
databases.emplace(create.to_database);
return executeDDLQueryOnCluster(query_ptr, context, std::move(databases));
}
/// Temporary tables are created out of databases.
if (create.temporary && !create.database.empty() && !create.is_live_view)
throw Exception("Temporary tables cannot be inside a database. You should not specify a database for a temporary table.",
@ -690,9 +678,6 @@ BlockIO InterpreterCreateQuery::fillTableIfNeeded(const ASTCreateQuery & create)
BlockIO InterpreterCreateQuery::createDictionary(ASTCreateQuery & create)
{
if (!create.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, {create.database});
String dictionary_name = create.table;
if (create.database.empty())
@ -730,7 +715,11 @@ BlockIO InterpreterCreateQuery::createDictionary(ASTCreateQuery & create)
BlockIO InterpreterCreateQuery::execute()
{
auto & create = query_ptr->as<ASTCreateQuery &>();
checkAccess(create);
if (!create.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccess());
context.checkAccess(getRequiredAccess());
ASTQueryWithOutput::resetOutputASTIfExist(create);
/// CREATE|ATTACH DATABASE
@ -743,43 +732,47 @@ BlockIO InterpreterCreateQuery::execute()
}
void InterpreterCreateQuery::checkAccess(const ASTCreateQuery & create)
AccessRightsElements InterpreterCreateQuery::getRequiredAccess() const
{
/// Internal queries (initiated by the server itself) always have access to everything.
if (internal)
return;
return {};
const Settings & settings = context.getSettingsRef();
auto readonly = settings.readonly;
auto allow_ddl = settings.allow_ddl;
AccessRightsElements required_access;
const auto & create = query_ptr->as<const ASTCreateQuery &>();
if (!readonly && allow_ddl)
return;
/// CREATE|ATTACH DATABASE
if (!create.database.empty() && create.table.empty())
if (create.table.empty())
{
if (readonly)
throw Exception("Cannot create database in readonly mode", ErrorCodes::READONLY);
throw Exception("Cannot create database. DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
required_access.emplace_back(AccessType::CREATE_DATABASE, create.database);
}
String object = "table";
if (create.is_dictionary)
else if (create.is_dictionary)
{
if (readonly)
throw Exception("Cannot create dictionary in readonly mode", ErrorCodes::READONLY);
object = "dictionary";
required_access.emplace_back(AccessType::CREATE_DICTIONARY, create.database, create.table);
}
else if (create.is_view || create.is_materialized_view || create.is_live_view)
{
if (create.temporary)
required_access.emplace_back(AccessType::CREATE_TEMPORARY_TABLE);
else
{
if (create.replace_view)
required_access.emplace_back(AccessType::DROP_VIEW | AccessType::CREATE_VIEW, create.database, create.table);
else
required_access.emplace_back(AccessType::CREATE_VIEW, create.database, create.table);
}
}
else
{
if (create.temporary)
required_access.emplace_back(AccessType::CREATE_TEMPORARY_TABLE);
else
required_access.emplace_back(AccessType::CREATE_TABLE, create.database, create.table);
}
if (create.temporary && readonly >= 2)
return;
if (!create.to_table.empty())
required_access.emplace_back(AccessType::INSERT, create.to_database, create.to_table);
if (readonly)
throw Exception("Cannot create table or dictionary in readonly mode", ErrorCodes::READONLY);
throw Exception("Cannot create " + object + ". DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
return required_access;
}
}

View File

@ -64,7 +64,7 @@ private:
TableProperties setProperties(ASTCreateQuery & create) const;
void validateTableStructure(const ASTCreateQuery & create, const TableProperties & properties) const;
void setEngine(ASTCreateQuery & create) const;
void checkAccess(const ASTCreateQuery & create);
AccessRightsElements getRequiredAccess() const;
/// Create IStorage and add it to database. If table already exists and IF NOT EXISTS specified, do nothing and return false.
bool doCreateTable(const ASTCreateQuery & create, const TableProperties & properties);

View File

@ -13,9 +13,9 @@ namespace DB
{
BlockIO InterpreterCreateQuotaQuery::execute()
{
context.checkQuotaManagementIsAllowed();
const auto & query = query_ptr->as<const ASTCreateQuotaQuery &>();
auto & access_control = context.getAccessControlManager();
context.checkAccess(query.alter ? AccessType::ALTER_QUOTA : AccessType::CREATE_QUOTA);
if (query.alter)
{

View File

@ -11,9 +11,9 @@ namespace DB
{
BlockIO InterpreterCreateRowPolicyQuery::execute()
{
context.checkRowPolicyManagementIsAllowed();
const auto & query = query_ptr->as<const ASTCreateRowPolicyQuery &>();
auto & access_control = context.getAccessControlManager();
context.checkAccess(query.alter ? AccessType::ALTER_POLICY : AccessType::CREATE_POLICY);
if (query.alter)
{

View File

@ -89,6 +89,9 @@ BlockInputStreamPtr InterpreterDescribeQuery::executeImpl()
String table_name;
std::tie(database_name, table_name) = IdentifierSemantic::extractDatabaseAndTable(identifier);
if (!database_name.empty() || !context.isExternalTableExist(table_name))
context.checkAccess(AccessType::SHOW, database_name, table_name);
table = context.getTable(database_name, table_name);
}

View File

@ -19,7 +19,7 @@ BlockIO InterpreterDropAccessEntityQuery::execute()
{
case Kind::QUOTA:
{
context.checkQuotaManagementIsAllowed();
context.checkAccess(AccessType::DROP_QUOTA);
if (query.if_exists)
access_control.tryRemove(access_control.find<Quota>(query.names));
else
@ -28,7 +28,7 @@ BlockIO InterpreterDropAccessEntityQuery::execute()
}
case Kind::ROW_POLICY:
{
context.checkRowPolicyManagementIsAllowed();
context.checkAccess(AccessType::DROP_POLICY);
Strings full_names;
boost::range::transform(
query.row_policies_names, std::back_inserter(full_names),

View File

@ -35,11 +35,8 @@ InterpreterDropQuery::InterpreterDropQuery(const ASTPtr & query_ptr_, Context &
BlockIO InterpreterDropQuery::execute()
{
auto & drop = query_ptr->as<ASTDropQuery &>();
checkAccess(drop);
if (!drop.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, {drop.database});
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccessForDDLOnCluster());
if (!drop.table.empty())
{
@ -56,8 +53,8 @@ BlockIO InterpreterDropQuery::execute()
BlockIO InterpreterDropQuery::executeToTable(
String & database_name_,
String & table_name,
const String & database_name_,
const String & table_name,
ASTDropQuery::Kind kind,
bool if_exists,
bool is_temporary,
@ -66,7 +63,6 @@ BlockIO InterpreterDropQuery::executeToTable(
if (is_temporary || database_name_.empty())
{
auto & session_context = context.hasSessionContext() ? context.getSessionContext() : context;
if (session_context.isExternalTableExist(table_name))
return executeToTemporaryTable(table_name, kind);
}
@ -74,7 +70,7 @@ BlockIO InterpreterDropQuery::executeToTable(
if (is_temporary)
{
if (if_exists)
return {};
return {};
throw Exception("Temporary table " + backQuoteIfNeed(table_name) + " doesn't exist.",
ErrorCodes::UNKNOWN_TABLE);
}
@ -83,41 +79,44 @@ BlockIO InterpreterDropQuery::executeToTable(
auto ddl_guard = (!no_ddl_lock ? context.getDDLGuard(database_name, table_name) : nullptr);
DatabaseAndTable database_and_table = tryGetDatabaseAndTable(database_name, table_name, if_exists);
auto [database, table] = tryGetDatabaseAndTable(database_name, table_name, if_exists);
if (database_and_table.first && database_and_table.second)
if (database && table)
{
auto table_id = database_and_table.second->getStorageID();
auto table_id = table->getStorageID();
if (kind == ASTDropQuery::Kind::Detach)
{
database_and_table.second->shutdown();
context.checkAccess(table->isView() ? AccessType::DETACH_VIEW : AccessType::DETACH_TABLE,
database_name, table_name);
table->shutdown();
/// If table was already dropped by anyone, an exception will be thrown
auto table_lock = database_and_table.second->lockExclusively(context.getCurrentQueryId());
auto table_lock = table->lockExclusively(context.getCurrentQueryId());
/// Drop table from memory, don't touch data and metadata
database_and_table.first->detachTable(table_id.table_name);
database->detachTable(table_name);
}
else if (kind == ASTDropQuery::Kind::Truncate)
{
database_and_table.second->checkTableCanBeDropped();
context.checkAccess(table->isView() ? AccessType::TRUNCATE_VIEW : AccessType::TRUNCATE_TABLE,
database_name, table_name);
table->checkTableCanBeDropped();
/// If table was already dropped by anyone, an exception will be thrown
auto table_lock = database_and_table.second->lockExclusively(context.getCurrentQueryId());
auto table_lock = table->lockExclusively(context.getCurrentQueryId());
/// Drop table data, don't touch metadata
database_and_table.second->truncate(query_ptr, context, table_lock);
table->truncate(query_ptr, context, table_lock);
}
else if (kind == ASTDropQuery::Kind::Drop)
{
database_and_table.second->checkTableCanBeDropped();
context.checkAccess(table->isView() ? AccessType::DROP_VIEW : AccessType::DROP_TABLE,
database_name, table_name);
table->checkTableCanBeDropped();
database_and_table.second->shutdown();
table->shutdown();
/// If table was already dropped by anyone, an exception will be thrown
auto table_lock = database_and_table.second->lockExclusively(context.getCurrentQueryId());
const std::string metadata_file_without_extension =
database_and_table.first->getMetadataPath()
+ escapeForFileName(table_id.table_name);
auto table_lock = table->lockExclusively(context.getCurrentQueryId());
const std::string metadata_file_without_extension = database->getMetadataPath() + escapeForFileName(table_id.table_name);
const auto prev_metadata_name = metadata_file_without_extension + ".sql";
const auto drop_metadata_name = metadata_file_without_extension + ".sql.tmp_drop";
@ -128,7 +127,7 @@ BlockIO InterpreterDropQuery::executeToTable(
if (Poco::File(prev_metadata_name).exists())
Poco::File(prev_metadata_name).renameTo(drop_metadata_name);
/// Delete table data
database_and_table.second->drop(table_lock);
table->drop(table_lock);
}
catch (...)
{
@ -137,11 +136,11 @@ BlockIO InterpreterDropQuery::executeToTable(
throw;
}
String table_data_path_relative = database_and_table.first->getTableDataPath(table_name);
String table_data_path_relative = database->getTableDataPath(table_name);
/// Delete table metadata and table itself from memory
database_and_table.first->removeTable(context, table_id.table_name);
database_and_table.second->is_dropped = true;
database->removeTable(context, table_name);
table->is_dropped = true;
/// If it is not virtual database like Dictionary then drop remaining data dir
if (!table_data_path_relative.empty())
@ -158,8 +157,8 @@ BlockIO InterpreterDropQuery::executeToTable(
BlockIO InterpreterDropQuery::executeToDictionary(
String & database_name_,
String & dictionary_name,
const String & database_name_,
const String & dictionary_name,
ASTDropQuery::Kind kind,
bool if_exists,
bool is_temporary,
@ -187,6 +186,7 @@ BlockIO InterpreterDropQuery::executeToDictionary(
if (kind == ASTDropQuery::Kind::Detach)
{
/// Drop dictionary from memory, don't touch data and metadata
context.checkAccess(AccessType::DETACH_DICTIONARY, database_name, dictionary_name);
database->detachDictionary(dictionary_name, context);
}
else if (kind == ASTDropQuery::Kind::Truncate)
@ -195,12 +195,13 @@ BlockIO InterpreterDropQuery::executeToDictionary(
}
else if (kind == ASTDropQuery::Kind::Drop)
{
context.checkAccess(AccessType::DROP_DICTIONARY, database_name, dictionary_name);
database->removeDictionary(context, dictionary_name);
}
return {};
}
BlockIO InterpreterDropQuery::executeToTemporaryTable(String & table_name, ASTDropQuery::Kind kind)
BlockIO InterpreterDropQuery::executeToTemporaryTable(const String & table_name, ASTDropQuery::Kind kind)
{
if (kind == ASTDropQuery::Kind::Detach)
throw Exception("Unable to detach temporary table.", ErrorCodes::SYNTAX_ERROR);
@ -233,7 +234,7 @@ BlockIO InterpreterDropQuery::executeToTemporaryTable(String & table_name, ASTDr
return {};
}
BlockIO InterpreterDropQuery::executeToDatabase(String & database_name, ASTDropQuery::Kind kind, bool if_exists)
BlockIO InterpreterDropQuery::executeToDatabase(const String & database_name, ASTDropQuery::Kind kind, bool if_exists)
{
auto ddl_guard = context.getDDLGuard(database_name, "");
@ -245,11 +246,14 @@ BlockIO InterpreterDropQuery::executeToDatabase(String & database_name, ASTDropQ
}
else if (kind == ASTDropQuery::Kind::Detach)
{
context.checkAccess(AccessType::DETACH_DATABASE, database_name);
context.detachDatabase(database_name);
database->shutdown();
}
else if (kind == ASTDropQuery::Kind::Drop)
{
context.checkAccess(AccessType::DROP_DATABASE, database_name);
for (auto iterator = database->getTablesIterator(context); iterator->isValid(); iterator->next())
{
String current_table_name = iterator->name();
@ -289,12 +293,12 @@ BlockIO InterpreterDropQuery::executeToDatabase(String & database_name, ASTDropQ
return {};
}
DatabasePtr InterpreterDropQuery::tryGetDatabase(String & database_name, bool if_exists)
DatabasePtr InterpreterDropQuery::tryGetDatabase(const String & database_name, bool if_exists)
{
return if_exists ? context.tryGetDatabase(database_name) : context.getDatabase(database_name);
}
DatabaseAndTable InterpreterDropQuery::tryGetDatabaseAndTable(String & database_name, String & table_name, bool if_exists)
DatabaseAndTable InterpreterDropQuery::tryGetDatabaseAndTable(const String & database_name, const String & table_name, bool if_exists)
{
DatabasePtr database = tryGetDatabase(database_name, if_exists);
@ -310,20 +314,38 @@ DatabaseAndTable InterpreterDropQuery::tryGetDatabaseAndTable(String & database_
return {};
}
void InterpreterDropQuery::checkAccess(const ASTDropQuery & drop)
AccessRightsElements InterpreterDropQuery::getRequiredAccessForDDLOnCluster() const
{
const Settings & settings = context.getSettingsRef();
auto readonly = settings.readonly;
bool allow_ddl = settings.allow_ddl;
AccessRightsElements required_access;
const auto & drop = query_ptr->as<const ASTDropQuery &>();
/// It's allowed to drop temporary tables.
if ((!readonly && allow_ddl) || (drop.database.empty() && context.tryGetExternalTable(drop.table) && readonly >= 2))
return;
if (drop.table.empty())
{
if (drop.kind == ASTDropQuery::Kind::Detach)
required_access.emplace_back(AccessType::DETACH_DATABASE, drop.database);
else if (drop.kind == ASTDropQuery::Kind::Drop)
required_access.emplace_back(AccessType::DROP_DATABASE, drop.database);
}
else if (drop.is_dictionary)
{
if (drop.kind == ASTDropQuery::Kind::Detach)
required_access.emplace_back(AccessType::DETACH_DICTIONARY, drop.database, drop.table);
else if (drop.kind == ASTDropQuery::Kind::Drop)
required_access.emplace_back(AccessType::DROP_DICTIONARY, drop.database, drop.table);
}
else if (!drop.temporary)
{
/// It can be view or table.
if (drop.kind == ASTDropQuery::Kind::Drop)
required_access.emplace_back(AccessType::DROP_TABLE | AccessType::DROP_VIEW, drop.database, drop.table);
else if (drop.kind == ASTDropQuery::Kind::Truncate)
required_access.emplace_back(AccessType::TRUNCATE_TABLE | AccessType::TRUNCATE_VIEW, drop.database, drop.table);
else if (drop.kind == ASTDropQuery::Kind::Detach)
required_access.emplace_back(AccessType::DETACH_TABLE | AccessType::DETACH_VIEW, drop.database, drop.table);
}
if (readonly)
throw Exception("Cannot drop table in readonly mode", ErrorCodes::READONLY);
throw Exception("Cannot drop table. DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
return required_access;
}
}

View File

@ -24,20 +24,20 @@ public:
BlockIO execute() override;
private:
void checkAccess(const ASTDropQuery & drop);
AccessRightsElements getRequiredAccessForDDLOnCluster() const;
ASTPtr query_ptr;
Context & context;
BlockIO executeToDatabase(String & database_name, ASTDropQuery::Kind kind, bool if_exists);
BlockIO executeToDatabase(const String & database_name, ASTDropQuery::Kind kind, bool if_exists);
BlockIO executeToTable(String & database_name, String & table_name, ASTDropQuery::Kind kind, bool if_exists, bool is_temporary, bool no_ddl_lock);
BlockIO executeToTable(const String & database_name, const String & table_name, ASTDropQuery::Kind kind, bool if_exists, bool is_temporary, bool no_ddl_lock);
BlockIO executeToDictionary(String & database_name, String & table_name, ASTDropQuery::Kind kind, bool if_exists, bool is_temporary, bool no_ddl_lock);
BlockIO executeToDictionary(const String & database_name, const String & table_name, ASTDropQuery::Kind kind, bool if_exists, bool is_temporary, bool no_ddl_lock);
DatabasePtr tryGetDatabase(String & database_name, bool exists);
DatabasePtr tryGetDatabase(const String & database_name, bool exists);
DatabaseAndTable tryGetDatabaseAndTable(String & database_name, String & table_name, bool if_exists);
DatabaseAndTable tryGetDatabaseAndTable(const String & database_name, const String & table_name, bool if_exists);
BlockIO executeToTemporaryTable(String & table_name, ASTDropQuery::Kind kind);
BlockIO executeToTemporaryTable(const String & table_name, ASTDropQuery::Kind kind);
};
}

View File

@ -42,14 +42,27 @@ BlockInputStreamPtr InterpreterExistsQuery::executeImpl()
if ((exists_query = query_ptr->as<ASTExistsTableQuery>()))
{
if (exists_query->temporary)
{
context.checkAccess(AccessType::EXISTS, "", exists_query->table);
result = context.isExternalTableExist(exists_query->table);
}
else
result = context.isTableExist(exists_query->database, exists_query->table);
{
String database = exists_query->database;
if (database.empty())
database = context.getCurrentDatabase();
context.checkAccess(AccessType::EXISTS, database, exists_query->table);
result = context.isTableExist(database, exists_query->table);
}
}
else if ((exists_query = query_ptr->as<ASTExistsDictionaryQuery>()))
{
if (exists_query->temporary)
throw Exception("Temporary dictionaries are not possible.", ErrorCodes::SYNTAX_ERROR);
String database = exists_query->database;
if (database.empty())
database = context.getCurrentDatabase();
context.checkAccess(AccessType::EXISTS, database, exists_query->table);
result = context.isDictionaryExists(exists_query->database, exists_query->table);
}

View File

@ -74,22 +74,6 @@ namespace ErrorCodes
}
static void throwIfNoAccess(Context & context)
{
if (context.getSettingsRef().readonly)
{
const auto & client_info = context.getClientInfo();
if (client_info.interface == ClientInfo::Interface::HTTP && client_info.http_method == ClientInfo::HTTPMethod::GET)
throw Exception("Cannot execute query in readonly mode. "
"For queries over HTTP, method GET implies readonly. You should use method POST for modifying queries.", ErrorCodes::READONLY);
else
throw Exception("Cannot execute query in readonly mode", ErrorCodes::READONLY);
}
else if (!context.getSettingsRef().allow_ddl)
throw Exception("Cannot execute query. DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
}
std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context & context, QueryProcessingStage::Enum stage)
{
ProfileEvents::increment(ProfileEvents::Query);
@ -108,23 +92,19 @@ std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context &
else if (query->as<ASTInsertQuery>())
{
ProfileEvents::increment(ProfileEvents::InsertQuery);
/// readonly is checked inside InterpreterInsertQuery
bool allow_materialized = static_cast<bool>(context.getSettingsRef().insert_allow_materialized_columns);
return std::make_unique<InterpreterInsertQuery>(query, context, allow_materialized);
}
else if (query->as<ASTCreateQuery>())
{
/// readonly and allow_ddl are checked inside InterpreterCreateQuery
return std::make_unique<InterpreterCreateQuery>(query, context);
}
else if (query->as<ASTDropQuery>())
{
/// readonly and allow_ddl are checked inside InterpreterDropQuery
return std::make_unique<InterpreterDropQuery>(query, context);
}
else if (query->as<ASTRenameQuery>())
{
throwIfNoAccess(context);
return std::make_unique<InterpreterRenameQuery>(query, context);
}
else if (query->as<ASTShowTablesQuery>())
@ -142,7 +122,6 @@ std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context &
}
else if (query->as<ASTOptimizeQuery>())
{
throwIfNoAccess(context);
return std::make_unique<InterpreterOptimizeQuery>(query, context);
}
else if (query->as<ASTExistsTableQuery>())
@ -179,7 +158,6 @@ std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context &
}
else if (query->as<ASTAlterQuery>())
{
throwIfNoAccess(context);
return std::make_unique<InterpreterAlterQuery>(query, context);
}
else if (query->as<ASTCheckQuery>())
@ -192,7 +170,6 @@ std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context &
}
else if (query->as<ASTSystemQuery>())
{
throwIfNoAccess(context);
return std::make_unique<InterpreterSystemQuery>(query, context);
}
else if (query->as<ASTWatchQuery>())

View File

@ -46,7 +46,7 @@ InterpreterInsertQuery::InterpreterInsertQuery(
}
StoragePtr InterpreterInsertQuery::getTable(const ASTInsertQuery & query)
StoragePtr InterpreterInsertQuery::getTable(ASTInsertQuery & query)
{
if (query.table_function)
{
@ -57,6 +57,8 @@ StoragePtr InterpreterInsertQuery::getTable(const ASTInsertQuery & query)
}
/// Into what table to write.
if (query.database.empty() && !context.isExternalTableExist(query.table))
query.database = context.getCurrentDatabase();
return context.getTable(query.database, query.table);
}
@ -97,15 +99,17 @@ Block InterpreterInsertQuery::getSampleBlock(const ASTInsertQuery & query, const
BlockIO InterpreterInsertQuery::execute()
{
const Settings & settings = context.getSettingsRef();
const auto & query = query_ptr->as<ASTInsertQuery &>();
checkAccess(query);
auto & query = query_ptr->as<ASTInsertQuery &>();
BlockIO res;
StoragePtr table = getTable(query);
StoragePtr table = getTable(query);
auto table_lock = table->lockStructureForShare(true, context.getInitialQueryId());
auto query_sample_block = getSampleBlock(query, table);
if (!query.table.empty() && !query.database.empty() /* always allow access to temporary tables */)
context.checkAccess(AccessType::INSERT, query.database, query.table, query_sample_block.getNames());
BlockInputStreams in_streams;
size_t out_streams_size = 1;
if (query.select)
@ -128,7 +132,6 @@ BlockIO InterpreterInsertQuery::execute()
}
BlockOutputStreams out_streams;
auto query_sample_block = getSampleBlock(query, table);
for (size_t i = 0; i < out_streams_size; i++)
{
@ -204,19 +207,6 @@ BlockIO InterpreterInsertQuery::execute()
}
void InterpreterInsertQuery::checkAccess(const ASTInsertQuery & query)
{
const Settings & settings = context.getSettingsRef();
auto readonly = settings.readonly;
if (!readonly || (query.database.empty() && context.tryGetExternalTable(query.table) && readonly >= 2))
{
return;
}
throw Exception("Cannot insert into table in readonly mode", ErrorCodes::READONLY);
}
std::pair<String, String> InterpreterInsertQuery::getDatabaseTable() const
{
const auto & query = query_ptr->as<ASTInsertQuery &>();

View File

@ -32,9 +32,8 @@ public:
std::pair<String, String> getDatabaseTable() const;
private:
StoragePtr getTable(const ASTInsertQuery & query);
StoragePtr getTable(ASTInsertQuery & query);
Block getSampleBlock(const ASTInsertQuery & query, const StoragePtr & table);
void checkAccess(const ASTInsertQuery & query);
ASTPtr query_ptr;
const Context & context;

View File

@ -6,6 +6,7 @@
#include <Interpreters/ProcessList.h>
#include <Interpreters/executeQuery.h>
#include <Interpreters/CancellationCode.h>
#include <Access/AccessRightsContext.h>
#include <Columns/ColumnString.h>
#include <Common/typeid_cast.h>
#include <DataTypes/DataTypeString.h>
@ -13,6 +14,7 @@
#include <DataTypes/DataTypesNumber.h>
#include <DataStreams/OneBlockInputStream.h>
#include <Storages/IStorage.h>
#include <Common/quoteString.h>
#include <thread>
#include <iostream>
#include <cstddef>
@ -26,6 +28,7 @@ namespace ErrorCodes
extern const int READONLY;
extern const int LOGICAL_ERROR;
extern const int CANNOT_KILL;
extern const int ACCESS_DENIED;
}
@ -78,24 +81,32 @@ static QueryDescriptors extractQueriesExceptMeAndCheckAccess(const Block & proce
const ColumnString & query_id_col = typeid_cast<const ColumnString &>(*processes_block.getByName("query_id").column);
const ColumnString & user_col = typeid_cast<const ColumnString &>(*processes_block.getByName("user").column);
const ClientInfo & my_client = context.getProcessListElement()->getClientInfo();
std::optional<bool> can_kill_query_started_by_another_user;
String query_user;
for (size_t i = 0; i < num_processes; ++i)
{
auto query_id = query_id_col.getDataAt(i).toString();
auto user = user_col.getDataAt(i).toString();
if (my_client.current_query_id == query_id && my_client.current_user == user)
if ((my_client.current_query_id == query_id_col.getDataAt(i).toString())
&& (my_client.current_user == user_col.getDataAt(i).toString()))
continue;
if (context.getSettingsRef().readonly && my_client.current_user != user)
auto query_id = query_id_col.getDataAt(i).toString();
query_user = user_col.getDataAt(i).toString();
if (my_client.current_user != query_user)
{
throw Exception("Readonly user " + my_client.current_user + " attempts to kill query created by " + user,
ErrorCodes::READONLY);
if (!can_kill_query_started_by_another_user)
can_kill_query_started_by_another_user = context.getAccessRights()->isGranted(&Poco::Logger::get("InterpreterKillQueryQuery"), AccessType::KILL_QUERY);
if (!can_kill_query_started_by_another_user.value())
continue;
}
res.emplace_back(std::move(query_id), std::move(user), i, false);
res.emplace_back(std::move(query_id), std::move(query_user), i, false);
}
if (res.empty() && !query_user.empty())
throw Exception("User " + my_client.current_user + " attempts to kill query created by " + query_user, ErrorCodes::ACCESS_DENIED);
return res;
}
@ -175,7 +186,7 @@ BlockIO InterpreterKillQueryQuery::execute()
const auto & query = query_ptr->as<ASTKillQueryQuery &>();
if (!query.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, {"system"});
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccessForDDLOnCluster());
BlockIO res_io;
switch (query.type)
@ -195,7 +206,6 @@ BlockIO InterpreterKillQueryQuery::execute()
if (!query.sync || query.test)
{
MutableColumns res_columns = header.cloneEmptyColumns();
for (const auto & query_desc : queries_to_stop)
{
auto code = (query.test) ? CancellationCode::Unknown : process_list.sendCancelToQuery(query_desc.query_id, query_desc.user, true);
@ -214,9 +224,6 @@ BlockIO InterpreterKillQueryQuery::execute()
}
case ASTKillQueryQuery::Type::Mutation:
{
if (context.getSettingsRef().readonly)
throw Exception("Cannot execute query in readonly mode", ErrorCodes::READONLY);
Block mutations_block = getSelectResult("database, table, mutation_id", "system.mutations");
if (!mutations_block)
return res_io;
@ -229,11 +236,12 @@ BlockIO InterpreterKillQueryQuery::execute()
header.insert(0, {ColumnString::create(), std::make_shared<DataTypeString>(), "kill_status"});
MutableColumns res_columns = header.cloneEmptyColumns();
String database_name, table_name;
for (size_t i = 0; i < mutations_block.rows(); ++i)
{
auto database_name = database_col.getDataAt(i).toString();
auto table_name = table_col.getDataAt(i).toString();
database_name = database_col.getDataAt(i).toString();
table_name = table_col.getDataAt(i).toString();
auto mutation_id = mutation_id_col.getDataAt(i).toString();
CancellationCode code = CancellationCode::Unknown;
@ -243,12 +251,21 @@ BlockIO InterpreterKillQueryQuery::execute()
if (!storage)
code = CancellationCode::NotFound;
else
{
if (!context.getAccessRights()->isGranted(&Poco::Logger::get("InterpreterKillQueryQuery"), AccessType::KILL_MUTATION, database_name, table_name))
continue;
code = storage->killMutation(mutation_id);
}
}
insertResultRow(i, code, mutations_block, header, res_columns);
}
if (res_columns[0]->empty() && !table_name.empty())
throw Exception(
"Not allowed to kill mutation on " + backQuoteIfNeed(database_name) + "." + backQuoteIfNeed(table_name),
ErrorCodes::ACCESS_DENIED);
res_io.in = std::make_shared<OneBlockInputStream>(header.cloneWithColumns(std::move(res_columns)));
break;
@ -265,7 +282,7 @@ Block InterpreterKillQueryQuery::getSelectResult(const String & columns, const S
if (where_expression)
select_query += " WHERE " + queryToString(where_expression);
BlockIO block_io = executeQuery(select_query, context, true, QueryProcessingStage::Complete, false, false);
BlockIO block_io = executeQuery(select_query, context.getGlobalContext(), true, QueryProcessingStage::Complete, false, false);
Block res = block_io.in->read();
if (res && block_io.in->read())
@ -274,4 +291,16 @@ Block InterpreterKillQueryQuery::getSelectResult(const String & columns, const S
return res;
}
AccessRightsElements InterpreterKillQueryQuery::getRequiredAccessForDDLOnCluster() const
{
const auto & query = query_ptr->as<ASTKillQueryQuery &>();
AccessRightsElements required_access;
if (query.type == ASTKillQueryQuery::Type::Query)
required_access.emplace_back(AccessType::KILL_QUERY);
else if (query.type == ASTKillQueryQuery::Type::Mutation)
required_access.emplace_back(AccessType::KILL_MUTATION);
return required_access;
}
}

View File

@ -9,6 +9,7 @@ namespace DB
{
class Context;
class AccessRightsElements;
class InterpreterKillQueryQuery : public IInterpreter
@ -20,6 +21,7 @@ public:
BlockIO execute() override;
private:
AccessRightsElements getRequiredAccessForDDLOnCluster() const;
Block getSelectResult(const String & columns, const String & table);
ASTPtr query_ptr;

View File

@ -20,11 +20,22 @@ BlockIO InterpreterOptimizeQuery::execute()
const auto & ast = query_ptr->as<ASTOptimizeQuery &>();
if (!ast.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, {ast.database});
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccess());
context.checkAccess(getRequiredAccess());
StoragePtr table = context.getTable(ast.database, ast.table);
table->optimize(query_ptr, ast.partition, ast.final, ast.deduplicate, context);
return {};
}
AccessRightsElements InterpreterOptimizeQuery::getRequiredAccess() const
{
const auto & optimize = query_ptr->as<const ASTOptimizeQuery &>();
AccessRightsElements required_access;
required_access.emplace_back(AccessType::OPTIMIZE, optimize.database, optimize.table);
return required_access;
}
}

View File

@ -6,8 +6,8 @@
namespace DB
{
class Context;
class AccessRightsElements;
/** Just call method "optimize" for table.
@ -23,9 +23,10 @@ public:
BlockIO execute() override;
private:
AccessRightsElements getRequiredAccess() const;
ASTPtr query_ptr;
Context & context;
};
}

View File

@ -36,19 +36,12 @@ struct RenameDescription
BlockIO InterpreterRenameQuery::execute()
{
const auto & rename = query_ptr->as<ASTRenameQuery &>();
const auto & rename = query_ptr->as<const ASTRenameQuery &>();
if (!rename.cluster.empty())
{
NameSet databases;
for (const auto & elem : rename.elements)
{
databases.emplace(elem.from.database);
databases.emplace(elem.to.database);
}
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccess());
return executeDDLQueryOnCluster(query_ptr, context, std::move(databases));
}
context.checkAccess(getRequiredAccess());
String path = context.getPath();
String current_database = context.getCurrentDatabase();
@ -81,9 +74,10 @@ BlockIO InterpreterRenameQuery::execute()
for (const auto & elem : rename.elements)
{
descriptions.emplace_back(elem, current_database);
const auto & description = descriptions.back();
UniqueTableName from(descriptions.back().from_database_name, descriptions.back().from_table_name);
UniqueTableName to(descriptions.back().to_database_name, descriptions.back().to_table_name);
UniqueTableName from(description.from_database_name, description.from_table_name);
UniqueTableName to(description.to_database_name, description.to_table_name);
table_guards[from];
table_guards[to];
@ -110,5 +104,16 @@ BlockIO InterpreterRenameQuery::execute()
return {};
}
AccessRightsElements InterpreterRenameQuery::getRequiredAccess() const
{
AccessRightsElements required_access;
const auto & rename = query_ptr->as<const ASTRenameQuery &>();
for (const auto & elem : rename.elements)
{
required_access.emplace_back(AccessType::SELECT | AccessType::DROP_TABLE, elem.from.database, elem.from.table);
required_access.emplace_back(AccessType::CREATE_TABLE | AccessType::INSERT, elem.to.database, elem.to.table);
}
return required_access;
}
}

View File

@ -8,6 +8,7 @@ namespace DB
{
class Context;
class AccessRightsElements;
/** Rename one table
@ -20,9 +21,10 @@ public:
BlockIO execute() override;
private:
AccessRightsElements getRequiredAccess() const;
ASTPtr query_ptr;
Context & context;
};
}

View File

@ -258,6 +258,7 @@ InterpreterSelectQuery::InterpreterSelectQuery(
auto & query = getSelectQuery();
ASTPtr table_expression = extractTableExpression(query, 0);
String database_name, table_name;
bool is_table_func = false;
bool is_subquery = false;
@ -295,9 +296,6 @@ InterpreterSelectQuery::InterpreterSelectQuery(
}
else
{
String database_name;
String table_name;
getDatabaseAndTableNames(query, database_name, table_name, *context);
if (auto view_source = context->getViewSource())
@ -433,21 +431,20 @@ InterpreterSelectQuery::InterpreterSelectQuery(
if (query.prewhere() && !query.where())
analysis_result.prewhere_info->need_filter = true;
if (!table_name.empty() && !database_name.empty() /* always allow access to temporary tables */)
context->checkAccess(AccessType::SELECT, database_name, table_name, required_columns);
/// Remove limits for some tables in the `system` database.
if (database_name == "system" && ((table_name == "quotas") || (table_name == "quota_usage") || (table_name == "one")))
{
options.ignore_quota = true;
options.ignore_limits = true;
}
/// Blocks used in expression analysis contains size 1 const columns for constant folding and
/// null non-const columns to avoid useless memory allocations. However, a valid block sample
/// requires all columns to be of size 0, thus we need to sanitize the block here.
sanitizeBlock(result_header);
/// Remove limits for some tables in the `system` database.
if (storage && (table_id.getDatabaseName() == "system"))
{
String table_name = table_id.getTableName();
if ((table_name == "quotas") || (table_name == "quota_usage") || (table_name == "one"))
{
options.ignore_quota = true;
options.ignore_limits = true;
}
}
}
@ -459,7 +456,7 @@ void InterpreterSelectQuery::getDatabaseAndTableNames(const ASTSelectQuery & que
database_name = db_and_table->database;
/// If the database is not specified - use the current database.
if (database_name.empty() && !context.tryGetTable("", table_name))
if (database_name.empty() && !context.isExternalTableExist(table_name))
database_name = context.getCurrentDatabase();
}
else /// If the table is not specified - use the table `system.one`.

View File

@ -49,18 +49,23 @@ BlockInputStreamPtr InterpreterShowCreateQuery::executeImpl()
if (show_query->temporary)
create_query = context.getCreateExternalTableQuery(show_query->table);
else
{
context.checkAccess(AccessType::SHOW, show_query->database, show_query->table);
create_query = context.getDatabase(show_query->database)->getCreateTableQuery(context, show_query->table);
}
}
else if ((show_query = query_ptr->as<ASTShowCreateDatabaseQuery>()))
{
if (show_query->temporary)
throw Exception("Temporary databases are not possible.", ErrorCodes::SYNTAX_ERROR);
context.checkAccess(AccessType::SHOW, show_query->database);
create_query = context.getDatabase(show_query->database)->getCreateDatabaseQuery(context);
}
else if ((show_query = query_ptr->as<ASTShowCreateDictionaryQuery>()))
{
if (show_query->temporary)
throw Exception("Temporary dictionaries are not possible.", ErrorCodes::SYNTAX_ERROR);
context.checkAccess(AccessType::SHOW, show_query->database, show_query->table);
create_query = context.getDatabase(show_query->database)->getCreateDictionaryQuery(context, show_query->table);
}

View File

@ -19,6 +19,7 @@
#include <Interpreters/TraceLog.h>
#include <Interpreters/TextLog.h>
#include <Interpreters/MetricLog.h>
#include <Access/AccessRightsContext.h>
#include <Databases/IDatabase.h>
#include <Storages/StorageDistributed.h>
#include <Storages/StorageReplicatedMergeTree.h>
@ -96,8 +97,29 @@ void executeCommandsAndThrowIfError(Callables && ... commands)
}
AccessType getRequiredAccessType(StorageActionBlockType action_type)
{
if (action_type == ActionLocks::PartsMerge)
return AccessType::STOP_MERGES;
else if (action_type == ActionLocks::PartsFetch)
return AccessType::STOP_FETCHES;
else if (action_type == ActionLocks::PartsSend)
return AccessType::STOP_REPLICATED_SENDS;
else if (action_type == ActionLocks::ReplicationQueue)
return AccessType::STOP_REPLICATION_QUEUES;
else if (action_type == ActionLocks::DistributedSend)
return AccessType::STOP_DISTRIBUTED_SENDS;
else if (action_type == ActionLocks::PartsTTLMerge)
return AccessType::STOP_TTL_MERGES;
else if (action_type == ActionLocks::PartsMove)
return AccessType::STOP_MOVES;
else
throw Exception("Unknown action type: " + std::to_string(action_type), ErrorCodes::LOGICAL_ERROR);
}
/// Implements SYSTEM [START|STOP] <something action from ActionLocks>
void startStopAction(Context & context, ASTSystemQuery & query, StorageActionBlockType action_type, bool start)
void startStopAction(Context & context, Poco::Logger * log, ASTSystemQuery & query, StorageActionBlockType action_type, bool start)
{
auto manager = context.getActionLocksManager();
manager->cleanExpired();
@ -105,7 +127,7 @@ void startStopAction(Context & context, ASTSystemQuery & query, StorageActionBlo
if (!query.table.empty())
{
String database = !query.database.empty() ? query.database : context.getCurrentDatabase();
context.checkAccess(getRequiredAccessType(action_type), database, query.table);
if (start)
manager->remove(database, query.table, action_type);
else
@ -113,12 +135,22 @@ void startStopAction(Context & context, ASTSystemQuery & query, StorageActionBlo
}
else
{
if (start)
manager->remove(action_type);
else
manager->add(action_type);
for (auto & elem : context.getDatabases())
{
for (auto iterator = elem.second->getTablesIterator(context); iterator->isValid(); iterator->next())
{
if (context.getAccessRights()->isGranted(log, getRequiredAccessType(action_type), elem.first, iterator->name()))
{
if (start)
manager->remove(iterator->table(), action_type);
else
manager->add(iterator->table(), action_type);
}
}
}
}
}
}
@ -133,7 +165,7 @@ BlockIO InterpreterSystemQuery::execute()
auto & query = query_ptr->as<ASTSystemQuery &>();
if (!query.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, {query.database});
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccessForDDLOnCluster());
using Type = ASTSystemQuery::Type;
@ -151,85 +183,95 @@ BlockIO InterpreterSystemQuery::execute()
switch (query.type)
{
case Type::SHUTDOWN:
context.checkAccess(AccessType::SHUTDOWN);
if (kill(0, SIGTERM))
throwFromErrno("System call kill(0, SIGTERM) failed", ErrorCodes::CANNOT_KILL);
break;
case Type::KILL:
context.checkAccess(AccessType::SHUTDOWN);
if (kill(0, SIGKILL))
throwFromErrno("System call kill(0, SIGKILL) failed", ErrorCodes::CANNOT_KILL);
break;
case Type::DROP_DNS_CACHE:
context.checkAccess(AccessType::DROP_CACHE);
DNSResolver::instance().dropCache();
/// Reinitialize clusters to update their resolved_addresses
system_context.reloadClusterConfig();
break;
case Type::DROP_MARK_CACHE:
context.checkAccess(AccessType::DROP_CACHE);
system_context.dropMarkCache();
break;
case Type::DROP_UNCOMPRESSED_CACHE:
context.checkAccess(AccessType::DROP_CACHE);
system_context.dropUncompressedCache();
break;
#if USE_EMBEDDED_COMPILER
case Type::DROP_COMPILED_EXPRESSION_CACHE:
context.checkAccess(AccessType::DROP_CACHE);
system_context.dropCompiledExpressionCache();
break;
#endif
case Type::RELOAD_DICTIONARY:
context.checkAccess(AccessType::RELOAD_DICTIONARY);
system_context.getExternalDictionariesLoader().loadOrReload(query.target_dictionary);
break;
case Type::RELOAD_DICTIONARIES:
context.checkAccess(AccessType::RELOAD_DICTIONARY);
executeCommandsAndThrowIfError(
[&] () { system_context.getExternalDictionariesLoader().reloadAllTriedToLoad(); },
[&] () { system_context.getEmbeddedDictionaries().reload(); }
);
break;
case Type::RELOAD_EMBEDDED_DICTIONARIES:
context.checkAccess(AccessType::RELOAD_DICTIONARY);
system_context.getEmbeddedDictionaries().reload();
break;
case Type::RELOAD_CONFIG:
context.checkAccess(AccessType::RELOAD_CONFIG);
system_context.reloadConfig();
break;
case Type::STOP_MERGES:
startStopAction(context, query, ActionLocks::PartsMerge, false);
startStopAction(context, log, query, ActionLocks::PartsMerge, false);
break;
case Type::START_MERGES:
startStopAction(context, query, ActionLocks::PartsMerge, true);
startStopAction(context, log, query, ActionLocks::PartsMerge, true);
break;
case Type::STOP_TTL_MERGES:
startStopAction(context, query, ActionLocks::PartsTTLMerge, false);
startStopAction(context, log, query, ActionLocks::PartsTTLMerge, false);
break;
case Type::START_TTL_MERGES:
startStopAction(context, query, ActionLocks::PartsTTLMerge, true);
startStopAction(context, log, query, ActionLocks::PartsTTLMerge, true);
break;
case Type::STOP_MOVES:
startStopAction(context, query, ActionLocks::PartsMove, false);
startStopAction(context, log, query, ActionLocks::PartsMove, false);
break;
case Type::START_MOVES:
startStopAction(context, query, ActionLocks::PartsMove, true);
startStopAction(context, log, query, ActionLocks::PartsMove, true);
break;
case Type::STOP_FETCHES:
startStopAction(context, query, ActionLocks::PartsFetch, false);
startStopAction(context, log, query, ActionLocks::PartsFetch, false);
break;
case Type::START_FETCHES:
startStopAction(context, query, ActionLocks::PartsFetch, true);
startStopAction(context, log, query, ActionLocks::PartsFetch, true);
break;
case Type::STOP_REPLICATED_SENDS:
startStopAction(context, query, ActionLocks::PartsSend, false);
startStopAction(context, log, query, ActionLocks::PartsSend, false);
break;
case Type::START_REPLICATED_SENDS:
startStopAction(context, query, ActionLocks::PartsSend, true);
startStopAction(context, log, query, ActionLocks::PartsSend, true);
break;
case Type::STOP_REPLICATION_QUEUES:
startStopAction(context, query, ActionLocks::ReplicationQueue, false);
startStopAction(context, log, query, ActionLocks::ReplicationQueue, false);
break;
case Type::START_REPLICATION_QUEUES:
startStopAction(context, query, ActionLocks::ReplicationQueue, true);
startStopAction(context, log, query, ActionLocks::ReplicationQueue, true);
break;
case Type::STOP_DISTRIBUTED_SENDS:
startStopAction(context, query, ActionLocks::DistributedSend, false);
startStopAction(context, log, query, ActionLocks::DistributedSend, false);
break;
case Type::START_DISTRIBUTED_SENDS:
startStopAction(context, query, ActionLocks::DistributedSend, true);
startStopAction(context, log, query, ActionLocks::DistributedSend, true);
break;
case Type::SYNC_REPLICA:
syncReplica(query);
@ -246,6 +288,7 @@ BlockIO InterpreterSystemQuery::execute()
ErrorCodes::BAD_ARGUMENTS);
break;
case Type::FLUSH_LOGS:
context.checkAccess(AccessType::FLUSH_LOGS);
executeCommandsAndThrowIfError(
[&] () { if (auto query_log = context.getQueryLog()) query_log->flush(); },
[&] () { if (auto part_log = context.getPartLog("")) part_log->flush(); },
@ -268,7 +311,9 @@ BlockIO InterpreterSystemQuery::execute()
StoragePtr InterpreterSystemQuery::tryRestartReplica(const String & database_name, const String & table_name, Context & system_context)
{
context.checkAccess(AccessType::RESTART_REPLICA, database_name, table_name);
auto database = system_context.getDatabase(database_name);
auto table_ddl_guard = system_context.getDDLGuard(database_name, table_name);
ASTPtr create_ast;
@ -342,6 +387,7 @@ void InterpreterSystemQuery::syncReplica(ASTSystemQuery & query)
String database_name = !query.database.empty() ? query.database : context.getCurrentDatabase();
const String & table_name = query.table;
context.checkAccess(AccessType::SYNC_REPLICA, database_name, table_name);
StoragePtr table = context.getTable(database_name, table_name);
if (auto storage_replicated = dynamic_cast<StorageReplicatedMergeTree *>(table.get()))
@ -364,6 +410,7 @@ void InterpreterSystemQuery::flushDistributed(ASTSystemQuery & query)
{
String database_name = !query.database.empty() ? query.database : context.getCurrentDatabase();
String & table_name = query.table;
context.checkAccess(AccessType::FLUSH_DISTRIBUTED, database_name, table_name);
if (auto storage_distributed = dynamic_cast<StorageDistributed *>(context.getTable(database_name, table_name).get()))
storage_distributed->flushClusterNodesAllData();
@ -372,4 +419,135 @@ void InterpreterSystemQuery::flushDistributed(ASTSystemQuery & query)
}
AccessRightsElements InterpreterSystemQuery::getRequiredAccessForDDLOnCluster() const
{
const auto & query = query_ptr->as<const ASTSystemQuery &>();
using Type = ASTSystemQuery::Type;
AccessRightsElements required_access;
switch (query.type)
{
case Type::SHUTDOWN: [[fallthrough]];
case Type::KILL:
{
required_access.emplace_back(AccessType::SHUTDOWN);
break;
}
case Type::DROP_DNS_CACHE: [[fallthrough]];
case Type::DROP_MARK_CACHE: [[fallthrough]];
#if USE_EMBEDDED_COMPILER
case Type::DROP_COMPILED_EXPRESSION_CACHE: [[fallthrough]];
#endif
case Type::DROP_UNCOMPRESSED_CACHE:
{
required_access.emplace_back(AccessType::DROP_CACHE);
break;
}
case Type::RELOAD_DICTIONARY: [[fallthrough]];
case Type::RELOAD_DICTIONARIES: [[fallthrough]];
case Type::RELOAD_EMBEDDED_DICTIONARIES:
{
required_access.emplace_back(AccessType::RELOAD_DICTIONARY);
break;
}
case Type::RELOAD_CONFIG:
{
required_access.emplace_back(AccessType::RELOAD_CONFIG);
break;
}
case Type::STOP_MERGES: [[fallthrough]];
case Type::START_MERGES:
{
if (query.table.empty())
required_access.emplace_back(AccessType::STOP_MERGES);
else
required_access.emplace_back(AccessType::STOP_MERGES, query.database, query.table);
break;
}
case Type::STOP_TTL_MERGES: [[fallthrough]];
case Type::START_TTL_MERGES:
{
if (query.table.empty())
required_access.emplace_back(AccessType::STOP_TTL_MERGES);
else
required_access.emplace_back(AccessType::STOP_TTL_MERGES, query.database, query.table);
break;
}
case Type::STOP_MOVES: [[fallthrough]];
case Type::START_MOVES:
{
if (query.table.empty())
required_access.emplace_back(AccessType::STOP_MOVES);
else
required_access.emplace_back(AccessType::STOP_MOVES, query.database, query.table);
break;
}
case Type::STOP_FETCHES: [[fallthrough]];
case Type::START_FETCHES:
{
if (query.table.empty())
required_access.emplace_back(AccessType::STOP_FETCHES);
else
required_access.emplace_back(AccessType::STOP_FETCHES, query.database, query.table);
break;
}
case Type::STOP_DISTRIBUTED_SENDS: [[fallthrough]];
case Type::START_DISTRIBUTED_SENDS:
{
if (query.table.empty())
required_access.emplace_back(AccessType::STOP_DISTRIBUTED_SENDS);
else
required_access.emplace_back(AccessType::STOP_DISTRIBUTED_SENDS, query.database, query.table);
break;
}
case Type::STOP_REPLICATED_SENDS: [[fallthrough]];
case Type::START_REPLICATED_SENDS:
{
if (query.table.empty())
required_access.emplace_back(AccessType::STOP_REPLICATED_SENDS);
else
required_access.emplace_back(AccessType::STOP_REPLICATED_SENDS, query.database, query.table);
break;
}
case Type::STOP_REPLICATION_QUEUES: [[fallthrough]];
case Type::START_REPLICATION_QUEUES:
{
if (query.table.empty())
required_access.emplace_back(AccessType::STOP_REPLICATION_QUEUES);
else
required_access.emplace_back(AccessType::STOP_REPLICATION_QUEUES, query.database, query.table);
break;
}
case Type::SYNC_REPLICA:
{
required_access.emplace_back(AccessType::SYNC_REPLICA, query.database, query.table);
break;
}
case Type::RESTART_REPLICA:
{
required_access.emplace_back(AccessType::RESTART_REPLICA, query.database, query.table);
break;
}
case Type::RESTART_REPLICAS:
{
required_access.emplace_back(AccessType::RESTART_REPLICA);
break;
}
case Type::FLUSH_DISTRIBUTED:
{
required_access.emplace_back(AccessType::FLUSH_DISTRIBUTED, query.database, query.table);
break;
}
case Type::FLUSH_LOGS:
{
required_access.emplace_back(AccessType::FLUSH_LOGS);
break;
}
case Type::STOP_LISTEN_QUERIES: break;
case Type::START_LISTEN_QUERIES: break;
case Type::UNKNOWN: break;
case Type::END: break;
}
return required_access;
}
}

View File

@ -11,6 +11,7 @@ namespace DB
{
class Context;
class AccessRightsElements;
class ASTSystemQuery;
class InterpreterSystemQuery : public IInterpreter
@ -35,6 +36,8 @@ private:
void restartReplicas(Context & system_context);
void syncReplica(ASTSystemQuery & query);
void flushDistributed(ASTSystemQuery & query);
AccessRightsElements getRequiredAccessForDDLOnCluster() const;
};

View File

@ -10,6 +10,7 @@ namespace DB
BlockIO InterpreterUseQuery::execute()
{
const String & new_database = query_ptr->as<ASTUseQuery &>().database;
context.checkAccess(AccessType::EXISTS, new_database);
context.getSessionContext().setCurrentDatabase(new_database);
return {};
}

View File

@ -61,6 +61,7 @@ BlockIO InterpreterWatchQuery::execute()
/// List of columns to read to execute the query.
Names required_columns = storage->getColumns().getNamesOfPhysical();
context.checkAccess(AccessType::SELECT, database, table, required_columns);
/// Get context settings for this query
const Settings & settings = context.getSettingsRef();

View File

@ -4,6 +4,7 @@
#include <Common/Exception.h>
#include <IO/ReadHelpers.h>
#include <Interpreters/Users.h>
#include <Dictionaries/IDictionary.h>
#include <common/logger_useful.h>
#include <Poco/MD5Engine.h>
@ -113,6 +114,15 @@ User::User(const String & name_, const String & config_elem, const Poco::Util::A
access.grant(AccessFlags::databaseLevel(), "system"); /// Anyone has access to the "system" database.
}
if (dictionaries)
{
access.fullRevoke(AccessType::dictGet, IDictionary::NO_DATABASE_TAG);
for (const String & dictionary : *dictionaries)
access.grant(AccessType::dictGet, IDictionary::NO_DATABASE_TAG, dictionary);
}
else if (databases)
access.grant(AccessType::dictGet, IDictionary::NO_DATABASE_TAG);
if (config.has(config_elem + ".allow_quota_management"))
is_quota_management_allowed = config.getBool(config_elem + ".allow_quota_management");
if (config.has(config_elem + ".allow_row_policy_management"))

View File

@ -93,6 +93,9 @@ public:
/// Returns true if the storage receives data from a remote server or servers.
virtual bool isRemote() const { return false; }
/// Returns true if the storage is a view of a table or another view.
virtual bool isView() const { return false; }
/// Returns true if the storage supports queries with the SAMPLE section.
virtual bool supportsSampling() const { return false; }

View File

@ -48,6 +48,7 @@ friend class LiveViewBlockOutputStream;
public:
~StorageLiveView() override;
String getName() const override { return "LiveView"; }
bool isView() const override { return true; }
StorageID getSelectTableID() const { return select_table_id; }
StorageID getBlocksStorageID() const
{

View File

@ -17,6 +17,7 @@ class StorageMaterializedView : public ext::shared_ptr_helper<StorageMaterialize
friend struct ext::shared_ptr_helper<StorageMaterializedView>;
public:
std::string getName() const override { return "MaterializedView"; }
bool isView() const override { return true; }
ASTPtr getSelectQuery() const { return select->clone(); }
ASTPtr getInnerQuery() const { return inner_query->clone(); }

View File

@ -15,6 +15,7 @@ class StorageView : public ext::shared_ptr_helper<StorageView>, public IStorage
friend struct ext::shared_ptr_helper<StorageView>;
public:
std::string getName() const override { return "View"; }
bool isView() const override { return true; }
/// It is passed inside the query and solved at its level.
bool supportsSampling() const override { return true; }

View File

@ -9,6 +9,7 @@
#include <Storages/VirtualColumnUtils.h>
#include <Parsers/queryToString.h>
#include <Parsers/ASTSelectQuery.h>
#include <Access/AccessRightsContext.h>
#include <Databases/IDatabase.h>
#include <Processors/Sources/NullSource.h>
@ -62,11 +63,12 @@ public:
ColumnPtr databases_,
ColumnPtr tables_,
Storages storages_,
const std::shared_ptr<const AccessRightsContext> & access_rights_,
String query_id_)
: SourceWithProgress(header_)
, columns_mask(std::move(columns_mask_)), max_block_size(max_block_size_)
, databases(std::move(databases_)), tables(std::move(tables_)), storages(std::move(storages_))
, query_id(std::move(query_id_)), total_tables(tables->size())
, query_id(std::move(query_id_)), total_tables(tables->size()), access_rights(access_rights_)
{
}
@ -81,6 +83,8 @@ protected:
MutableColumns res_columns = getPort().getHeader().cloneEmptyColumns();
size_t rows_count = 0;
const bool check_access_for_tables = !access_rights->isGranted(AccessType::SHOW);
while (rows_count < max_block_size && db_table_num < total_tables)
{
const std::string database_name = (*databases)[db_table_num].get<std::string>();
@ -125,11 +129,16 @@ protected:
column_sizes = storage->getColumnSizes();
}
bool check_access_for_columns = check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, database_name, table_name);
for (const auto & column : columns)
{
if (column.is_virtual)
continue;
if (check_access_for_columns && !access_rights->isGranted(AccessType::SHOW, database_name, table_name, column.name))
continue;
size_t src_index = 0;
size_t res_index = 0;
@ -222,6 +231,7 @@ private:
String query_id;
size_t db_table_num = 0;
size_t total_tables;
std::shared_ptr<const AccessRightsContext> access_rights;
};
@ -266,8 +276,7 @@ Pipes StorageSystemColumns::readWithProcessors(
/// We are skipping "Lazy" database because we cannot afford initialization of all its tables.
/// This should be documented.
if (context.hasDatabaseAccessRights(database.first)
&& database.second->getEngineName() != "Lazy")
if (database.second->getEngineName() != "Lazy")
database_column_mut->insert(database.first);
}
@ -324,7 +333,7 @@ Pipes StorageSystemColumns::readWithProcessors(
pipes.emplace_back(std::make_shared<ColumnsSource>(
std::move(columns_mask), std::move(header), max_block_size,
std::move(filtered_database_column), std::move(filtered_table_column), std::move(storages),
context.getCurrentQueryId()));
context.getAccessRights(), context.getCurrentQueryId()));
return pipes;
}

View File

@ -1,6 +1,7 @@
#include <Databases/IDatabase.h>
#include <DataTypes/DataTypeString.h>
#include <Interpreters/Context.h>
#include <Access/AccessRightsContext.h>
#include <Storages/System/StorageSystemDatabases.h>
@ -19,17 +20,20 @@ NamesAndTypesList StorageSystemDatabases::getNamesAndTypes()
void StorageSystemDatabases::fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo &) const
{
const auto access_rights = context.getAccessRights();
const bool check_access_for_databases = !access_rights->isGranted(AccessType::SHOW);
auto databases = context.getDatabases();
for (const auto & database : databases)
{
if (context.hasDatabaseAccessRights(database.first))
{
res_columns[0]->insert(database.first);
res_columns[1]->insert(database.second->getEngineName());
res_columns[2]->insert(context.getPath() + database.second->getDataPath());
res_columns[3]->insert(database.second->getMetadataPath());
}
}
if (check_access_for_databases && !access_rights->isGranted(AccessType::SHOW, database.first))
continue;
res_columns[0]->insert(database.first);
res_columns[1]->insert(database.second->getEngineName());
res_columns[2]->insert(context.getPath() + database.second->getDataPath());
res_columns[3]->insert(database.second->getMetadataPath());
}
}
}

View File

@ -8,6 +8,7 @@
#include <Dictionaries/DictionaryStructure.h>
#include <Interpreters/Context.h>
#include <Interpreters/ExternalDictionariesLoader.h>
#include <Access/AccessRightsContext.h>
#include <Storages/System/StorageSystemDictionaries.h>
#include <Storages/VirtualColumnUtils.h>
#include <Columns/ColumnString.h>
@ -47,6 +48,9 @@ NamesAndTypesList StorageSystemDictionaries::getNamesAndTypes()
void StorageSystemDictionaries::fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo & /*query_info*/) const
{
const auto access_rights = context.getAccessRights();
const bool check_access_for_dictionaries = !access_rights->isGranted(AccessType::SHOW);
const auto & external_dictionaries = context.getExternalDictionariesLoader();
for (const auto & load_result : external_dictionaries.getCurrentLoadResults())
{
@ -68,6 +72,10 @@ void StorageSystemDictionaries::fillData(MutableColumns & res_columns, const Con
}
}
if (check_access_for_dictionaries
&& !access_rights->isGranted(AccessType::SHOW, database.empty() ? IDictionary::NO_DATABASE_TAG : database, short_name))
continue;
size_t i = 0;
res_columns[i++]->insert(database);
res_columns[i++]->insert(short_name);

View File

@ -1,6 +1,7 @@
#include <Interpreters/Context.h>
#include <Storages/MergeTree/MergeList.h>
#include <Storages/System/StorageSystemMerges.h>
#include <Access/AccessRightsContext.h>
namespace DB
@ -35,32 +36,35 @@ NamesAndTypesList StorageSystemMerges::getNamesAndTypes()
void StorageSystemMerges::fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo &) const
{
const auto access_rights = context.getAccessRights();
const bool check_access_for_tables = !access_rights->isGranted(AccessType::SHOW);
for (const auto & merge : context.getMergeList().get())
{
if (context.hasDatabaseAccessRights(merge.database))
{
size_t i = 0;
res_columns[i++]->insert(merge.database);
res_columns[i++]->insert(merge.table);
res_columns[i++]->insert(merge.elapsed);
res_columns[i++]->insert(merge.progress);
res_columns[i++]->insert(merge.num_parts);
res_columns[i++]->insert(merge.source_part_names);
res_columns[i++]->insert(merge.result_part_name);
res_columns[i++]->insert(merge.source_part_paths);
res_columns[i++]->insert(merge.result_part_path);
res_columns[i++]->insert(merge.partition_id);
res_columns[i++]->insert(merge.is_mutation);
res_columns[i++]->insert(merge.total_size_bytes_compressed);
res_columns[i++]->insert(merge.total_size_marks);
res_columns[i++]->insert(merge.bytes_read_uncompressed);
res_columns[i++]->insert(merge.rows_read);
res_columns[i++]->insert(merge.bytes_written_uncompressed);
res_columns[i++]->insert(merge.rows_written);
res_columns[i++]->insert(merge.columns_written);
res_columns[i++]->insert(merge.memory_usage);
res_columns[i++]->insert(merge.thread_number);
}
if (check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, merge.database, merge.table))
continue;
size_t i = 0;
res_columns[i++]->insert(merge.database);
res_columns[i++]->insert(merge.table);
res_columns[i++]->insert(merge.elapsed);
res_columns[i++]->insert(merge.progress);
res_columns[i++]->insert(merge.num_parts);
res_columns[i++]->insert(merge.source_part_names);
res_columns[i++]->insert(merge.result_part_name);
res_columns[i++]->insert(merge.source_part_paths);
res_columns[i++]->insert(merge.result_part_path);
res_columns[i++]->insert(merge.partition_id);
res_columns[i++]->insert(merge.is_mutation);
res_columns[i++]->insert(merge.total_size_bytes_compressed);
res_columns[i++]->insert(merge.total_size_marks);
res_columns[i++]->insert(merge.bytes_read_uncompressed);
res_columns[i++]->insert(merge.rows_read);
res_columns[i++]->insert(merge.bytes_written_uncompressed);
res_columns[i++]->insert(merge.rows_written);
res_columns[i++]->insert(merge.columns_written);
res_columns[i++]->insert(merge.memory_usage);
res_columns[i++]->insert(merge.thread_number);
}
}

View File

@ -7,6 +7,7 @@
#include <Storages/MergeTree/MergeTreeData.h>
#include <Storages/MergeTree/MergeTreeMutationStatus.h>
#include <Storages/VirtualColumnUtils.h>
#include <Access/AccessRightsContext.h>
#include <Databases/IDatabase.h>
@ -36,6 +37,9 @@ NamesAndTypesList StorageSystemMutations::getNamesAndTypes()
void StorageSystemMutations::fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo & query_info) const
{
const auto access_rights = context.getAccessRights();
const bool check_access_for_databases = !access_rights->isGranted(AccessType::SHOW);
/// Collect a set of *MergeTree tables.
std::map<String, std::map<String, StoragePtr>> merge_tree_tables;
for (const auto & db : context.getDatabases())
@ -43,10 +47,19 @@ void StorageSystemMutations::fillData(MutableColumns & res_columns, const Contex
/// Lazy database can not contain MergeTree tables
if (db.second->getEngineName() == "Lazy")
continue;
if (context.hasDatabaseAccessRights(db.first))
for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next())
if (dynamic_cast<const MergeTreeData *>(iterator->table().get()))
merge_tree_tables[db.first][iterator->name()] = iterator->table();
const bool check_access_for_tables = check_access_for_databases && !access_rights->isGranted(AccessType::SHOW, db.first);
for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next())
{
if (!dynamic_cast<const MergeTreeData *>(iterator->table().get()))
continue;
if (check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, db.first, iterator->name()))
continue;
merge_tree_tables[db.first][iterator->name()] = iterator->table();
}
}
MutableColumnPtr col_database_mut = ColumnString::create();

View File

@ -9,6 +9,7 @@
#include <DataStreams/OneBlockInputStream.h>
#include <Storages/MergeTree/MergeTreeData.h>
#include <Storages/VirtualColumnUtils.h>
#include <Access/AccessRightsContext.h>
#include <Databases/IDatabase.h>
#include <Parsers/queryToString.h>
#include <Parsers/ASTIdentifier.h>
@ -71,6 +72,9 @@ StoragesInfoStream::StoragesInfoStream(const SelectQueryInfo & query_info, const
MutableColumnPtr engine_column_mut = ColumnString::create();
MutableColumnPtr active_column_mut = ColumnUInt8::create();
const auto access_rights = context.getAccessRights();
const bool check_access_for_tables = !access_rights->isGranted(AccessType::SHOW);
{
Databases databases = context.getDatabases();
@ -80,7 +84,7 @@ StoragesInfoStream::StoragesInfoStream(const SelectQueryInfo & query_info, const
{
/// Lazy database can not contain MergeTree tables
/// and it's unnecessary to load all tables of Lazy database just to filter all of them.
if (context.hasDatabaseAccessRights(database.first) && database.second->getEngineName() != "Lazy")
if (database.second->getEngineName() != "Lazy")
database_column_mut->insert(database.first);
}
block_to_filter.insert(ColumnWithTypeAndName(
@ -114,6 +118,9 @@ StoragesInfoStream::StoragesInfoStream(const SelectQueryInfo & query_info, const
if (!dynamic_cast<MergeTreeData *>(storage.get()))
continue;
if (check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, database_name, table_name))
continue;
storages[std::make_pair(database_name, iterator->name())] = storage;
/// Add all combinations of flag 'active'.

View File

@ -6,6 +6,7 @@
#include <Storages/System/StorageSystemReplicas.h>
#include <Storages/StorageReplicatedMergeTree.h>
#include <Storages/VirtualColumnUtils.h>
#include <Access/AccessRightsContext.h>
#include <Common/typeid_cast.h>
#include <Databases/IDatabase.h>
#include <Processors/Sources/SourceFromSingleChunk.h>
@ -63,6 +64,9 @@ Pipes StorageSystemReplicas::readWithProcessors(
{
check(column_names);
const auto access_rights = context.getAccessRights();
const bool check_access_for_databases = !access_rights->isGranted(AccessType::SHOW);
/// We collect a set of replicated tables.
std::map<String, std::map<String, StoragePtr>> replicated_tables;
for (const auto & db : context.getDatabases())
@ -70,11 +74,14 @@ Pipes StorageSystemReplicas::readWithProcessors(
/// Lazy database can not contain replicated tables
if (db.second->getEngineName() == "Lazy")
continue;
if (context.hasDatabaseAccessRights(db.first))
const bool check_access_for_tables = check_access_for_databases && !access_rights->isGranted(AccessType::SHOW, db.first);
for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next())
{
for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next())
if (dynamic_cast<const StorageReplicatedMergeTree *>(iterator->table().get()))
replicated_tables[db.first][iterator->name()] = iterator->table();
if (!dynamic_cast<const StorageReplicatedMergeTree *>(iterator->table().get()))
continue;
if (check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, db.first, iterator->name()))
continue;
replicated_tables[db.first][iterator->name()] = iterator->table();
}
}

View File

@ -8,6 +8,7 @@
#include <Storages/System/StorageSystemReplicationQueue.h>
#include <Storages/StorageReplicatedMergeTree.h>
#include <Storages/VirtualColumnUtils.h>
#include <Access/AccessRightsContext.h>
#include <Common/typeid_cast.h>
#include <Databases/IDatabase.h>
@ -48,6 +49,9 @@ NamesAndTypesList StorageSystemReplicationQueue::getNamesAndTypes()
void StorageSystemReplicationQueue::fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo & query_info) const
{
const auto access_rights = context.getAccessRights();
const bool check_access_for_databases = !access_rights->isGranted(AccessType::SHOW);
std::map<String, std::map<String, StoragePtr>> replicated_tables;
for (const auto & db : context.getDatabases())
{
@ -55,11 +59,15 @@ void StorageSystemReplicationQueue::fillData(MutableColumns & res_columns, const
if (db.second->getEngineName() == "Lazy")
continue;
if (context.hasDatabaseAccessRights(db.first))
const bool check_access_for_tables = check_access_for_databases && !access_rights->isGranted(AccessType::SHOW, db.first);
for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next())
{
for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next())
if (dynamic_cast<const StorageReplicatedMergeTree *>(iterator->table().get()))
replicated_tables[db.first][iterator->name()] = iterator->table();
if (!dynamic_cast<const StorageReplicatedMergeTree *>(iterator->table().get()))
continue;
if (check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, db.first, iterator->name()))
continue;
replicated_tables[db.first][iterator->name()] = iterator->table();
}
}

View File

@ -6,6 +6,7 @@
#include <Storages/System/StorageSystemTables.h>
#include <Storages/VirtualColumnUtils.h>
#include <Databases/IDatabase.h>
#include <Access/AccessRightsContext.h>
#include <Interpreters/Context.h>
#include <Parsers/ASTCreateQuery.h>
#include <Parsers/queryToString.h>
@ -105,6 +106,9 @@ protected:
MutableColumns res_columns = getPort().getHeader().cloneEmptyColumns();
const auto access_rights = context.getAccessRights();
const bool check_access_for_databases = !access_rights->isGranted(AccessType::SHOW);
size_t rows_count = 0;
while (rows_count < max_block_size)
{
@ -116,7 +120,7 @@ protected:
database_name = databases->getDataAt(database_idx).toString();
database = context.tryGetDatabase(database_name);
if (!database || !context.hasDatabaseAccessRights(database_name))
if (!database)
{
/// Database was deleted just now or the user has no access.
++database_idx;
@ -193,6 +197,8 @@ protected:
return Chunk(std::move(res_columns), num_rows);
}
const bool check_access_for_tables = check_access_for_databases && !access_rights->isGranted(AccessType::SHOW, database_name);
if (!tables_it || !tables_it->isValid())
tables_it = database->getTablesWithDictionaryTablesIterator(context);
@ -201,8 +207,10 @@ protected:
for (; rows_count < max_block_size && tables_it->isValid(); tables_it->next())
{
auto table_name = tables_it->name();
StoragePtr table = nullptr;
if (check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, database_name, table_name))
continue;
StoragePtr table = nullptr;
TableStructureReadLockHolder lock;
try

View File

@ -62,6 +62,8 @@ StoragePtr ITableFunctionFileLike::executeImpl(const ASTPtr & ast_function, cons
if (args.size() == 4)
compression_method = args[3]->as<ASTLiteral &>().value.safeGet<String>();
context.checkAccess(getRequiredAccessType());
/// Create table
StoragePtr storage = getStorage(filename, format, columns, const_cast<Context &>(context), table_name, compression_method);

View File

@ -17,5 +17,6 @@ private:
StoragePtr executeImpl(const ASTPtr & ast_function, const Context & context, const std::string & table_name) const override;
virtual StoragePtr getStorage(
const String & source, const String & format, const ColumnsDescription & columns, Context & global_context, const std::string & table_name, const String & compression_method) const = 0;
virtual AccessType getRequiredAccessType() const = 0;
};
}

View File

@ -60,6 +60,8 @@ StoragePtr ITableFunctionXDBC::executeImpl(const ASTPtr & ast_function, const Co
remote_table_name = args[1]->as<ASTLiteral &>().value.safeGet<String>();
}
context.checkAccess(getRequiredAccessType());
/* Infer external table structure */
/// Have to const_cast, because bridges store their commands inside context
BridgeHelperPtr helper = createBridgeHelper(const_cast<Context &>(context), context.getSettingsRef().http_receive_timeout.value, connection_string);

View File

@ -21,6 +21,8 @@ private:
virtual BridgeHelperPtr createBridgeHelper(Context & context,
const Poco::Timespan & http_timeout_,
const std::string & connection_string_) const = 0;
virtual AccessType getRequiredAccessType() const = 0;
};
class TableFunctionJDBC : public ITableFunctionXDBC
@ -39,6 +41,8 @@ private:
{
return std::make_shared<XDBCBridgeHelper<JDBCBridgeMixin>>(context, http_timeout_, connection_string_);
}
AccessType getRequiredAccessType() const override { return AccessType::jdbc; }
};
class TableFunctionODBC : public ITableFunctionXDBC
@ -57,5 +61,7 @@ private:
{
return std::make_shared<XDBCBridgeHelper<ODBCBridgeMixin>>(context, http_timeout_, connection_string_);
}
AccessType getRequiredAccessType() const override { return AccessType::odbc; }
};
}

View File

@ -32,9 +32,6 @@ TableFunctionPtr TableFunctionFactory::get(
const std::string & name,
const Context & context) const
{
if (context.getSettings().readonly == 1) /** For example, for readonly = 2 - allowed. */
throw Exception("Table functions are forbidden in readonly mode", ErrorCodes::READONLY);
auto res = tryGet(name, context);
if (!res)
{

View File

@ -14,6 +14,11 @@ StoragePtr TableFunctionFile::getStorage(
return StorageFile::create(source, global_context.getUserFilesPath(), args);
}
AccessType TableFunctionFile::getRequiredAccessType() const
{
return AccessType::file;
}
void registerTableFunctionFile(TableFunctionFactory & factory)
{
factory.registerFunction<TableFunctionFile>();

View File

@ -24,5 +24,6 @@ public:
private:
StoragePtr getStorage(
const String & source, const String & format, const ColumnsDescription & columns, Context & global_context, const std::string & table_name, const std::string & compression_method) const override;
AccessType getRequiredAccessType() const override;
};
}

View File

@ -21,6 +21,11 @@ StoragePtr TableFunctionHDFS::getStorage(
compression_method);
}
AccessType TableFunctionHDFS::getRequiredAccessType() const
{
return AccessType::hdfs;
}
#if USE_HDFS
void registerTableFunctionHDFS(TableFunctionFactory & factory)
{

View File

@ -25,6 +25,7 @@ public:
private:
StoragePtr getStorage(
const String & source, const String & format, const ColumnsDescription & columns, Context & global_context, const std::string & table_name, const String & compression_method) const override;
AccessType getRequiredAccessType() const override;
};
}

View File

@ -35,6 +35,8 @@ StoragePtr TableFunctionInput::executeImpl(const ASTPtr & ast_function, const Co
throw Exception("Table function '" + getName() + "' requires exactly 1 argument: structure",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
context.checkAccess(AccessType::input);
String structure = evaluateConstantExpressionOrIdentifierAsLiteral(args[0], context)->as<ASTLiteral &>().value.safeGet<String>();
auto columns = parseColumnsListFromString(structure, context);
StoragePtr storage = StorageInput::create(table_name, columns);

View File

@ -67,6 +67,8 @@ StoragePtr TableFunctionMerge::executeImpl(const ASTPtr & ast_function, const Co
String source_database = args[0]->as<ASTLiteral &>().value.safeGet<String>();
String table_name_regexp = args[1]->as<ASTLiteral &>().value.safeGet<String>();
context.checkAccess(AccessType::merge, source_database);
auto res = StorageMerge::create(
StorageID(getDatabaseName(), table_name),
ColumnsDescription{chooseColumns(source_database, table_name_regexp, context)},

View File

@ -55,6 +55,8 @@ StoragePtr TableFunctionMySQL::executeImpl(const ASTPtr & ast_function, const Co
std::string user_name = args[3]->as<ASTLiteral &>().value.safeGet<String>();
std::string password = args[4]->as<ASTLiteral &>().value.safeGet<String>();
context.checkAccess(AccessType::mysql);
bool replace_query = false;
std::string on_duplicate_clause;
if (args.size() >= 6)

View File

@ -31,6 +31,8 @@ StoragePtr TableFunctionNumbers<multithreaded>::executeImpl(const ASTPtr & ast_f
UInt64 offset = arguments.size() == 2 ? evaluateArgument(context, arguments[0]) : 0;
UInt64 length = arguments.size() == 2 ? evaluateArgument(context, arguments[1]) : evaluateArgument(context, arguments[0]);
context.checkAccess(AccessType::numbers);
auto res = StorageSystemNumbers::create(table_name, multithreaded, length, offset, false);
res->startup();
return res;

View File

@ -132,6 +132,8 @@ StoragePtr TableFunctionRemote::executeImpl(const ASTPtr & ast_function, const C
if (arg_num < args.size())
throw Exception(help_message, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
context.checkAccess(AccessType::remote);
/// ExpressionAnalyzer will be created in InterpreterSelectQuery that will meet these `Identifier` when processing the request.
/// We need to mark them as the name of the database or table, because the default value is column.
for (auto ast : args)

View File

@ -61,6 +61,8 @@ StoragePtr TableFunctionS3::executeImpl(const ASTPtr & ast_function, const Conte
else
compression_method = "auto";
context.checkAccess(AccessType::s3);
ColumnsDescription columns = parseColumnsListFromString(structure, context);
/// Create table

View File

@ -15,6 +15,11 @@ StoragePtr TableFunctionURL::getStorage(
return StorageURL::create(uri, StorageID(getDatabaseName(), table_name), format, columns, ConstraintsDescription{}, global_context, compression_method);
}
AccessType TableFunctionURL::getRequiredAccessType() const
{
return AccessType::url;
}
void registerTableFunctionURL(TableFunctionFactory & factory)
{
factory.registerFunction<TableFunctionURL>();

View File

@ -20,5 +20,6 @@ public:
private:
StoragePtr getStorage(
const String & source, const String & format, const ColumnsDescription & columns, Context & global_context, const std::string & table_name, const String & compression_method) const override;
AccessType getRequiredAccessType() const override;
};
}

View File

@ -72,6 +72,8 @@ StoragePtr TableFunctionValues::executeImpl(const ASTPtr & ast_function, const C
throw Exception("Table function '" + getName() + "' requires 2 or more arguments: structure and values.",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
context.checkAccess(AccessType::values);
/// Parsing first argument as table structure and creating a sample block
std::string structure = args[0]->as<ASTLiteral &>().value.safeGet<String>();